Note: When a client posts a project and a freelancer takes it on, risks arise: non-completion, non-payment, disputes. To minimize these, a complex architecture is needed: two user types, escrow payments, offline-capable chat, and a rating system. We build such marketplaces using modern mobile technologies: Swift 5.9, Kotlin, Flutter 3.x, and Stripe Connect integration for secure transactions. Over 6 years on the market, we have delivered more than 20 mobile freelance exchanges, including projects with millions of users. The average exchange processes up to 10,000 orders monthly; with an average order value of $150 and a platform commission of 10–15%, that's $15–22.5 per order. 85% of orders complete successfully, and the average freelancer rating is 4.7. A basic MVP costs approximately $15,000–$25,000 depending on complexity. Contact us for a consultation on your project.
How to Build a Mobile App for a Freelance Exchange
The choice between separate accounts and a single account with role switching defines the app architecture. Separate accounts (like Upwork) are simpler for rights management, but a user cannot be both. A single account (like Kwork) requires a context manager but is more convenient. Technically: userRole: 'client' | 'freelancer' in session, different navigation trees.
// iOS: navigation by role via enum
enum UserContext {
case client
case freelancer
}
class AppCoordinator {
var currentContext: UserContext = .client {
didSet { updateTabBar() }
}
private func updateTabBar() {
switch currentContext {
case .client:
tabBarController.viewControllers = [
ProjectsListVC(), MyProjectsVC(), MessagesVC(), ProfileVC()
]
case .freelancer:
tabBarController.viewControllers = [
FindProjectsVC(), MyOrdersVC(), MessagesVC(), ProfileVC()
]
}
}
}
How to Implement Secure Payments?
The key feature of an exchange is funds held by the platform until task completion. The scheme:
- Client creates a project and deposits the amount into an escrow account (charge/capture)
- Freelancer completes the work and submits the result
- Client approves → platform transfers funds to freelancer minus commission
- If dispute → arbitration (manual or automated)
- If client does not respond for N days → automatic confirmation
Escrow payments are implemented via Stripe Connect automatic splitting. For example, to release payment:
# Stripe Connect: split payment with automatic platform commission
import stripe
def release_payment_to_freelancer(escrow_payment: EscrowPayment) -> None:
platform_fee = int(escrow_payment.amount * Decimal("0.10")) # 10% commission
# Create transfer to freelancer
transfer = stripe.Transfer.create(
amount=escrow_payment.amount - platform_fee,
currency="usd",
destination=escrow_payment.freelancer_stripe_account_id,
transfer_group=escrow_payment.project_id,
metadata={
"project_id": escrow_payment.project_id,
"order_id": escrow_payment.order_id
}
)
db.update_escrow(
escrow_payment.id,
status="RELEASED",
transfer_id=transfer.id
)
notify_freelancer(escrow_payment.freelancer_id, "payment_released", transfer.amount)
Partial payment (milestone payments) reduces risk by 40% compared to lump-sum payment.
How Does the Bidding System Work?
Freelancers place bids on projects: they offer a price and deadline. The client sees a list of proposals, portfolios, and ratings. Beginner freelancers get 5 free bids per month, after which bids are paid. This is a monetization model.
// Android: bidding list with sorting
data class Bid(
val id: String,
val freelancerId: String,
val amount: Decimal,
val deliveryDays: Int,
val coverLetter: String,
val freelancer: FreelancerProfile
)
@Composable
fun BidsList(
bids: List<Bid>,
sortBy: BidSortOption,
onHire: (Bid) -> Unit
) {
val sorted = when (sortBy) {
BidSortOption.PRICE -> bids.sortedBy { it.amount }
BidSortOption.RATING -> bids.sortedByDescending { it.freelancer.rating }
BidSortOption.DELIVERY -> bids.sortedBy { it.deliveryDays }
}
LazyColumn {
items(sorted, key = { it.id }) { bid ->
BidCard(bid = bid, onHire = { onHire(bid) })
}
}
}
How to Organize Chat with Offline Mode?
Chat is a mandatory component. Requirements: delivery, statuses, files, links to tasks. Technically: WebSocket + offline queue. Storage — Core Data (iOS) or Room (Android).
// iOS: offline message queue
class MessageQueue {
private let context: NSManagedObjectContext
private var syncTimer: Timer?
func sendMessage(_ content: String, conversationId: String) {
// Save locally immediately
let pending = PendingMessage(context: context)
pending.id = UUID().uuidString
pending.content = content
pending.conversationId = conversationId
pending.createdAt = Date()
pending.status = "PENDING"
try? context.save()
// Show in UI as sent
NotificationCenter.default.post(name: .messageSent, object: pending)
// Sync when connection is available
syncPendingMessages()
}
func syncPendingMessages() {
guard networkMonitor.isConnected else { return }
let pending = fetchPendingMessages()
pending.forEach { msg in
apiClient.sendMessage(msg) { result in
switch result {
case .success(let serverMessage):
msg.status = "DELIVERED"
msg.serverId = serverMessage.id
try? context.save()
case .failure:
// Retry on next sync
break
}
}
}
}
}
Comparison: Separate Accounts vs Single Role
| Criterion | Separate accounts | Single account with role switching |
|---|---|---|
| Rights management | Simpler, logic isolated | Requires context manager |
| Flexibility | User cannot be both client and freelancer | Easy to switch between roles |
| Development speed | Higher (separate modules) | Lower (role checks needed) |
| UX | Two apps in one | Single login, single interface |
Separate accounts are simpler to manage and develop, saving up to 20% of implementation time compared to a single account. However, a single account is more convenient for users who want to combine roles.
Ratings and Reputation
After order closure — mutual reviews. Rating affects search ranking. Protection from manipulation: review can only be left for a specific completed order; mutual anonymity until publication (like on Upwork); Bayesian average algorithm instead of simple average — a new profile without reviews won't show "5.0 out of 5". The average freelancer rating is 4.7, and 85% of orders complete successfully. Verified freelancers get 3x more job offers than unverified ones.
Verification and KYC
For fund withdrawal — mandatory identity verification. Exchanges often have two levels: basic (email + phone) and advanced (KYC via Sumsub or Veriff SDK). The advanced level lifts withdrawal limits, adds a "Verified" badge, and increases trust in the freelancer by 3 times.
What's Included in Development?
We provide a full package: project documentation (architecture, API specs, DB schemas), mobile app source code, backend for payments and chat, configured CI/CD pipelines, access to test and production accounts, operation instructions, and technical support for 2 weeks after launch. We train your team on how to use the system.
Timeline Estimates
| Scope | Time |
|---|---|
| Project catalog, profiles, bids, no payments | 5–7 weeks |
| Escrow payments, Stripe Connect for payouts | +3–4 weeks |
| Chat with WebSocket and offline queue | +2–3 weeks |
| KYC integration, review system | +2 weeks |
Cost is calculated individually after analyzing requirements and chosen stack. We will evaluate your project within 1–2 days — simply describe the task. Get a consultation: specify the main features and preferred stack, and we'll propose the optimal solution. Our mobile freelance exchange development includes escrow payments, chat, and ratings. Request a consultation, and we'll contact you within 24 hours.







