With this guide, you'll build a fully functional iMessage clone for iOS with SwiftUI, Stream Chat, and Stream Video by handling the app's creation with your favorite coding agent using the Stream Agent Skills.
Users can interact with the app and use its features as demonstrated in the preview below.
Before You Start
Let's ensure you have everything in place first:
- Xcode 26 or later: Download Xcode 26 or later. You can preview the app on an iOS simulator or on an iPhone.
- A free Stream account (sign up) with an app created in your dashboard.
- Stream CLI Skills
- Stream Swift Skills
The Completed iMessage Clone Project
Using the Stream's CLI and Swift skills, your coding agent can generate the required Swift code, set up Stream's backend, and configure the iOS chat/video SDK to work with your SwiftUI app. The source code of this tutorial's demo can be explored from this GitHub repo.
The entire project is made up of these Swift files, linked throughout this tutorial:
| File | Role |
|---|---|
| IMessageChatService.swift | Owns the Stream Chat client, app theming, and user connection |
| IMessageCallService.swift | Stream Video client, call coordinator, and Liquid Glass call UIs |
| IMessageScreens.swift | Inbox, message, and profile screens. It also consists of Calls/Contacts/Search tabs |
| iMessageCloneStreamSkillsApp.swift | App root consisting of tab bar composition, client initialization, and call overlay |
The complete project has:
- An iMessage-style inbox powered by Stream Channel List, message avatars with initials, message previews, unread indicators, date and timeline, and more.
- The iMessage's familiar Edit and Filter menus in the trailing toolbar.
- Messages List with a floating Liquid Glass composer and an iMessage-style contact header.
- A contact profile with audio/video buttons for initiating calls.
- A Liquid Glass tab bar consisting of Messages, Calls, Contacts, and Search items.
- FaceTime-style call screen for video calls.
What Are Stream Agent Skills?
The Stream skills equip coding agents to build, query, and manage Stream Chat, Audio/Video, Feeds, and Moderation across React/Next.js, React Native, iOS/UIKit/SwiftUI, Android, and Flutter apps.
Read the full documentation or the quickstart guide.
The project in this article uses two Stream Skills, each with a distinct role. You can install them by running these commands.
123npx skills add https://github.com/getstream/agent-skills --skill stream-cli npx skills add https://github.com/getstream/agent-skills --skill stream-swift
The stream-swift clarifies users' requests, orchestrates, and points an agent to Stream docs, fetches the required/up-to-date information, and applies it inside your project. Once requested, it builds the app using a set of rules. The stream-cli skill teaches coding agents to drive Stream's server-side APIs from the terminal. It manages user credentials and seed users, channels, and messages.
How the Skills Scaffold iOS/SwiftUI Apps
With a provided starter Xcode project, the agent (Fable 5) used the skills and followed these five decision steps to create the iMessage clone with calling support.
- File Isolation for Combined Chat and Video: One service file per SDK IMessageChatService.swift (chat only), IMessageCallService.swift (video only).
- CLI-Minted Credentials: Per the skill rules, the agent ran the
GetStreamtoken to mint a user JWT signed server-side. - Seeded Demo Data: The agent used the
GetStreamAPI to upsert six contacts and created six one-to-one channels. - View Customization: The agent used a Liquid Glass style to enhance the channel/messages header and floating message composer as specified in the prompt.
- Client Lifetime: Both Chat and Video SDK clients are singletons initialized once in the root view's
init(), and the app-wideCallViewModel.
Start With an Xcode Project
To use the CLI and Swift skills, start with a SwiftUI project in Xcode. Once you prompt the agent to build the app with the skills, it installs the required Stream SDKs via Swift Package Manager. In this case, that's Chat and Video.
Additionally, the agent will configure camera, microphone, and photo usage descriptions in the project's Info settings, since the app supports video calls and media uploads in chat.
- Photo Library Usage Description: Media upload in chat.
- Camera Usage Description: Video calls use the camera.
- Microphone Usage Description: Audio calls use the microphone.
Backend Seed With the Stream CLI
Instead of writing a seeding script, stream-cli drives the server-side chat API directly from the terminal, creating six contacts for the app that appear in the inbox.
12345678910getstream api UpdateUsers --request '{ "users": { "aiti3": {"id": "aiti3", "name": "Aiti3"}, "kofi-seth": {"id": "kofi-seth", "name": "Bra Kofi Sapong Seth"}, "amnesty": {"id": "amnesty", "name": "Amnesty"}, "caruna": {"id": "caruna", "name": "Caruna"}, "ana-sofia": {"id": "ana-sofia", "name": "Ana Sofia"}, "tuntematon": {"id": "tuntematon", "name": "Tuntematon"} } }'
Agent-Generated Chat Service
The generated ChatClient implementation is in IMessageChatService.swift. The file contains a @MainActor singleton whose setUpIfNeeded() method builds the ChatClient, applies iMessage theming, and connects the user using the CLI-minted token.
Details of IMessageChatService.swift:
- The client is created once and held by a singleton, not in a view body or a computed property.
- The app's appearance is set at
StreamChatinit time. There are three theming tokens (accentPrimary,navigationBarTintColor,chatBackgroundOutgoing). - There is also an
isLocalStorageEnabledproperty that provides offline support for the app.
Generated Video Service, Call Coordinator, and Liquid Glass UI
The video service, call coordinator, and Liquid Glass-enhanced call UIs were generated and implemented by the agent in IMessageCallService.swift. The file mimics the Stream Video pattern with a customized look and feel (based on the prompt). The same API key, user ID, and CLI-minted JWT are used for both the chat and video Stream products.
The file contains four main parts:
- Client Singleton: The
IMessageCallService.setUpIfNeeded()method initializes theStreamVideoclient and aStreamVideoUIinstance (which must remain alive for the SDK's injected appearance to work). - Call Coordinator: These are the chat screens needed for audio/video call initiation.
- Root Wrapper: It contains
IMessageCallRootclass, which wraps the app's content in the SDK'sCallModifier, that overlays call states (outgoing, incoming, in-call) on whatever screen the user is on. - Liquid Glass Call UI: To enhance the app's UI with Liquid Glass effects,
IMessageCallViewFactoryis used to override five of theViewFactoryslots in the Stream Video SDK, all implemented in the same file:makeWaitingLocalUserView->IMessageWaitingLocalUserView: A fullscreen local camera feed (LocalVideoView) with top bar and controls floating above it.makeCallTopView->IMessageCallTopView: A glass top bar with a minimize button, a live indicator, call duration, and participants' count.makeCallControlsView->IMessageCallControlsView: A row of Liquid Glass buttons (mic, camera, speaker, flip camera, and hang up).makeOutgoingCallView->IMessageOutgoingCallView: An audio-call ringing screen.makeIncomingCallView->IMessageIncomingCallView: A FaceTime-style incoming screen.
The iMessage-Like Inbox
IMessageScreens.swift contains all the app screens. The inbox is a custom SwiftUI list over a live ChatChannelListController:
IMessageInboxViewModel: It enforces the Stream skill rules to bridge the controller's delegate into@Publishedstate.IMessageContact: It derives a contact from its one-to-one channel.IMessageChannelRow: It recreates the iMessage anatomy (unread indicator, message avatar, name, and date).IMessageMessagesTab: Its role is to wrap the list in aNavigationStackand add the toolbar menus (Edit and Unread filter).
The Contact Profile and Root View
The IMessageContactProfileView in IMessageScreens.swift mimics the iMessage contact card, consisting of a large glass message avatar, the contact's name, and a GlassEffectContainer row with phone and video buttons for initiating calls. Tapping the camera button starts and joins the call. When a user taps the phone button instead, the recipient's device will ring. When the call is answered, the local and remote devices join a real-time WebRTC session.
The root view composes the entire app. It wraps a four-tab TabView in IMessageCallRoot. With all these parts in place, when you run the app, you will see an output similar to this image.
Discover More
The Stream Skills help developers to build apps and services faster with our four products (chat, audio/video, activity feed, and moderation). With the CLI and Swift Skills, you have seen how quickly you can clone a popular community-based social, calling, or messaging app like iMessage, with seamlessly integrated chat and calling.
Visit our website to interact with the Stream Skills in your browser and watch it build an app.
And, check out the complete documentation and how to get started with the skills on the following available platforms.

