Build multi-modal AI applications using our new open-source Vision AI SDK.

Build an iMessage Clone With Stream's Agent Skills

New
8 min read
Amos G.
Amos G.
Published August 20, 2026
Build an iMessage Clone With Stream's Agent Skills

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:

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 completed iMessage clone: inbox, conversation, and call screens

The entire project is made up of these Swift files, linked throughout this tutorial:

FileRole
IMessageChatService.swiftOwns the Stream Chat client, app theming, and user connection
IMessageCallService.swiftStream Video client, call coordinator, and Liquid Glass call UIs
IMessageScreens.swiftInbox, message, and profile screens. It also consists of Calls/Contacts/Search tabs
iMessageCloneStreamSkillsApp.swiftApp 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.

bash
1
2
3
npx 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 GetStream token to mint a user JWT signed server-side.
  • Seeded Demo Data: The agent used the GetStream API 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-wide CallViewModel.

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.
Building your own app? Get access to our Livestream or Video Calling API and launch in days!

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.

json
1
2
3
4
5
6
7
8
9
10
getstream 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 StreamChat init time. There are three theming tokens (accentPrimary, navigationBarTintColor, chatBackgroundOutgoing).
  • There is also an isLocalStorageEnabled property 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 the StreamVideo client and a StreamVideoUI instance (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 IMessageCallRoot class, which wraps the app's content in the SDK's CallModifier, 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, IMessageCallViewFactory is used to override five of the ViewFactory slots 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 @Published state.
  • 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 a NavigationStack and add the toolbar menus (Edit and Unread filter).

The Contact Profile and Root View

The contact profile screen alongside the inbox, new message, calls list, and an active call

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.

The finished app: inbox, a conversation with a shared photo, and call screens

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.

Scaling WebRTC Video to 100,000 Participants
View Stream's latest Video API benchmark and the architecture that powers performance at scale.