diff --git a/MastodonIntent/IntentHandler.swift b/MastodonIntent/IntentHandler.swift index 88544e711a..7e4ddfdcf5 100644 --- a/MastodonIntent/IntentHandler.swift +++ b/MastodonIntent/IntentHandler.swift @@ -10,6 +10,7 @@ import MastodonCore class IntentHandler: INExtension { + @MainActor override func handler(for intent: INIntent) -> Any { AuthenticationServiceProvider.shared.prepareForUse() diff --git a/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift b/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift index a391002d35..6c51e557e2 100644 --- a/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift +++ b/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift @@ -83,21 +83,21 @@ public extension AuthenticationServiceProvider { return authentications.sorted(by: { $0.activedAt > $1.activedAt }) } + @MainActor func prepareForUse() { if authentications.isEmpty { restoreFromKeychain() } } + @MainActor private func restoreFromKeychain() { - DispatchQueue.main.async { - self.authentications = Self.keychain.allKeys().compactMap { - guard - let encoded = Self.keychain[$0], - let data = Data(base64Encoded: encoded) - else { return nil } - return try? JSONDecoder().decode(MastodonAuthentication.self, from: data) - } + self.authentications = Self.keychain.allKeys().compactMap { + guard + let encoded = Self.keychain[$0], + let data = Data(base64Encoded: encoded) + else { return nil } + return try? JSONDecoder().decode(MastodonAuthentication.self, from: data) } } diff --git a/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift b/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift index 59dbdea1ea..45dcbd1f9d 100644 --- a/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift +++ b/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift @@ -73,7 +73,7 @@ private extension FollowersCountWidgetProvider { func loadCurrentEntry(for configuration: FollowersCountIntent, in context: Context, completion: @escaping (FollowersCountEntry) -> Void) { Task { - AuthenticationServiceProvider.shared.prepareForUse() + await AuthenticationServiceProvider.shared.prepareForUse() guard let authBox = WidgetExtension.appContext diff --git a/WidgetExtension/Variants/Hashtag/HashtagWidget.swift b/WidgetExtension/Variants/Hashtag/HashtagWidget.swift index 0ac6fd6ee7..a8f43939f0 100644 --- a/WidgetExtension/Variants/Hashtag/HashtagWidget.swift +++ b/WidgetExtension/Variants/Hashtag/HashtagWidget.swift @@ -24,75 +24,77 @@ struct HashtagWidgetProvider: IntentTimelineProvider { extension HashtagWidgetProvider { func loadMostRecentHashtag(for configuration: HashtagIntent, in context: Context, completion: @escaping (HashtagWidgetTimelineEntry) -> Void ) { - - AuthenticationServiceProvider.shared.prepareForUse() - - guard - let authBox = WidgetExtension.appContext - .authenticationService - .mastodonAuthenticationBoxes - .first - else { - if context.isPreview { - return completion(.placeholder) - } else { - return completion(.unconfigured) - } - } - - let desiredHashtag: String - - if let hashtag = configuration.hashtag { - desiredHashtag = hashtag.replacingOccurrences(of: "#", with: "") - } else { - return completion(.notFound("hashtag")) - } - + Task { - - do { - let mostRecentStatuses = try await WidgetExtension.appContext - .apiService - .hashtagTimeline(limit: 40, hashtag: desiredHashtag, authenticationBox: authBox) - .value - - let filteredStatuses: [Mastodon.Entity.Status] - if configuration.ignoreContentWarnings?.boolValue == true { - filteredStatuses = mostRecentStatuses - } else { - filteredStatuses = mostRecentStatuses.filter { $0.sensitive == false } + await MainActor.run { + + AuthenticationServiceProvider.shared.prepareForUse() + + guard + let authBox = WidgetExtension.appContext + .authenticationService + .mastodonAuthenticationBoxes + .first + else { + if context.isPreview { + return completion(.placeholder) + } else { + return completion(.unconfigured) + } } - - if let mostRecentStatus = filteredStatuses.first { - - let hashtagEntry = HashtagEntry( - accountName: mostRecentStatus.account.displayNameWithFallback, - account: mostRecentStatus.account.acct, - content: mostRecentStatus.content ?? "-", - reblogCount: mostRecentStatus.reblogsCount, - favoriteCount: mostRecentStatus.favouritesCount, - hashtag: "#\(desiredHashtag)", - timestamp: mostRecentStatus.createdAt - ) - - let hashtagTimelineEntry = HashtagWidgetTimelineEntry( - date: mostRecentStatus.createdAt, - hashtag: hashtagEntry - ) - - completion(hashtagTimelineEntry) + + let desiredHashtag: String + + if let hashtag = configuration.hashtag { + desiredHashtag = hashtag.replacingOccurrences(of: "#", with: "") } else { - let noStatusFound = HashtagWidgetTimelineEntry.notFound(desiredHashtag) - - completion(noStatusFound) + return completion(.notFound("hashtag")) + } + + Task { + + do { + let mostRecentStatuses = try await WidgetExtension.appContext + .apiService + .hashtagTimeline(limit: 40, hashtag: desiredHashtag, authenticationBox: authBox) + .value + + let filteredStatuses: [Mastodon.Entity.Status] + if configuration.ignoreContentWarnings?.boolValue == true { + filteredStatuses = mostRecentStatuses + } else { + filteredStatuses = mostRecentStatuses.filter { $0.sensitive == false } + } + + if let mostRecentStatus = filteredStatuses.first { + + let hashtagEntry = HashtagEntry( + accountName: mostRecentStatus.account.displayNameWithFallback, + account: mostRecentStatus.account.acct, + content: mostRecentStatus.content ?? "-", + reblogCount: mostRecentStatus.reblogsCount, + favoriteCount: mostRecentStatus.favouritesCount, + hashtag: "#\(desiredHashtag)", + timestamp: mostRecentStatus.createdAt + ) + + let hashtagTimelineEntry = HashtagWidgetTimelineEntry( + date: mostRecentStatus.createdAt, + hashtag: hashtagEntry + ) + + completion(hashtagTimelineEntry) + } else { + let noStatusFound = HashtagWidgetTimelineEntry.notFound(desiredHashtag) + + completion(noStatusFound) + } + } catch { + completion(.notFound(desiredHashtag)) + } } - } catch { - completion(.notFound(desiredHashtag)) } } - - - } } diff --git a/WidgetExtension/Variants/MultiFollowersCount/MultiFollowersCountWidget.swift b/WidgetExtension/Variants/MultiFollowersCount/MultiFollowersCountWidget.swift index 588e6c558f..2cb9748d5a 100644 --- a/WidgetExtension/Variants/MultiFollowersCount/MultiFollowersCountWidget.swift +++ b/WidgetExtension/Variants/MultiFollowersCount/MultiFollowersCountWidget.swift @@ -73,7 +73,7 @@ private extension MultiFollowersCountWidgetProvider { func loadCurrentEntry(for configuration: MultiFollowersCountIntent, in context: Context, completion: @escaping (MultiFollowersCountEntry) -> Void) { Task { - AuthenticationServiceProvider.shared.prepareForUse() + await AuthenticationServiceProvider.shared.prepareForUse() guard let authBox = WidgetExtension.appContext