Native iOS client for the Mezon messaging platform.
High-performance, real-time communication built with AsyncDisplayKit and SwiftSignalKit.
Mezon iOS is the native Apple platform client for Mezon — a Live, Work, and Play platform designed for gaming communities, professional teams, and content creators. The app delivers sub-millisecond UI responsiveness through async rendering and a fully reactive data pipeline.
| Layer | Technology |
|---|---|
| UI Rendering | AsyncDisplayKit (Texture) 3.2 — async layout & render off main thread |
| Declarative UI | ComponentFlow — SwiftUI-like component system for forms & profiles |
| Reactive | SwiftSignalKit — custom Signal, ValuePipe, Disposable |
| Database | SQLCipher 4.0 — encrypted SQLite via Postbox layer |
| Networking | HTTP REST (Protobuf) + WebSocket (real-time) |
| Serialization | swift-protobuf 1.35 |
| Build | Xcode 16+ · CocoaPods · Swift Package Manager |
| Min Target | iOS 13.0 |
┌─────────────────────────────────────────────────────┐
│ AppDelegate │
│ Window1 → isLoggedInSignal │
│ ├─ authorized → MezonRootController (TabBar) │
│ └─ unauthorized → LoginViewController │
├─────────────────────────────────────────────────────┤
│ SharedAccountContext (app-wide) │
│ mainWindow · theme · controller factories │
├─────────────────────────────────────────────────────┤
│ AccountContext (per-account) │
│ .account → Account (Postbox + HTTP + Socket) │
│ .engine → MezonEngine │
│ session · currentUser · login/logout │
├─────────────────────────────────────────────────────┤
│ MezonEngine │
│ .auth · .clans · .channels · .messages · .peers │
│ .data → EngineData (typed subscribe/get) │
├─────────────────────────────────────────────────────┤
│ Account │
│ .postbox → Postbox (SQLCipher + ViewTracker) │
│ .network → MezonHTTPClient (REST + Protobuf) │
│ .socket → MezonSocket (WebSocket) │
└─────────────────────────────────────────────────────┘
Network (HTTP / WebSocket)
│
▼
Postbox.write { tx in tx.addMessages(...) }
│
▼ ViewTracker.replay() → incremental updates
│
engine.data.subscribe(Item.MessageHistory(channelId:))
│
▼ Signal<[MessageRecord], NoError>
│
Controller → stateSignal() → ContainerNode / ComponentHostView
│
▼
ASDisplayNode (async render off main thread)
All controllers subscribe directly to reactive Postbox signals. No ViewModel layer — state lives in the controller, UI subscribes via Signal.
MezonChat/
│
├── Application/ App lifecycle
│ ├── AppDelegate.swift Window, auth flow, scene handling
│ └── SplashViewController.swift Launch screen
│
├── Core/
│ ├── Account/ Infrastructure layer
│ │ ├── Account.swift Postbox + HTTP + Socket
│ │ ├── User.swift User model
│ │ └── Message.swift Message model
│ │
│ ├── AccountContext/ Dependency injection
│ │ ├── AccountContext.swift Protocol
│ │ ├── AccountContextImpl.swift Implementation
│ │ └── SharedAccountContext.swift App-wide context + factories
│ │
│ ├── MezonEngine/ Domain API layer
│ │ ├── MezonEngine.swift Engine + lazy sub-engine registry
│ │ ├── MezonEngine+Auth.swift Login, OTP, refresh, logout
│ │ ├── MezonEngine+Clans.swift Clan listing + Postbox views
│ │ ├── MezonEngine+Channels.swift Channels, DMs
│ │ ├── MezonEngine+Messages.swift Send, list, history views
│ │ ├── MezonEngine+Peers.swift Profiles
│ │ └── Data/ Typed reactive Postbox access
│ │ ├── MezonEngineData.swift subscribe() / get() API
│ │ ├── ClansData.swift
│ │ ├── ChannelsData.swift
│ │ ├── MessagesData.swift
│ │ └── PreferencesData.swift
│ │
│ ├── Postbox/ Local persistence (SQLCipher)
│ │ ├── Core/ Postbox, Transaction, ViewTracker
│ │ ├── Database/ SqliteDatabase, Table
│ │ ├── Auth/ AuthRecord, AuthTable
│ │ ├── Channel/ ChannelRecord, ChannelTable
│ │ ├── Clans/ ClanRecord, ClanTable
│ │ ├── Messages/ MessageRecord, MessageTable
│ │ ├── Profile/ ProfileRecord, ProfileTable
│ │ ├── Settings/ SettingsTable
│ │ ├── Preferences/ PreferencesTable
│ │ └── Coding/ PostboxCoding protocol
│ │
│ ├── Display/ UI framework (145 files)
│ │ ├── ViewController.swift ASDisplayNode-backed base controller
│ │ ├── NavigationBar.swift Custom navigation bar
│ │ ├── ListView.swift High-performance scrolling list
│ │ ├── TabBarControllerImpl.swift Tab bar controller
│ │ └── Navigation/ NavigationController, containers
│ │
│ ├── ComponentFlow/ Declarative UI (34 files)
│ │ ├── Base/ Component, CombinedComponent, Environment
│ │ ├── Components/ Text, Image, Button, VStack, HStack,
│ │ │ List, ScrollComponent, CardComponent
│ │ ├── Gestures/ Tap, Pan, LongPress
│ │ ├── Host/ ComponentHostView
│ │ └── Utils/ ActionSlot, Color, Insets, Size
│ │
│ ├── SSignalKit/ Reactive framework
│ │ ├── SwiftSignalKit/ Signal, Subscriber, ValuePipe, Queue
│ │ └── SSignalKit/ Obj-C counterpart
│ │
│ ├── Extensions/ UIColor+Theme, UIView+Layout
│ ├── Localization/ L10n, LanguageManager
│ ├── Theme/ ThemeManager, AppTheme
│ ├── Security/ KeychainHelper
│ └── Utils/ Constants, ScreenScale, AppLogger
│
├── Features/
│ ├── Auth/Login/ Login form (ComponentFlow)
│ ├── Auth/VerifyOTP/ OTP verification
│ ├── Clans/ Clan sidebar + channel list + chat
│ ├── Main/ HomeViewController, MezonRootController
│ ├── Messages/ Direct messages
│ ├── Profile/ User profile (ComponentFlow)
│ ├── SendMessageInput/ Message input bar
│ ├── Settings/ Theme & language settings
│ └── TabBar/ Placeholder tabs
│
├── Networking/
│ ├── MezonHTTPClient.swift REST client (JSON + Protobuf)
│ ├── MezonSocket.swift WebSocket real-time messaging
│ ├── MezonSession.swift Session model
│ ├── MezonEnvironment.swift Dev / Prod config
│ └── SessionRefreshManager.swift Token refresh with retry
│
├── Generated/ Protobuf generated code
│ ├── api/api.pb.swift
│ └── rtapi/realtime.pb.swift
│
└── Resources/
├── Info.plist
├── Assets.xcassets/
└── Images.xcassets/
Every controller receives AccountContext via init. No singletons in feature code.
let vc = ClanListViewController(context: context)
// Access through DI chain:
context.engine.clans.listClanDescs(token:)
context.account.postbox.write { tx in ... }
context.engine.data.subscribe(Item.ClanList())// Continuous updates from local database
context.engine.data.subscribe(
MezonEngine.EngineData.Item.ClanList()
)
|> deliverOnMainQueue
|> start(next: { clans in
// UI updates automatically when data changes
})
// One-shot read
context.engine.data.get(
MezonEngine.EngineData.Item.MessageHistory(channelId: "123")
)Controllers own state and expose a reactive signal. Display nodes subscribe and re-render.
// Controller holds state, exposes signal
final class ClanListViewController: ViewController {
private let needsReloadPipe = ValuePipe<Void>()
func stateSignal() -> Signal<ClanListState, NoError> { ... }
}
// Node subscribes to signal
init(signal: Signal<ClanListState, NoError>, interaction: ClanListInteraction) {
disposables.add(
(signal |> deliverOnMainQueue).start(next: { state in
self.state = state
self.collectionView.reloadData()
})
)
}Declarative component system for static and semi-static screens.
final class ProfileContentComponent: CombinedComponent {
static var body: Body {
let nameText = Child(Text.self)
let card = Child(CardComponent.self)
return { context in
let name = nameText.update(
component: Text(text: context.component.displayName, font: ..., color: ...),
availableSize: CGSize(width: contentWidth, height: 100),
transition: context.transition
)
context.add(name.position(CGPoint(x: ..., y: ...)))
return CGSize(width: context.availableSize.width, height: totalHeight)
}
}
}| Optimization | Detail |
|---|---|
| Async Rendering | All UI layout and rendering happens off the main thread via AsyncDisplayKit |
| Incremental Updates | Postbox ViewTracker replays only changed records to active views |
| Encrypted Persistence | SQLCipher with WAL mode for concurrent reads and fast writes |
| Lazy Initialization | MezonEngine sub-engines created on first access, not at startup |
| Component Diffing | ComponentFlow skips re-render when props are equal |
| Signal Deduplication | distinctUntilChanged prevents redundant UI updates |
| Pixel-Perfect Layout | ScreenScale rounds all dimensions to device pixels |
| Image Caching | Text component renders to CGImage and caches size measurements |
- Xcode 16.0+
- CocoaPods (
gem install cocoapods) - iOS 13.0+ simulator or device
git clone https://github.com/mezonai/mezon-ios.git
cd mezon-ios
pod install
open MezonChat.xcworkspace
# Select simulator → Cmd+RThe app connects to Mezon production by default. To switch to development:
// In AppDelegate.swift
MezonEnvironment.current = .dev // or .prod| Package | Version | Purpose |
|---|---|---|
| Texture | ~> 3.2 | Async UI layout and rendering |
| SQLCipher | ~> 4.0 | Encrypted SQLite database |
| swift-protobuf | 1.35 | Protobuf serialization |
We welcome contributions! See the main Mezon repository for contribution guidelines.
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Make changes and ensure the project builds
- Submit a pull request
This project is licensed under the MIT License. See LICENSE for details.
Made with care by the Mezon team
Website · GitHub · App Store
