From fdca50179e95688aa1f685f8ab09c9689ca8716a Mon Sep 17 00:00:00 2001 From: shannon Date: Wed, 30 Oct 2024 14:38:21 -0400 Subject: [PATCH] Wait for authentications to be available before trying to use them. The prior fix for memory corruption problems in authentications had caused the app to launch to the new user screen every time. --- MastodonIntent/IntentHandler.swift | 1 + .../AuthenticationServiceProvider.swift | 16 +-- .../FollowersCount/FollowersCountWidget.swift | 2 +- .../Variants/Hashtag/HashtagWidget.swift | 128 +++++++++--------- .../MultiFollowersCountWidget.swift | 2 +- 5 files changed, 76 insertions(+), 73 deletions(-) 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