Design and Implementation of a Standardized IM Message Flow Interaction Layer for iOS
The paper presents a customizable, dependency‑injected iOS IM framework that separates message‑flow structure from interaction, defines reusable MessageView components with pluggable size‑strategies and a snapshot cache, and exposes standard service protocols, enabling low‑cost integration, business extensibility, and reduced maintenance across diverse chat scenarios.
The article introduces the background of building instant‑messaging (IM) features for a social product. Multiple social‑scene apps (private chat, group chat, chat rooms) share very similar message‑flow UI, yet each has specific interaction requirements. To reduce integration cost and unify technical solutions, a standardized IM message‑flow capability is designed for the iOS client.
Existing industry solutions such as cloud‑based IM services (e.g., Yunxin, LeanCloud) or community frameworks like MessageKit either sacrifice flexibility or require heavy modifications to meet the team’s needs. Therefore a custom solution is proposed.
Key considerations for a message‑flow interaction layer include:
Standardized message‑flow structure.
Standardized message interaction capabilities.
Business extensibility.
Low integration cost for downstream teams.
The design adopts dependency injection to allow business‑specific customizations while keeping a common core. The architecture is split into two layers:
Message‑flow structure layer – defines view, layout, and data conventions, and provides configuration for "Message" and "Conversation" dimensions.
Message interaction layer – offers message‑related operations and allows injection of custom interaction implementations.
Message components are divided into five parts, each represented by a MessageView subclass that supports style, visibility, and layout configuration. Sample code shows the base class with properties such as canPerformMenuAction , prepareForReuse , and a size‑strategy factory method.
open class MessageView: MessageAbstractView {
public var canPerformMenuAction = false
open func refresh(with message: Message) {}
open func prepareForReuse() {}
open class func createSizeStrategy(message: Message, fittingSize: CGSize) -> MessageLayoutSizeStrategy? {
// ...
}
}The size‑strategy subsystem defines a MessageLayoutSizeStrategy protocol with implementations for auto‑layout, sizeThatFits , and custom strategies. Example implementations illustrate how each strategy calculates the component size.
public protocol MessageLayoutSizeStrategy {
func caclulateSize(_ sizeViewType: MessageView.Type, message: Message, fittingSize: CGSize) -> CGSize
}
public struct MessageAutoLayoutSizeStrategy: MessageLayoutSizeStrategy {
public func caclulateSize(_ sizeViewType: MessageView.Type, message: Message, fittingSize: CGSize) -> CGSize {
// ...
return sizeView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
}
}
public struct MessageSizeThatFitsStrategy: MessageLayoutSizeStrategy {
public func caclulateSize(_ sizeViewType: MessageView.Type, message: Message, fittingSize: CGSize) -> CGSize {
// ...
return sizeView.sizeThatFits(fittingSize)
}
}A layout snapshot feature caches fixed component sizes to reduce layout computation, achieving a 10%‑20% CPU peak reduction during fast scrolling.
The interaction layer exposes standard IM actions (send, resend, forward, revoke, save, delete) via a MessageServiceInterface protocol, and supports custom implementations through dependency injection. Message sources are abstracted by a SessionMessageProvider protocol, allowing data from cloud‑IM services, HTTP APIs, or other back‑ends.
public protocol SessionMessageProvider {
func messages(in session: Session, anchorMessage: Message?, limit: Int, completion: @escaping ([Message]) -> Void)
}Interaction hooks such as MessageServicePrechecker and MessageServicePreparation enable pre‑send validation and preparation (e.g., uploading attachments, adding anti‑cheat tokens).
public protocol MessageServicePrechecker {
// Message send pre‑check
func shouldSend(message: Message, in session: Session) -> Bool
}
public protocol MessageServicePreparation {
func prepareSend(message: Message, in session: Session, callback: @escaping MessageServicePreparationCallback)
}Business integration only requires configuring message‑type to component‑type mappings and injecting the appropriate data‑source and service implementations. The solution has been applied across multiple IM scenarios within the team, eliminating duplicated code, lowering maintenance cost, and speeding up new‑feature onboarding.
Conclusion: The standardized, extensible IM message‑flow interaction layer provides a unified foundation for diverse messaging scenarios on iOS, improving development efficiency and reducing long‑term maintenance overhead.
NetEase Cloud Music Tech Team
Official account of NetEase Cloud Music Tech Team
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.