Skip to content

Commit a988c75

Browse files
committed
Use MainActor in StreamChat, Images, Fonts, Appearance, Utils
1 parent 471944e commit a988c75

36 files changed

+305
-278
lines changed

Sources/StreamChatSwiftUI/Appearance.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import SwiftUI
66

77
/// An object containing visual configuration for the whole application.
8-
public class Appearance {
8+
@MainActor public class Appearance {
99
public var colors: ColorPalette
1010
public var images: Images
1111
public var fonts: Fonts
@@ -21,21 +21,21 @@ public class Appearance {
2121
}
2222

2323
/// Provider for custom localization which is dependent on App Bundle.
24-
nonisolated(unsafe)
25-
public static var localizationProvider: @Sendable(_ key: String, _ table: String) -> String = { key, table in
24+
public nonisolated(unsafe)
25+
static var localizationProvider: @Sendable (_ key: String, _ table: String) -> String = { key, table in
2626
Bundle.streamChatUI.localizedString(forKey: key, value: nil, table: table)
2727
}
2828
}
2929

3030
// MARK: - Appearance + Default
3131

3232
public extension Appearance {
33-
nonisolated(unsafe) static var `default`: Appearance = .init()
33+
static let `default`: Appearance = .init()
3434
}
3535

3636
/// Provides the default value of the `Appearance` class.
3737
public struct AppearanceKey: EnvironmentKey {
38-
public static var defaultValue: Appearance { Appearance() }
38+
public static var defaultValue: Appearance { StreamConcurrency.onMain { Appearance() } }
3939
}
4040

4141
extension EnvironmentValues {

Sources/StreamChatSwiftUI/ChatChannel/ChannelControllerFactory.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import StreamChat
66
import SwiftUI
77

88
/// Factory for creating channel controllers.
9-
class ChannelControllerFactory {
9+
@MainActor class ChannelControllerFactory {
1010
@Injected(\.chatClient) var chatClient
1111

1212
var currentChannelController: ChatChannelController?
@@ -16,7 +16,7 @@ class ChannelControllerFactory {
1616
/// - Parameter channelId: the channel's id.
1717
/// - Returns: `ChatChannelController`
1818
func makeChannelController(for channelId: ChannelId) -> ChatChannelController {
19-
if let currentChannelController = currentChannelController, channelId == currentChannelController.cid {
19+
if let currentChannelController, channelId == currentChannelController.cid {
2020
return currentChannelController
2121
}
2222
let controller = chatClient.channelController(for: channelId)

Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/MediaAttachmentsViewModel.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ import SwiftUI
4747
if !loadingNextMessages {
4848
loadingNextMessages = true
4949
messageSearchController.loadNextMessages { [weak self] _ in
50-
guard let self = self else { return }
51-
self.updateAttachments()
52-
self.loadingNextMessages = false
50+
guard let self else { return }
51+
updateAttachments()
52+
loadingNextMessages = false
5353
}
5454
}
5555
}
@@ -68,9 +68,9 @@ import SwiftUI
6868

6969
loading = true
7070
messageSearchController.search(query: query, completion: { [weak self] _ in
71-
guard let self = self else { return }
72-
self.updateAttachments()
73-
self.loading = false
71+
guard let self else { return }
72+
updateAttachments()
73+
loading = false
7474
})
7575
}
7676

@@ -128,7 +128,7 @@ public struct MediaItem: Identifiable {
128128
self.imageAttachment = imageAttachment
129129
}
130130

131-
public var mediaAttachment: MediaAttachment? {
131+
@MainActor public var mediaAttachment: MediaAttachment? {
132132
if let videoAttachment {
133133
return MediaAttachment(url: videoAttachment.videoURL, type: .video)
134134
} else if let imageAttachment {

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelViewModel.swift

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@ import SwiftUI
1616
private var cancellables = Set<AnyCancellable>()
1717
private var lastRefreshThreshold = 200
1818
private let refreshThreshold = 200
19-
private static let newerMessagesLimit: Int = {
20-
if #available(iOS 17, *) {
21-
// On iOS 17 we can maintain the scroll position.
22-
return 25
23-
} else {
24-
return 5
25-
}
26-
}()
19+
private static let newerMessagesLimit: Int = if #available(iOS 17, *) {
20+
// On iOS 17 we can maintain the scroll position.
21+
25
22+
} else {
23+
5
24+
}
2725

2826
private var timer: Timer?
2927
private var currentDate: Date? {
@@ -145,7 +143,7 @@ import SwiftUI
145143
&& messageController == nil {
146144
channelController.synchronize()
147145
}
148-
if let messageController = messageController {
146+
if let messageController {
149147
self.messageController = messageController
150148
messageController.synchronize()
151149
channelDataSource = MessageThreadDataSource(
@@ -526,7 +524,7 @@ import SwiftUI
526524
before: nil,
527525
limit: utils.messageListConfig.pageSize,
528526
completion: { [weak self] _ in
529-
guard let self = self else { return }
527+
guard let self else { return }
530528
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
531529
self.loadingPreviousMessages = false
532530
}
@@ -546,7 +544,7 @@ import SwiftUI
546544
}
547545

548546
channelDataSource.loadNextMessages(limit: Self.newerMessagesLimit) { [weak self] _ in
549-
guard let self = self else { return }
547+
guard let self else { return }
550548
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
551549
self.loadingNextMessages = false
552550
}
@@ -642,7 +640,7 @@ import SwiftUI
642640
}
643641

644642
private func checkHeaderType() {
645-
guard let channel = channel else {
643+
guard let channel else {
646644
return
647645
}
648646

@@ -709,7 +707,7 @@ import SwiftUI
709707
}
710708

711709
private func handleDateChange() {
712-
guard showScrollToLatestButton == true, let currentDate = currentDate else {
710+
guard showScrollToLatestButton == true, let currentDate else {
713711
currentDateString = nil
714712
return
715713
}
@@ -763,7 +761,7 @@ import SwiftUI
763761
}
764762

765763
private func checkTypingIndicator() {
766-
guard let channel = channel else { return }
764+
guard let channel else { return }
767765
let shouldShow = !channel.currentlyTypingUsersFiltered(currentUserId: chatClient.currentUserId).isEmpty
768766
&& utils.messageListConfig.typingIndicatorPlacement == .bottomOverlay
769767
&& channel.config.typingEventsEnabled
@@ -804,11 +802,11 @@ import SwiftUI
804802
}
805803

806804
extension ChatMessage: Identifiable {
807-
public var scrollMessageId: String {
805+
@MainActor public var scrollMessageId: String {
808806
messageId
809807
}
810808

811-
var messageId: String {
809+
@MainActor var messageId: String {
812810
InjectedValues[\.utils].messageIdBuilder.makeMessageId(for: self)
813811
}
814812

Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel.swift

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import SwiftUI
1010
/// View model for the `MessageComposerView`.
1111
@MainActor open class MessageComposerViewModel: ObservableObject {
1212
@Injected(\.chatClient) private var chatClient
13-
@Injected(\.utils) internal var utils
13+
@Injected(\.utils) var utils
1414

1515
var attachmentsConverter = MessageAttachmentsConverter()
1616
var composerAssets: ComposerAssets {
@@ -202,13 +202,13 @@ import SwiftUI
202202
&& messageController == nil
203203
}
204204

205-
internal lazy var audioRecorder: AudioRecording = {
205+
lazy var audioRecorder: AudioRecording = {
206206
let audioRecorder = utils.audioRecorder
207207
audioRecorder.subscribe(self)
208208
return audioRecorder
209209
}()
210210

211-
internal lazy var audioAnalysisFactory: AudioAnalysisEngine? = try? .init(
211+
lazy var audioAnalysisFactory: AudioAnalysisEngine? = try? .init(
212212
assetPropertiesLoader: StreamAssetPropertyLoader()
213213
)
214214

@@ -228,12 +228,12 @@ import SwiftUI
228228
public var mentionedUsers = Set<ChatUser>()
229229

230230
private var messageText: String {
231-
if let composerCommand = composerCommand,
231+
if let composerCommand,
232232
let displayInfo = composerCommand.displayInfo,
233233
displayInfo.isInstant == true {
234-
return "\(composerCommand.id) \(text)"
234+
"\(composerCommand.id) \(text)"
235235
} else {
236-
return adjustedText
236+
adjustedText
237237
}
238238
}
239239

@@ -321,7 +321,7 @@ import SwiftUI
321321
let availableCommands = channelController.channel?.config.commands ?? []
322322
let command = availableCommands.first { composerCommand?.id == "/\($0.name)" }
323323

324-
if let messageController = messageController {
324+
if let messageController {
325325
messageController.updateDraftReply(
326326
text: messageText,
327327
isSilent: isSilent,
@@ -352,7 +352,7 @@ import SwiftUI
352352
return
353353
}
354354

355-
if let messageController = messageController {
355+
if let messageController {
356356
messageController.deleteDraftReply()
357357
} else {
358358
channelController.deleteDraftMessage()
@@ -366,13 +366,13 @@ import SwiftUI
366366
skipPush: Bool = false,
367367
skipEnrichUrl: Bool = false,
368368
extraData: [String: RawJSON] = [:],
369-
completion: @escaping @MainActor() -> Void
369+
completion: @escaping @MainActor () -> Void
370370
) {
371371
defer {
372372
checkChannelCooldown()
373373
}
374374

375-
if let composerCommand = composerCommand, composerCommand.id != "instantCommands" {
375+
if let composerCommand, composerCommand.id != "instantCommands" {
376376
commandsHandler.executeOnMessageSent(
377377
composerCommand: composerCommand
378378
) { [weak self] _ in
@@ -388,7 +388,7 @@ import SwiftUI
388388
clearRemovedMentions()
389389
let mentionedUserIds = mentionedUsers.map(\.id)
390390

391-
if let editedMessage = editedMessage {
391+
if let editedMessage {
392392
edit(
393393
message: editedMessage,
394394
attachments: try? convertAddedAssetsToPayloads(),
@@ -399,7 +399,7 @@ import SwiftUI
399399

400400
do {
401401
let attachments = try convertAddedAssetsToPayloads()
402-
if let messageController = messageController {
402+
if let messageController {
403403
messageController.createNewReply(
404404
text: messageText,
405405
attachments: attachments,
@@ -445,7 +445,7 @@ import SwiftUI
445445
}
446446

447447
public var sendButtonEnabled: Bool {
448-
if let composerCommand = composerCommand,
448+
if let composerCommand,
449449
let handler = commandsHandler.commandHandler(for: composerCommand) {
450450
return handler
451451
.canBeExecuted(composerCommand: composerCommand)
@@ -714,7 +714,7 @@ import SwiftUI
714714
private func edit(
715715
message: ChatMessage,
716716
attachments: [AnyAttachmentPayload]?,
717-
completion: @escaping @MainActor() -> Void
717+
completion: @escaping @MainActor () -> Void
718718
) {
719719
guard let channelId = channelController.channel?.cid else {
720720
return
@@ -782,7 +782,7 @@ import SwiftUI
782782
}
783783

784784
private func showTypingSuggestions() {
785-
if let composerCommand = composerCommand {
785+
if let composerCommand {
786786
commandsHandler.showSuggestions(for: composerCommand)
787787
.sink { _ in
788788
log.debug("Finished showing suggestions")
@@ -874,7 +874,7 @@ import SwiftUI
874874
}
875875

876876
private func checkAttachmentSize(with url: URL?) -> Bool {
877-
guard let url = url else { return true }
877+
guard let url else { return true }
878878

879879
_ = url.startAccessingSecurityScopedResource()
880880

@@ -938,7 +938,7 @@ struct FileAddedAsset: Sendable {
938938
}
939939

940940
// The converter responsible to map attachments to assets and vice versa.
941-
class MessageAttachmentsConverter {
941+
@MainActor class MessageAttachmentsConverter {
942942
@Injected(\.utils) var utils
943943

944944
/// Converts the added assets to payloads.
@@ -977,7 +977,7 @@ class MessageAttachmentsConverter {
977977
/// Converts the attachments to assets asynchronously.
978978
func attachmentsToAssets(
979979
_ attachments: [AnyChatMessageAttachment],
980-
completion: @escaping @MainActor(ComposerAssets) -> Void
980+
completion: @escaping @MainActor (ComposerAssets) -> Void
981981
) {
982982
let group = DispatchGroup()
983983
attachmentsToAssets(attachments, with: group, completion: completion)
@@ -991,11 +991,11 @@ class MessageAttachmentsConverter {
991991
func attachmentsToAssets(
992992
_ attachments: [AnyChatMessageAttachment],
993993
with group: DispatchGroup?,
994-
completion: @escaping @MainActor(ComposerAssets) -> Void
994+
completion: @escaping @MainActor (ComposerAssets) -> Void
995995
) {
996996
nonisolated(unsafe) var addedAssets = ComposerAssets()
997997

998-
attachments.forEach { attachment in
998+
for attachment in attachments {
999999
group?.enter()
10001000

10011001
switch attachment.type {
@@ -1089,7 +1089,7 @@ class MessageAttachmentsConverter {
10891089

10901090
private func imageAttachmentToAddedAsset(
10911091
_ attachment: AnyChatMessageAttachment,
1092-
completion: @escaping @Sendable(AddedAsset?) -> Void
1092+
completion: @escaping @Sendable (AddedAsset?) -> Void
10931093
) {
10941094
guard let imageAttachment = attachment.attachment(payloadType: ImageAttachmentPayload.self) else {
10951095
return completion(nil)

Sources/StreamChatSwiftUI/ChatChannel/Composer/PhotoAssetsUtils.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import StreamChat
77
import SwiftUI
88

99
/// Helper class that loads assets from the photo library.
10-
public class PhotoAssetLoader: NSObject, ObservableObject {
10+
@MainActor public class PhotoAssetLoader: NSObject, ObservableObject {
1111
@Injected(\.chatClient) private var chatClient
1212

1313
@Published var loadedImages = [String: UIImage]()
@@ -28,12 +28,12 @@ public class PhotoAssetLoader: NSObject, ObservableObject {
2828
contentMode: .aspectFit,
2929
options: options
3030
) { [weak self] image, _ in
31-
guard let self = self, let image = image else { return }
32-
self.loadedImages[asset.localIdentifier] = image
31+
guard let self, let image else { return }
32+
loadedImages[asset.localIdentifier] = image
3333
}
3434
}
3535

36-
func compressAsset(at url: URL, type: AssetType, completion: @escaping @MainActor(URL?) -> Void) {
36+
func compressAsset(at url: URL, type: AssetType, completion: @escaping @MainActor (URL?) -> Void) {
3737
if type == .video {
3838
let compressedURL = NSURL.fileURL(withPath: NSTemporaryDirectory() + UUID().uuidString + ".mp4")
3939
compressVideo(inputURL: url, outputURL: compressedURL) { exportSession in
@@ -66,7 +66,7 @@ public class PhotoAssetLoader: NSObject, ObservableObject {
6666
private func compressVideo(
6767
inputURL: URL,
6868
outputURL: URL,
69-
handler: @escaping @Sendable(_ exportSession: AVAssetExportSession?) -> Void
69+
handler: @escaping @Sendable (_ exportSession: AVAssetExportSession?) -> Void
7070
) {
7171
let urlAsset = AVURLAsset(url: inputURL, options: nil)
7272

0 commit comments

Comments
 (0)