mirror of
https://github.com/mastodon/mastodon-ios
synced 2025-04-11 22:58:02 +02:00
Increasing thread safety for authentications.
Attempting to fix multiple crashes associated with the call to persist() triggered by the authentications setter called in restore().
This commit is contained in:
parent
a9e088a9c7
commit
20f7d761bd
@ -17,7 +17,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
let appContext = AppContext()
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
AuthenticationServiceProvider.shared.restore()
|
||||
AuthenticationServiceProvider.shared.prepareForUse()
|
||||
|
||||
AppSecret.default.register()
|
||||
|
||||
|
@ -11,7 +11,7 @@ import MastodonCore
|
||||
class IntentHandler: INExtension {
|
||||
|
||||
override func handler(for intent: INIntent) -> Any {
|
||||
AuthenticationServiceProvider.shared.restore()
|
||||
AuthenticationServiceProvider.shared.prepareForUse()
|
||||
|
||||
switch intent {
|
||||
case is SendPostIntent:
|
||||
|
@ -19,7 +19,7 @@ public class AuthenticationServiceProvider: ObservableObject {
|
||||
|
||||
@Published public var authentications: [MastodonAuthentication] = [] {
|
||||
didSet {
|
||||
persist() // todo: Is this too heavy and too often here???
|
||||
persist(authentications)
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ public class AuthenticationServiceProvider: ObservableObject {
|
||||
return self
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func delete(authentication: MastodonAuthentication) throws {
|
||||
try Self.keychain.remove(authentication.persistenceIdentifier)
|
||||
authentications.removeAll(where: { $0 == authentication })
|
||||
@ -81,14 +82,22 @@ public extension AuthenticationServiceProvider {
|
||||
func authenticationSortedByActivation() -> [MastodonAuthentication] { // fixme: why do we need this?
|
||||
return authentications.sorted(by: { $0.activedAt > $1.activedAt })
|
||||
}
|
||||
|
||||
func prepareForUse() {
|
||||
if authentications.isEmpty {
|
||||
restoreFromKeychain()
|
||||
}
|
||||
}
|
||||
|
||||
func restore() {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,8 +126,10 @@ public extension AuthenticationServiceProvider {
|
||||
logger.log(level: .default, "All account authentications were successful.")
|
||||
}
|
||||
|
||||
self.authentications = migratedAuthentications
|
||||
userDefaults.didMigrateAuthentications = true
|
||||
DispatchQueue.main.async {
|
||||
self.authentications = migratedAuthentications
|
||||
self.userDefaults.didMigrateAuthentications = true
|
||||
}
|
||||
} catch {
|
||||
userDefaults.didMigrateAuthentications = false
|
||||
logger.log(level: .error, "Could not migrate legacy authentications")
|
||||
@ -148,9 +159,11 @@ public extension AuthenticationServiceProvider {
|
||||
|
||||
// MARK: - Private
|
||||
private extension AuthenticationServiceProvider {
|
||||
func persist() {
|
||||
for authentication in authentications {
|
||||
Self.keychain[authentication.persistenceIdentifier] = try? JSONEncoder().encode(authentication).base64EncodedString()
|
||||
func persist(_ authentications: [MastodonAuthentication]) {
|
||||
DispatchQueue.main.async {
|
||||
for authentication in authentications {
|
||||
Self.keychain[authentication.persistenceIdentifier] = try? JSONEncoder().encode(authentication).base64EncodedString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,13 +137,13 @@ extension AuthenticationService {
|
||||
}
|
||||
|
||||
public func signOutMastodonUser(authentication: MastodonAuthentication) async throws {
|
||||
try AuthenticationServiceProvider.shared.delete(authentication: authentication)
|
||||
try await AuthenticationServiceProvider.shared.delete(authentication: authentication)
|
||||
_ = try await apiService?.cancelSubscription(domain: authentication.domain, authorization: authentication.authorization)
|
||||
}
|
||||
|
||||
public func signOutMastodonUser(authenticationBox: MastodonAuthenticationBox) async throws {
|
||||
do {
|
||||
try AuthenticationServiceProvider.shared.delete(authentication: authenticationBox.authentication)
|
||||
try await AuthenticationServiceProvider.shared.delete(authentication: authenticationBox.authentication)
|
||||
} catch {
|
||||
assertionFailure("Failed to delete Authentication: \(error)")
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ extension ShareViewController {
|
||||
|
||||
extension ShareViewController {
|
||||
private func setupAuthContext() throws -> AuthContext? {
|
||||
AuthenticationServiceProvider.shared.restore()
|
||||
AuthenticationServiceProvider.shared.prepareForUse()
|
||||
|
||||
let authentication = AuthenticationServiceProvider.shared.authenticationSortedByActivation().first
|
||||
let authContext = authentication.flatMap { AuthContext(authentication: $0) }
|
||||
|
@ -73,7 +73,7 @@ private extension FollowersCountWidgetProvider {
|
||||
func loadCurrentEntry(for configuration: FollowersCountIntent, in context: Context, completion: @escaping (FollowersCountEntry) -> Void) {
|
||||
Task {
|
||||
|
||||
AuthenticationServiceProvider.shared.restore()
|
||||
AuthenticationServiceProvider.shared.prepareForUse()
|
||||
|
||||
guard
|
||||
let authBox = WidgetExtension.appContext
|
||||
|
@ -25,7 +25,7 @@ struct HashtagWidgetProvider: IntentTimelineProvider {
|
||||
extension HashtagWidgetProvider {
|
||||
func loadMostRecentHashtag(for configuration: HashtagIntent, in context: Context, completion: @escaping (HashtagWidgetTimelineEntry) -> Void ) {
|
||||
|
||||
AuthenticationServiceProvider.shared.restore()
|
||||
AuthenticationServiceProvider.shared.prepareForUse()
|
||||
|
||||
guard
|
||||
let authBox = WidgetExtension.appContext
|
||||
|
@ -80,7 +80,7 @@ private extension LatestFollowersWidgetProvider {
|
||||
func loadCurrentEntry(for configuration: LatestFollowersIntent, in context: Context, completion: @escaping (LatestFollowersEntry) -> Void) {
|
||||
Task { @MainActor in
|
||||
|
||||
AuthenticationServiceProvider.shared.restore()
|
||||
AuthenticationServiceProvider.shared.prepareForUse()
|
||||
|
||||
guard
|
||||
let authBox = WidgetExtension.appContext
|
||||
|
@ -73,7 +73,7 @@ private extension MultiFollowersCountWidgetProvider {
|
||||
func loadCurrentEntry(for configuration: MultiFollowersCountIntent, in context: Context, completion: @escaping (MultiFollowersCountEntry) -> Void) {
|
||||
Task {
|
||||
|
||||
AuthenticationServiceProvider.shared.restore()
|
||||
AuthenticationServiceProvider.shared.prepareForUse()
|
||||
|
||||
guard
|
||||
let authBox = WidgetExtension.appContext
|
||||
|
Loading…
x
Reference in New Issue
Block a user