stream-chat-swiftui icon indicating copy to clipboard operation
stream-chat-swiftui copied to clipboard

ViewFactory should be @MainActor constrained

Open swizzlr opened this issue 11 months ago • 3 comments

What are you trying to achieve?

I have views that depend on MainActor isolated view models. In order to initialize my views I need to retrieve these MainActor isolated dependencies that are retrieved via "@Inject" in the viewfactory. Since the viewfactory methods are nonisolated, I'm not able to access these - and if I annotate the methods as @MainActor, they do not satisfy the protocol requirements.

If possible, how can you achieve this currently?

Maybe I could try injecting them directly into the views?

What would be the better way?

Since SwiftUI Views are MainActor isolated to begin with, and the ViewFactory is invoked from Views in a MainActor isolated context, it seems reasonable to constrain the ViewFactory to MainActor

GetStream Environment

GetStream Chat version: 4.61 GetStream Chat frameworks: StreamChat, StreamChatSwiftUI iOS version: any Swift version: 6 Xcode version: 16.2 Device: any

Additional context

n/a

swizzlr avatar Jan 17 '25 19:01 swizzlr

Hi @swizzlr,

We'll review it internally and will get back to you. Adding a @MainActor requirement might cause a breaking change for SDK users, therefore it needs to be considered carefully.

Thank you, Toomas

laevandus avatar Jan 20 '25 07:01 laevandus

We have a strong suspicion that using Swift 6 in the project and using the ViewFactory causes visible framerate issues. Using the predefined signleton implementation works without issues. Thank you!

andrej-jasso avatar May 20 '25 09:05 andrej-jasso

as @andrej-jasso mentioned, using Swift 6 in the project and annotating functions with @MainActor is causing slight freezes in the app. The more functions marked with @MainActor, the more noticeable the glitches — especially during scrolling messages and when opening or closing the keyboard.

Below are the implemented functions we suspect to be contributing to the problem: `

@MainActor
func makeChannelHeaderViewModifier(for channel: ChatChannel) -> some ChatChannelHeaderViewModifier {
    lastVisitedChannelId = channel.cid.id
    return NonNavigableChatHeaderModifier(channel: channel)
}

@MainActor
func makeMessageContainerView(
    channel: ChatChannel,
    message: ChatMessage,
    width: CGFloat?,
    showsAllInfo: Bool,
    isInThread: Bool,
    scrolledId: Binding<String?>,
    quotedMessage: Binding<ChatMessage?>,
    onLongPress: @escaping (MessageDisplayInfo) -> Void,
    isLast: Bool
) -> some View {
    MessageContainerWrapperView(
        factory: self,
        channel: channel,
        message: message,
        width: width,
        showsAllInfo: showsAllInfo,
        isInThread: isInThread,
        isLast: isLast,
        scrolledId: scrolledId,
        quotedMessage: quotedMessage,
        onLongPress: onLongPress
    )
}

@MainActor
func makeMessageAvatarView(for userDisplayInfo: UserDisplayInfo) -> some View {
    CustomMessageAvatarView(avatarURL: userDisplayInfo.imageURL, name: userDisplayInfo.name, id: lastVisitedChannelId)
}

marekVrican avatar Jun 17 '25 09:06 marekVrican