Real-Time Chat App with Combine and WebSockets
Build an iOS chat app that sends and receives messages in real time over a WebSocket, with the incoming messages and the connection state managed as Combine streams. The app shows messages as they arrive, typing indicators ("Ana is typing…") and a delivery status for every message you send (sending, sent, failed), reconnects on its own with increasing waits when the connection drops, and resends messages that failed. You'll also build a small WebSocket server to chat with. You'll learn URLSessionWebSocketTask, Combine publishers, subjects and operators, connecting Combine to SwiftUI, reconnection with backoff, message acknowledgements, and testing networking code with a fake transport.
- Estimate
- ~13h
- Steps
- 6
- Completed by
- 0
- Proposed by
- codeseed.app
SwiftUI · Combine · URLSessionWebSocketTask
Project roadmap
- 01
Build a server and connect
~2hWrite a small WebSocket chat server (for example in Node.js with the ws package, or Swift with Vapor) that relays JSON messages to everyone and acknowledges each message by its client id. Define the message types in PROTOCOL.md: chat (id, sender, text, time), ack (id), typing (sender). In the app, open a URLSessionWebSocketTask and read messages in a loop with receive(). Done when: the app connects and prints messages from the server.
- 02
Expose Combine publishers
~2hCreate a ChatConnection class that publishes incoming messages through a PassthroughSubject and the connection state (connecting, connected, disconnected) through a CurrentValueSubject, exposed as AnyPublisher. Decode JSON into Codable types, and report decoding errors without closing the connection. Done when: subscribers receive messages and state changes.
- 03
Build the chat screen
~2.5hBuild a ChatViewModel (ObservableObject) that subscribes to the publishers with sink and receive(on: DispatchQueue.main), storing the subscriptions in a set of AnyCancellable. Show messages in a scrolling list with bubbles for you and others, an input bar, and the connection state at the top. Scroll to the newest message automatically. Done when: two simulators can chat through the server.
- 04
Reconnect automatically
~2hWhen the connection fails or closes, reconnect after 1, 2, 4 and up to 30 seconds plus jitter, resetting after a successful connection. Send pings every 20 seconds to detect dead connections. Pause while the app is in the background and reconnect when it returns. Done when: restarting the server makes the app reconnect by itself, with the state shown on screen.
- 05
Add typing indicators and delivery status
~2.5hSend a typing event when the user types, at most once every 3 seconds (use Combine's throttle), and show Name is typing… for 5 seconds after receiving one. Give every sent message a status: sending until the ack arrives, sent after it, and failed after a timeout or disconnect, with a tap to retry. Resend pending messages after reconnecting, using the same id so the server ignores duplicates. Done when: statuses update correctly, even across a disconnect.
- 06
Test with a fake transport
~2hPut the WebSocket behind a protocol so tests can use a fake that delivers messages, fails and closes on demand. Test the reconnection timing (with an injectable scheduler, such as a test scheduler from the Combine Schedulers library), delivery statuses and retries, the typing throttle, and decoding errors. Done when: the unit tests pass without a real server.
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack