Fix crash issue after first sign in.

This commit is contained in:
Marcin Czachursk 2023-03-29 17:06:41 +02:00
parent 68e40d2fd1
commit 2320a83ff5
7 changed files with 71 additions and 57 deletions

View File

@ -35,9 +35,9 @@ class ApplicationSettingsHandler {
}
}
func set(accountData: AccountData?) {
func set(accountId: String?) {
let defaultSettings = self.get()
defaultSettings.currentAccount = accountData?.id
defaultSettings.currentAccount = accountId
CoreDataHandler.shared.save()
}

View File

@ -1308,7 +1308,7 @@
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 88;
CURRENT_PROJECT_VERSION = 89;
DEVELOPMENT_TEAM = B2U9FEKYP8;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VernissageWidget/Info.plist;
@ -1336,7 +1336,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 88;
CURRENT_PROJECT_VERSION = 89;
DEVELOPMENT_TEAM = B2U9FEKYP8;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VernissageWidget/Info.plist;
@ -1484,7 +1484,7 @@
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 88;
CURRENT_PROJECT_VERSION = 89;
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
DEVELOPMENT_TEAM = B2U9FEKYP8;
ENABLE_PREVIEWS = YES;
@ -1524,7 +1524,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 88;
CURRENT_PROJECT_VERSION = 89;
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
DEVELOPMENT_TEAM = B2U9FEKYP8;
ENABLE_PREVIEWS = YES;

View File

@ -15,28 +15,28 @@ public class AuthorizationService {
private init() { }
/// Access token verification.
public func verifyAccount(session: AuthorizationSession, currentAccount: AccountData?, _ result: @escaping (AccountData?) -> Void) async {
public func verifyAccount(session: AuthorizationSession, accountModel: AccountModel, _ result: @escaping (AccountModel?) -> Void) async {
// When we dont have even one account stored in database then we have to ask user to enter server and sign in.
guard let currentAccount, let accessToken = currentAccount.accessToken else {
guard let accessToken = accountModel.accessToken else {
result(nil)
return
}
// When we have at least one account then we have to verify access token.
let client = PixelfedClient(baseURL: currentAccount.serverUrl).getAuthenticated(token: accessToken)
let client = PixelfedClient(baseURL: accountModel.serverUrl).getAuthenticated(token: accessToken)
do {
let account = try await client.verifyCredentials()
await self.update(accountId: currentAccount.id,
let signedInAccountModel = await self.update(accountId: accountModel.id,
basedOn: account,
accessToken: accessToken,
refreshToken: currentAccount.refreshToken)
refreshToken: accountModel.refreshToken)
result(currentAccount)
result(signedInAccountModel)
} catch {
do {
try await self.refreshCredentials(for: currentAccount, presentationContextProvider: session)
result(currentAccount)
let signedInAccountModel = try await self.refreshCredentials(for: accountModel, presentationContextProvider: session)
result(signedInAccountModel)
} catch {
ErrorService.shared.handle(error, message: "Issues during refreshing credentials.", showToastr: true)
}
@ -44,7 +44,7 @@ public class AuthorizationService {
}
/// Sign in to the Pixelfed server.
public func sign(in serverAddress: String, session: AuthorizationSession, _ result: @escaping (AccountData) -> Void) async throws {
public func sign(in serverAddress: String, session: AuthorizationSession, _ result: @escaping (AccountModel) -> Void) async throws {
guard let baseUrl = URL(string: serverAddress) else {
throw AuthorisationError.badServerUrl
@ -124,7 +124,8 @@ public class AuthorizationService {
CoreDataHandler.shared.save(viewContext: backgroundContext)
// Return account data.
result(accountData)
let accountModel = AccountModel(accountData: accountData)
result(accountModel)
}
public func refreshAccessTokens() async {
@ -134,7 +135,7 @@ public class AuthorizationService {
for account in accounts {
group.addTask {
do {
try await self.refreshAccessToken(accountData: account)
_ = try await self.refreshAccessToken(accountData: account)
#if DEBUG
ToastrService.shared.showSuccess("New access tokens has been retrieved.", imageSystemName: "key.fill")
@ -151,11 +152,11 @@ public class AuthorizationService {
}
}
private func refreshAccessToken(accountData: AccountData) async throws {
private func refreshAccessToken(accountData: AccountData) async throws -> AccountModel? {
let client = PixelfedClient(baseURL: accountData.serverUrl)
guard let refreshToken = accountData.refreshToken else {
return
return nil
}
let oAuthSwiftCredential = try await client.refreshToken(clientId: accountData.clientId,
@ -167,21 +168,22 @@ public class AuthorizationService {
// Get account information from server.
let account = try await authenticatedClient.verifyCredentials()
await self.update(accountId: accountData.id,
return await self.update(accountId: accountData.id,
basedOn: account,
accessToken: oAuthSwiftCredential.oauthToken,
refreshToken: oAuthSwiftCredential.oauthRefreshToken)
}
private func refreshCredentials(for accountData: AccountData,
private func refreshCredentials(for accountModel: AccountModel,
presentationContextProvider: ASWebAuthenticationPresentationContextProviding
) async throws {
) async throws -> AccountModel? {
let client = PixelfedClient(baseURL: accountData.serverUrl)
let client = PixelfedClient(baseURL: accountModel.serverUrl)
// Create application (we will get clientId and clientSecret).
let oAuthApp = Application(clientId: accountData.clientId,
clientSecret: accountData.clientSecret,
let oAuthApp = Application(clientId: accountModel.clientId,
clientSecret: accountModel.clientSecret,
redirectUri: AppConstants.oauthRedirectUri)
// Authorize a user (browser, we will get clientCode).
@ -195,7 +197,8 @@ public class AuthorizationService {
// Get account information from server.
let account = try await authenticatedClient.verifyCredentials()
await self.update(accountId: accountData.id,
return await self.update(accountId: accountModel.id,
basedOn: account,
accessToken: oAuthSwiftCredential.oauthToken,
refreshToken: oAuthSwiftCredential.oauthRefreshToken)
@ -205,10 +208,10 @@ public class AuthorizationService {
basedOn account: Account,
accessToken: String,
refreshToken: String?
) async {
) async -> AccountModel? {
let backgroundContext = CoreDataHandler.shared.newBackgroundContext()
guard let dbAccount = AccountDataHandler.shared.getAccountData(accountId: accountId, viewContext: backgroundContext) else {
return
return nil
}
dbAccount.username = account.username
@ -241,6 +244,8 @@ public class AuthorizationService {
// Save account data in database and in application state.
CoreDataHandler.shared.save(viewContext: backgroundContext)
return AccountModel(accountData: dbAccount)
}
private func getAccountData(account: Account, backgroundContext: NSManagedObjectContext) -> AccountData {

View File

@ -34,8 +34,8 @@ struct VernissageApp: App {
.withAppRouteur()
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
case .signIn:
SignInView { accountData in
self.setApplicationState(accountData: accountData)
SignInView { accountModel in
self.setApplicationState(accountModel: accountModel)
}
.withAppRouteur()
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
@ -91,6 +91,7 @@ struct VernissageApp: App {
}
}
@MainActor
private func onApplicationStart() async {
UIPageControl.appearance().currentPageIndicatorTintColor = UIColor.white.withAlphaComponent(0.7)
UIPageControl.appearance().pageIndicatorTintColor = UIColor.white.withAlphaComponent(0.4)
@ -104,22 +105,29 @@ struct VernissageApp: App {
// Refresh other access tokens.
await self.refreshAccessTokens()
// Verify access token correctness.
let authorizationSession = AuthorizationSession()
let currentAccount = AccountDataHandler.shared.getCurrentAccountData()
await AuthorizationService.shared.verifyAccount(session: authorizationSession, currentAccount: currentAccount) { accountData in
guard let accountData = accountData else {
// When user doesn't exists then we have to open sign in view.
guard let currentAccount = AccountDataHandler.shared.getCurrentAccountData() else {
self.applicationViewMode = .signIn
return
}
self.setApplicationState(accountData: accountData, checkNewPhotos: true)
// Create model based on core data entity.
let accountModel = AccountModel(accountData: currentAccount)
// Verify access token correctness.
let authorizationSession = AuthorizationSession()
await AuthorizationService.shared.verifyAccount(session: authorizationSession, accountModel: accountModel) { signedInAccountModel in
guard let signedInAccountModel else {
self.applicationViewMode = .signIn
return
}
self.setApplicationState(accountModel: signedInAccountModel, checkNewPhotos: true)
}
}
private func setApplicationState(accountData: AccountData, checkNewPhotos: Bool = false) {
private func setApplicationState(accountModel: AccountModel, checkNewPhotos: Bool = false) {
Task { @MainActor in
let accountModel = AccountModel(accountData: accountData)
let instance = try? await self.client.instances.instance(url: accountModel.serverUrl)
// Refresh client state.
@ -128,7 +136,7 @@ struct VernissageApp: App {
// Refresh application state.
self.applicationState.changeApplicationState(accountModel: accountModel,
instance: instance,
lastSeenStatusId: accountData.lastSeenStatusId)
lastSeenStatusId: accountModel.lastSeenStatusId)
// Change view displayed by application.
self.applicationViewMode = .mainView

View File

@ -323,26 +323,27 @@ struct MainView: View {
Task {
// Verify access token correctness.
let authorizationSession = AuthorizationSession()
await AuthorizationService.shared.verifyAccount(session: authorizationSession, currentAccount: account) { accountData in
guard let accountData = accountData else {
let accountModel = AccountModel(accountData: account)
await AuthorizationService.shared.verifyAccount(session: authorizationSession, accountModel: accountModel) { signedInAccountModel in
guard let signedInAccountModel else {
ToastrService.shared.showError(subtitle: "mainview.error.switchAccounts")
return
}
Task { @MainActor in
let accountModel = AccountModel(accountData: accountData)
let instance = try? await self.client.instances.instance(url: accountModel.serverUrl)
let instance = try? await self.client.instances.instance(url: signedInAccountModel.serverUrl)
// Refresh client state.
self.client.setAccount(account: accountModel)
self.client.setAccount(account: signedInAccountModel)
// Refresh application state.
self.applicationState.changeApplicationState(accountModel: accountModel,
self.applicationState.changeApplicationState(accountModel: signedInAccountModel,
instance: instance,
lastSeenStatusId: accountData.lastSeenStatusId)
lastSeenStatusId: signedInAccountModel.lastSeenStatusId)
// Set account as default (application will open this account after restart).
ApplicationSettingsHandler.shared.set(accountData: accountData)
ApplicationSettingsHandler.shared.set(accountId: signedInAccountModel.id)
}
}
}

View File

@ -72,7 +72,7 @@ struct AccountsSectionView: View {
if shouldClearApplicationState {
// We have to do this after animation of deleting row is ended.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
ApplicationSettingsHandler.shared.set(accountData: nil)
ApplicationSettingsHandler.shared.set(accountId: nil)
self.applicationState.clearApplicationState()
self.client.clearAccount()
}

View File

@ -19,7 +19,7 @@ struct SignInView: View {
@State private var instructionsUrlString:String?
@State private var instances: [Instance] = []
var onSignedIn: ((_ accountData: AccountData) -> Void)?
var onSignedIn: ((_ accountModel: AccountModel) -> Void)?
var body: some View {
List {
@ -91,8 +91,8 @@ struct SignInView: View {
Task {
do {
let authorizationSession = AuthorizationSession()
try await AuthorizationService.shared.sign(in: baseAddress, session: authorizationSession) { accountData in
onSignedIn?(accountData)
try await AuthorizationService.shared.sign(in: baseAddress, session: authorizationSession) { accountModel in
onSignedIn?(accountModel)
DispatchQueue.main.sync {
dismiss()