Fix crash issue after first sign in.
This commit is contained in:
parent
68e40d2fd1
commit
2320a83ff5
|
@ -35,9 +35,9 @@ class ApplicationSettingsHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func set(accountData: AccountData?) {
|
func set(accountId: String?) {
|
||||||
let defaultSettings = self.get()
|
let defaultSettings = self.get()
|
||||||
defaultSettings.currentAccount = accountData?.id
|
defaultSettings.currentAccount = accountId
|
||||||
CoreDataHandler.shared.save()
|
CoreDataHandler.shared.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1308,7 +1308,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 88;
|
CURRENT_PROJECT_VERSION = 89;
|
||||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = VernissageWidget/Info.plist;
|
INFOPLIST_FILE = VernissageWidget/Info.plist;
|
||||||
|
@ -1336,7 +1336,7 @@
|
||||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||||
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 88;
|
CURRENT_PROJECT_VERSION = 89;
|
||||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = VernissageWidget/Info.plist;
|
INFOPLIST_FILE = VernissageWidget/Info.plist;
|
||||||
|
@ -1484,7 +1484,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 88;
|
CURRENT_PROJECT_VERSION = 89;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
@ -1524,7 +1524,7 @@
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 88;
|
CURRENT_PROJECT_VERSION = 89;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
DEVELOPMENT_TEAM = B2U9FEKYP8;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
|
|
@ -15,28 +15,28 @@ public class AuthorizationService {
|
||||||
private init() { }
|
private init() { }
|
||||||
|
|
||||||
/// Access token verification.
|
/// 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.
|
// 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)
|
result(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// When we have at least one account then we have to verify access token.
|
// 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 {
|
do {
|
||||||
let account = try await client.verifyCredentials()
|
let account = try await client.verifyCredentials()
|
||||||
await self.update(accountId: currentAccount.id,
|
let signedInAccountModel = await self.update(accountId: accountModel.id,
|
||||||
basedOn: account,
|
basedOn: account,
|
||||||
accessToken: accessToken,
|
accessToken: accessToken,
|
||||||
refreshToken: currentAccount.refreshToken)
|
refreshToken: accountModel.refreshToken)
|
||||||
|
|
||||||
result(currentAccount)
|
result(signedInAccountModel)
|
||||||
} catch {
|
} catch {
|
||||||
do {
|
do {
|
||||||
try await self.refreshCredentials(for: currentAccount, presentationContextProvider: session)
|
let signedInAccountModel = try await self.refreshCredentials(for: accountModel, presentationContextProvider: session)
|
||||||
result(currentAccount)
|
result(signedInAccountModel)
|
||||||
} catch {
|
} catch {
|
||||||
ErrorService.shared.handle(error, message: "Issues during refreshing credentials.", showToastr: true)
|
ErrorService.shared.handle(error, message: "Issues during refreshing credentials.", showToastr: true)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ public class AuthorizationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign in to the Pixelfed server.
|
/// 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 {
|
guard let baseUrl = URL(string: serverAddress) else {
|
||||||
throw AuthorisationError.badServerUrl
|
throw AuthorisationError.badServerUrl
|
||||||
|
@ -124,7 +124,8 @@ public class AuthorizationService {
|
||||||
CoreDataHandler.shared.save(viewContext: backgroundContext)
|
CoreDataHandler.shared.save(viewContext: backgroundContext)
|
||||||
|
|
||||||
// Return account data.
|
// Return account data.
|
||||||
result(accountData)
|
let accountModel = AccountModel(accountData: accountData)
|
||||||
|
result(accountModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func refreshAccessTokens() async {
|
public func refreshAccessTokens() async {
|
||||||
|
@ -134,7 +135,7 @@ public class AuthorizationService {
|
||||||
for account in accounts {
|
for account in accounts {
|
||||||
group.addTask {
|
group.addTask {
|
||||||
do {
|
do {
|
||||||
try await self.refreshAccessToken(accountData: account)
|
_ = try await self.refreshAccessToken(accountData: account)
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
ToastrService.shared.showSuccess("New access tokens has been retrieved.", imageSystemName: "key.fill")
|
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)
|
let client = PixelfedClient(baseURL: accountData.serverUrl)
|
||||||
|
|
||||||
guard let refreshToken = accountData.refreshToken else {
|
guard let refreshToken = accountData.refreshToken else {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let oAuthSwiftCredential = try await client.refreshToken(clientId: accountData.clientId,
|
let oAuthSwiftCredential = try await client.refreshToken(clientId: accountData.clientId,
|
||||||
|
@ -167,21 +168,22 @@ public class AuthorizationService {
|
||||||
|
|
||||||
// Get account information from server.
|
// Get account information from server.
|
||||||
let account = try await authenticatedClient.verifyCredentials()
|
let account = try await authenticatedClient.verifyCredentials()
|
||||||
await self.update(accountId: accountData.id,
|
|
||||||
basedOn: account,
|
return await self.update(accountId: accountData.id,
|
||||||
accessToken: oAuthSwiftCredential.oauthToken,
|
basedOn: account,
|
||||||
refreshToken: oAuthSwiftCredential.oauthRefreshToken)
|
accessToken: oAuthSwiftCredential.oauthToken,
|
||||||
|
refreshToken: oAuthSwiftCredential.oauthRefreshToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func refreshCredentials(for accountData: AccountData,
|
private func refreshCredentials(for accountModel: AccountModel,
|
||||||
presentationContextProvider: ASWebAuthenticationPresentationContextProviding
|
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).
|
// Create application (we will get clientId and clientSecret).
|
||||||
let oAuthApp = Application(clientId: accountData.clientId,
|
let oAuthApp = Application(clientId: accountModel.clientId,
|
||||||
clientSecret: accountData.clientSecret,
|
clientSecret: accountModel.clientSecret,
|
||||||
redirectUri: AppConstants.oauthRedirectUri)
|
redirectUri: AppConstants.oauthRedirectUri)
|
||||||
|
|
||||||
// Authorize a user (browser, we will get clientCode).
|
// Authorize a user (browser, we will get clientCode).
|
||||||
|
@ -195,20 +197,21 @@ public class AuthorizationService {
|
||||||
|
|
||||||
// Get account information from server.
|
// Get account information from server.
|
||||||
let account = try await authenticatedClient.verifyCredentials()
|
let account = try await authenticatedClient.verifyCredentials()
|
||||||
await self.update(accountId: accountData.id,
|
|
||||||
basedOn: account,
|
return await self.update(accountId: accountModel.id,
|
||||||
accessToken: oAuthSwiftCredential.oauthToken,
|
basedOn: account,
|
||||||
refreshToken: oAuthSwiftCredential.oauthRefreshToken)
|
accessToken: oAuthSwiftCredential.oauthToken,
|
||||||
|
refreshToken: oAuthSwiftCredential.oauthRefreshToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func update(accountId: String,
|
private func update(accountId: String,
|
||||||
basedOn account: Account,
|
basedOn account: Account,
|
||||||
accessToken: String,
|
accessToken: String,
|
||||||
refreshToken: String?
|
refreshToken: String?
|
||||||
) async {
|
) async -> AccountModel? {
|
||||||
let backgroundContext = CoreDataHandler.shared.newBackgroundContext()
|
let backgroundContext = CoreDataHandler.shared.newBackgroundContext()
|
||||||
guard let dbAccount = AccountDataHandler.shared.getAccountData(accountId: accountId, viewContext: backgroundContext) else {
|
guard let dbAccount = AccountDataHandler.shared.getAccountData(accountId: accountId, viewContext: backgroundContext) else {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dbAccount.username = account.username
|
dbAccount.username = account.username
|
||||||
|
@ -241,6 +244,8 @@ public class AuthorizationService {
|
||||||
|
|
||||||
// Save account data in database and in application state.
|
// Save account data in database and in application state.
|
||||||
CoreDataHandler.shared.save(viewContext: backgroundContext)
|
CoreDataHandler.shared.save(viewContext: backgroundContext)
|
||||||
|
|
||||||
|
return AccountModel(accountData: dbAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getAccountData(account: Account, backgroundContext: NSManagedObjectContext) -> AccountData {
|
private func getAccountData(account: Account, backgroundContext: NSManagedObjectContext) -> AccountData {
|
||||||
|
|
|
@ -34,8 +34,8 @@ struct VernissageApp: App {
|
||||||
.withAppRouteur()
|
.withAppRouteur()
|
||||||
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
|
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
|
||||||
case .signIn:
|
case .signIn:
|
||||||
SignInView { accountData in
|
SignInView { accountModel in
|
||||||
self.setApplicationState(accountData: accountData)
|
self.setApplicationState(accountModel: accountModel)
|
||||||
}
|
}
|
||||||
.withAppRouteur()
|
.withAppRouteur()
|
||||||
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
|
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
|
||||||
|
@ -91,6 +91,7 @@ struct VernissageApp: App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
private func onApplicationStart() async {
|
private func onApplicationStart() async {
|
||||||
UIPageControl.appearance().currentPageIndicatorTintColor = UIColor.white.withAlphaComponent(0.7)
|
UIPageControl.appearance().currentPageIndicatorTintColor = UIColor.white.withAlphaComponent(0.7)
|
||||||
UIPageControl.appearance().pageIndicatorTintColor = UIColor.white.withAlphaComponent(0.4)
|
UIPageControl.appearance().pageIndicatorTintColor = UIColor.white.withAlphaComponent(0.4)
|
||||||
|
@ -104,22 +105,29 @@ struct VernissageApp: App {
|
||||||
// Refresh other access tokens.
|
// Refresh other access tokens.
|
||||||
await self.refreshAccessTokens()
|
await self.refreshAccessTokens()
|
||||||
|
|
||||||
|
// When user doesn't exists then we have to open sign in view.
|
||||||
|
guard let currentAccount = AccountDataHandler.shared.getCurrentAccountData() else {
|
||||||
|
self.applicationViewMode = .signIn
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create model based on core data entity.
|
||||||
|
let accountModel = AccountModel(accountData: currentAccount)
|
||||||
|
|
||||||
// Verify access token correctness.
|
// Verify access token correctness.
|
||||||
let authorizationSession = AuthorizationSession()
|
let authorizationSession = AuthorizationSession()
|
||||||
let currentAccount = AccountDataHandler.shared.getCurrentAccountData()
|
await AuthorizationService.shared.verifyAccount(session: authorizationSession, accountModel: accountModel) { signedInAccountModel in
|
||||||
await AuthorizationService.shared.verifyAccount(session: authorizationSession, currentAccount: currentAccount) { accountData in
|
guard let signedInAccountModel else {
|
||||||
guard let accountData = accountData else {
|
|
||||||
self.applicationViewMode = .signIn
|
self.applicationViewMode = .signIn
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.setApplicationState(accountData: accountData, checkNewPhotos: true)
|
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
|
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: accountModel.serverUrl)
|
||||||
|
|
||||||
// Refresh client state.
|
// Refresh client state.
|
||||||
|
@ -128,7 +136,7 @@ struct VernissageApp: App {
|
||||||
// Refresh application state.
|
// Refresh application state.
|
||||||
self.applicationState.changeApplicationState(accountModel: accountModel,
|
self.applicationState.changeApplicationState(accountModel: accountModel,
|
||||||
instance: instance,
|
instance: instance,
|
||||||
lastSeenStatusId: accountData.lastSeenStatusId)
|
lastSeenStatusId: accountModel.lastSeenStatusId)
|
||||||
|
|
||||||
// Change view displayed by application.
|
// Change view displayed by application.
|
||||||
self.applicationViewMode = .mainView
|
self.applicationViewMode = .mainView
|
||||||
|
|
|
@ -323,26 +323,27 @@ struct MainView: View {
|
||||||
Task {
|
Task {
|
||||||
// Verify access token correctness.
|
// Verify access token correctness.
|
||||||
let authorizationSession = AuthorizationSession()
|
let authorizationSession = AuthorizationSession()
|
||||||
await AuthorizationService.shared.verifyAccount(session: authorizationSession, currentAccount: account) { accountData in
|
let accountModel = AccountModel(accountData: account)
|
||||||
guard let accountData = accountData else {
|
|
||||||
|
await AuthorizationService.shared.verifyAccount(session: authorizationSession, accountModel: accountModel) { signedInAccountModel in
|
||||||
|
guard let signedInAccountModel else {
|
||||||
ToastrService.shared.showError(subtitle: "mainview.error.switchAccounts")
|
ToastrService.shared.showError(subtitle: "mainview.error.switchAccounts")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
let accountModel = AccountModel(accountData: accountData)
|
let instance = try? await self.client.instances.instance(url: signedInAccountModel.serverUrl)
|
||||||
let instance = try? await self.client.instances.instance(url: accountModel.serverUrl)
|
|
||||||
|
|
||||||
// Refresh client state.
|
// Refresh client state.
|
||||||
self.client.setAccount(account: accountModel)
|
self.client.setAccount(account: signedInAccountModel)
|
||||||
|
|
||||||
// Refresh application state.
|
// Refresh application state.
|
||||||
self.applicationState.changeApplicationState(accountModel: accountModel,
|
self.applicationState.changeApplicationState(accountModel: signedInAccountModel,
|
||||||
instance: instance,
|
instance: instance,
|
||||||
lastSeenStatusId: accountData.lastSeenStatusId)
|
lastSeenStatusId: signedInAccountModel.lastSeenStatusId)
|
||||||
|
|
||||||
// Set account as default (application will open this account after restart).
|
// Set account as default (application will open this account after restart).
|
||||||
ApplicationSettingsHandler.shared.set(accountData: accountData)
|
ApplicationSettingsHandler.shared.set(accountId: signedInAccountModel.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ struct AccountsSectionView: View {
|
||||||
if shouldClearApplicationState {
|
if shouldClearApplicationState {
|
||||||
// We have to do this after animation of deleting row is ended.
|
// We have to do this after animation of deleting row is ended.
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
ApplicationSettingsHandler.shared.set(accountData: nil)
|
ApplicationSettingsHandler.shared.set(accountId: nil)
|
||||||
self.applicationState.clearApplicationState()
|
self.applicationState.clearApplicationState()
|
||||||
self.client.clearAccount()
|
self.client.clearAccount()
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ struct SignInView: View {
|
||||||
@State private var instructionsUrlString:String?
|
@State private var instructionsUrlString:String?
|
||||||
@State private var instances: [Instance] = []
|
@State private var instances: [Instance] = []
|
||||||
|
|
||||||
var onSignedIn: ((_ accountData: AccountData) -> Void)?
|
var onSignedIn: ((_ accountModel: AccountModel) -> Void)?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
|
@ -91,8 +91,8 @@ struct SignInView: View {
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
let authorizationSession = AuthorizationSession()
|
let authorizationSession = AuthorizationSession()
|
||||||
try await AuthorizationService.shared.sign(in: baseAddress, session: authorizationSession) { accountData in
|
try await AuthorizationService.shared.sign(in: baseAddress, session: authorizationSession) { accountModel in
|
||||||
onSignedIn?(accountData)
|
onSignedIn?(accountModel)
|
||||||
|
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
Loading…
Reference in New Issue