Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ extension SessionManager: ZMAuthenticationStatusDelegate {
}

public func authenticationWasRequested() {
addAccount()
Task {
await addAccount()
}
}

public func loginCodeRequestDidFail(_ error: Error!) {
Expand All @@ -44,9 +46,11 @@ extension SessionManager: ZMAuthenticationStatusDelegate {
}

public func companyLoginCodeDidBecomeAvailable(_ uuid: UUID!) {
addAccount(userInfo: [
SessionManager.companyLoginCodeKey: uuid ?? UUID(),
SessionManager.companyLoginRequestTimestampKey: Date()
])
Task {
await addAccount(userInfo: [
SessionManager.companyLoginCodeKey: uuid ?? UUID(),
SessionManager.companyLoginRequestTimestampKey: Date()
])
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,18 @@ extension SessionManager: UserSessionEncryptionAtRestDelegate {
let sharedContainerURL = sharedContainerURL

delegate?.sessionManagerWillMigrateAccount(userSessionCanBeTornDown: { [weak self] in
self?.tearDownBackgroundSession(for: account.userIdentifier) {
Task {
await self?.tearDownBackgroundSession(for: account.userIdentifier)
self?.activeUserSession = nil
Task {
do {
try await CoreDataStack.migrateLocalStorage(
accountIdentifier: account.userIdentifier,
applicationContainer: sharedContainerURL,
migration: onReady
)
_ = await self?.loadSession(for: account)
} catch {
WireLogger.ear.error("failed to migrate account: \(error)")
}
do {
try await CoreDataStack.migrateLocalStorage(
accountIdentifier: account.userIdentifier,
applicationContainer: sharedContainerURL,
migration: onReady
)
_ = await self?.loadSession(for: account)
} catch {
WireLogger.ear.error("failed to migrate account: \(error)")
}
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,21 +120,17 @@
return try await withSession(for: account)
}

fileprivate func activateAccount(for session: ZMUserSession, completion: @escaping () -> Void) {
fileprivate func activateAccount(for session: ZMUserSession) async {
if session == activeUserSession {
completion()
return
}

var foundSession = false
backgroundUserSessions.forEach { accountId, backgroundSession in
for (accountId, backgroundSession) in backgroundUserSessions {
if session == backgroundSession, let account = self.accountManager.account(with: accountId) {

Check warning on line 130 in wire-ios-sync-engine/Source/SessionManager/SessionManager+Push.swift

View workflow job for this annotation

GitHub Actions / SwiftFormat

Insert/remove explicit self where applicable. (redundantSelf)

self.select(account, completion: { _ in
completion()
})
_ = await self.select(account)

Check warning on line 131 in wire-ios-sync-engine/Source/SessionManager/SessionManager+Push.swift

View workflow job for this annotation

GitHub Actions / SwiftFormat

Insert/remove explicit self where applicable. (redundantSelf)
foundSession = true
return
break
}
}

Expand All @@ -155,14 +151,16 @@
return
}

activateAccount(for: session) {
self.presentationDelegate?.showConversation(conversation, at: message)
Task {
await activateAccount(for: session)
presentationDelegate?.showConversation(conversation, at: message)
}
}

func showConversationList(in session: ZMUserSession) {
activateAccount(for: session) {
self.presentationDelegate?.showConversationList()
Task {
await activateAccount(for: session)
presentationDelegate?.showConversationList()
}
}

Expand Down
138 changes: 70 additions & 68 deletions wire-ios-sync-engine/Source/SessionManager/SessionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@
object: nil,
queue: nil
) { [weak self] _ in
WireLogger.sessionManager.debug("Received memory warning, tearing down background user sessions.")

Check warning on line 474 in wire-ios-sync-engine/Source/SessionManager/SessionManager.swift

View workflow job for this annotation

GitHub Actions / Test Results

Capture of 'self' with non-Sendable type 'SessionManager?' in a '@Sendable' closure

Capture of 'self' with non-Sendable type 'SessionManager?' in a '@sendable' closure
self?.tearDownAllBackgroundSessions()
}

Expand Down Expand Up @@ -688,70 +688,62 @@
}

/// Select the account to be the active account.
/// - completion: runs when the user session was loaded
/// - tearDownCompletion: runs when the UI no longer holds any references to the previous user session.
public func select(
_ account: Account,
completion: ((ZMUserSession?) -> Void)? = nil,
tearDownCompletion: (() -> Void)? = nil
) {
@MainActor
public func select(_ account: Account) async -> ZMUserSession? {
guard !isSelectingAccount else {
completion?(nil)
return
return nil
}

confirmSwitchingAccount { [weak self] isConfirmed in
let isConfirmed = await confirmSwitchingAccount()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: not really part of the scope here but I'm curious to see what you think. Calling this method will ask a ui delegate to present an alert and request user confirmation. Do you think this kind of back and forth communication makes sense? Or would it be better to have a single direction: ui tries to select an account, if confirmation is needed, throw error, ui handles, gets confirmation, tries again passing in a confirmation flag. Something liKe:

public func select(_ account: Account, authoritative: Bool) async throws -> ZMUserSession? {
  if confirmationNeeded && !authoritative  {
    throw Error.confirmationNeeded
  }

  // continue...
}


guard isConfirmed else {
completion?(nil)
return
}
guard isConfirmed else {
return nil
}

self?.isSelectingAccount = true
let selectedAccount = self?.accountManager.selectedAccount
isSelectingAccount = true
let selectedAccount = accountManager.selectedAccount

guard let delegate = self?.delegate else {
completion?(nil)
return
}
guard let delegate = delegate else {

Check warning on line 706 in wire-ios-sync-engine/Source/SessionManager/SessionManager.swift

View workflow job for this annotation

GitHub Actions / SwiftFormat

Remove redundant identifiers in optional binding conditions. (redundantOptionalBinding)

Check failure on line 706 in wire-ios-sync-engine/Source/SessionManager/SessionManager.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Use shorthand syntax for optional binding (shorthand_optional_binding)
return nil
}

return await withCheckedContinuation { continuation in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: if you follow the path delegate.sessionManagerWillOpenAccount you'll see that it does nothing but immediately invoke the userSessionCanBeTornDown completion handler. I think you can simplify this by removing the delegate method as well as the AppState.loading case.

delegate.sessionManagerWillOpenAccount(
account,
from: selectedAccount,
userSessionCanBeTornDown: { [weak self] in
self?.activeUserSession = nil
tearDownCompletion?()

Task { @MainActor [weak self] in
guard let self else {
completion?(nil)
continuation.resume(returning: nil)
return
}

accountManager.select(account)
let session = await loadSession(for: account)
isSelectingAccount = false

if let session {
completion?(session)
} else {
completion?(nil)
}
continuation.resume(returning: session)
}
}
)
}
}

public func addAccount(userInfo: [String: Any]? = nil, completion: (() -> Void)? = nil) {
confirmSwitchingAccount { [weak self] isConfirmed in
guard isConfirmed else { return }
let error = NSError(userSessionErrorCode: .addAccountRequested, userInfo: userInfo)
self?.delegate?.sessionManagerWillLogout(
@MainActor
public func addAccount(userInfo: [String: Any]? = nil) async {
let isConfirmed = await confirmSwitchingAccount()
guard isConfirmed else { return }

let error = NSError(userSessionErrorCode: .addAccountRequested, userInfo: userInfo)
await withCheckedContinuation { continuation in
delegate?.sessionManagerWillLogout(
environment: nil,
error: error
) { [weak self] in
self?.activeUserSession = nil
completion?()
continuation.resume()
}
}
}
Expand All @@ -778,9 +770,10 @@
WireLogger.sessionManager.debug("Deleting account \(account.userIdentifier)...")
if let secondAccount = accountManager.inactiveAccounts.first {
// Deleted an account but we can switch to another account
select(secondAccount, tearDownCompletion: { [weak self] in
self?.tearDownSessionAndDelete(account: account, eraseData: eraseData)
})
Task {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: why not make delete(account:reason:eraseData) async?

_ = await select(secondAccount)
tearDownSessionAndDelete(account: account, eraseData: eraseData)
}
} else if accountManager.selectedAccount != account {
// Deleted an inactive account, there's no need notify the UI
tearDownSessionAndDelete(account: account, eraseData: eraseData)
Expand All @@ -795,9 +788,10 @@
}

fileprivate func tearDownSessionAndDelete(account: Account, eraseData: Bool) {
tearDownBackgroundSession(for: account.userIdentifier) {
Task {
await tearDownBackgroundSession(for: account.userIdentifier)
if eraseData {
self.deleteAccountData(for: account)
deleteAccountData(for: account)
}
}
}
Expand All @@ -809,7 +803,9 @@
if session == activeUserSession {
logoutCurrentSession(deleteCookie: true, deleteAccount: false, error: error)
} else {
tearDownBackgroundSession(for: account.userIdentifier)
Task {
await tearDownBackgroundSession(for: account.userIdentifier)
}
}
}
}
Expand Down Expand Up @@ -1089,27 +1085,24 @@
}

/// The active user session will be torn down and the app goes into migration state.
public func prepareForRestoreWithMigration(completion: @escaping () -> Void) {
@MainActor
public func prepareForRestoreWithMigration() async {
guard let delegate else {
WireLogger.sessionManager.debug("SessionManager.delegate is nil, aborting migration preparation")
return completion()
return
}

WireLogger.sessionManager.debug("SessionManager.delegate.sessionManagerWillMigrateAccount ...")
delegate.sessionManagerWillMigrateAccount { [self] in
await delegate.sessionManagerWillMigrateAccount()

WireLogger.sessionManager.debug("... userSessionCanBeTornDown { ... }")
WireLogger.sessionManager.debug("... userSessionCanBeTornDown { ... }")

if let accountID = activeUserSession?.account.userIdentifier {
tearDownBackgroundSession(for: accountID) { [self] in
activeUserSession = nil
accountTokens.removeValue(forKey: accountID)
completion()
}
} else {
activeUserSession = nil
completion()
}
if let accountID = activeUserSession?.account.userIdentifier {
await tearDownBackgroundSession(for: accountID)
activeUserSession = nil
accountTokens.removeValue(forKey: accountID)
} else {
activeUserSession = nil
}
}

Expand Down Expand Up @@ -1274,23 +1267,24 @@
notifyNewUserSessionCreated(newSession)
}

func tearDownBackgroundSession(for accountId: UUID, completion: (() -> Void)? = nil) {
@MainActor
func tearDownBackgroundSession(for accountId: UUID) async {
guard let userSession = backgroundUserSessions[accountId] else {
WireLogger.sessionManager
.error("No session to tear down for \(accountId), known sessions: \(backgroundUserSessions)")
completion?()
return
}
tearDownObservers(account: accountId)
backgroundUserSessions[accountId] = nil

dispatchGroup.enter()
userSession.close(deleteCookie: false) { [weak self] in
self?.notifyUserSessionDestroyed(accountId)
completion?()
self?.dispatchGroup.leave()
await withCheckedContinuation { continuation in
userSession.close(deleteCookie: false) { [weak self] in
self?.notifyUserSessionDestroyed(accountId)
continuation.resume()
self?.dispatchGroup.leave()
}
}

}

// Tears down and releases all background user sessions.
Expand All @@ -1299,8 +1293,10 @@
activeUserSession != session
}

backgroundSessions.keys.forEach {
tearDownBackgroundSession(for: $0)
Task {
for key in backgroundSessions.keys {
await tearDownBackgroundSession(for: key)
}
}
}

Expand Down Expand Up @@ -1823,22 +1819,28 @@

public extension SessionManager {

func confirmSwitchingAccount(completion: @escaping (_ isConfirmed: Bool) -> Void) {
@MainActor
func confirmSwitchingAccount() async -> Bool {
guard
let switchingDelegate,
let activeUserSession,
activeUserSession.isCallOngoing
else {
// no confirmation to show if no call is ongoing
return completion(true)
return true
}

switchingDelegate.confirmSwitchingAccount { confirmed in
if confirmed {
activeUserSession.callCenter?.endAllCalls()
let confirmed = await withCheckedContinuation { continuation in
switchingDelegate.confirmSwitchingAccount { confirmed in
continuation.resume(returning: confirmed)
}
completion(confirmed)
}

if confirmed {
activeUserSession.callCenter?.endAllCalls()
}

return confirmed
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,12 @@ private struct ImportBackupAppStateUpdater: ImportBackupAppStateUpdaterProtocol

@MainActor
func reportMigrationNeeded() async {
await withCheckedContinuation { continuation in
sessionManager.prepareForRestoreWithMigration(completion: continuation.resume)
}
await sessionManager.prepareForRestoreWithMigration()
}

@MainActor
func selectAccountAndTriggerSlowSync(_ account: Account) async {
let userSession = await withCheckedContinuation { continuation in
sessionManager.select(account, completion: { continuation.resume(returning: $0) })
}
guard let userSession else { return }
guard let userSession = await sessionManager.select(account) else { return }
do {
try await userSession.syncAgent?.performInitialSync()
} catch {
Expand Down
Loading
Loading