Skip to content

Commit

Permalink
Fix app auto-reconnect on background
Browse files Browse the repository at this point in the history
Also moves the handling logic from WSClient to ChatClient.

Basically, when we disconnected, we automatically set a timer for reconnection, and reconnected while in background.
  • Loading branch information
b-onc committed Jun 9, 2021
1 parent 2634a62 commit 45cc880
Show file tree
Hide file tree
Showing 9 changed files with 370 additions and 219 deletions.
4 changes: 2 additions & 2 deletions Sources/StreamChat/APIClient/APIClient_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,8 @@ extension URLSessionConfiguration {
return commonEquatability
&& multipathServiceType == otherConfiguration.multipathServiceType
&& sessionSendsLaunchEvents == otherConfiguration.sessionSendsLaunchEvents
#endif

#else
return commonEquatability
#endif
}
}
62 changes: 62 additions & 0 deletions Sources/StreamChat/ChatClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ public class _ChatClient<ExtraData: ExtraDataTypes> {

private(set) lazy var internetConnection = environment.internetConnection()
private(set) lazy var clientUpdater = environment.clientUpdaterBuilder(self)
/// Used for starting and ending background tasks. Hides platform specific logic.
private lazy var backgroundTaskScheduler = environment.backgroundTaskSchedulerBuilder()

/// The environment object containing all dependencies of this `Client` instance.
private let environment: Environment
Expand Down Expand Up @@ -299,6 +301,11 @@ public class _ChatClient<ExtraData: ExtraDataTypes> {
currentUserId = fetchCurrentUserIdFromDatabase()

clientUpdater.reloadUserIfNeeded(completion: completion)

backgroundTaskScheduler?.startListeningForAppStateUpdates(
onEnteringBackground: { [weak self] in self?.handleAppDidEnterBackground() },
onEnteringForeground: { [weak self] in self?.handleAppDidBecomeActive() }
)
}

deinit {
Expand Down Expand Up @@ -342,6 +349,47 @@ public class _ChatClient<ExtraData: ExtraDataTypes> {
waiters.removeAll()
}
}

private func handleAppDidEnterBackground() {
// If user wants to manage connection manaully, we don't handle
guard config.shouldConnectAutomatically else { return }
// We can't disconnect if we're not connected
guard connectionStatus == .connected else { return }

guard config.staysConnectedInBackground else {
// We immediately disconnect
clientUpdater.disconnect(source: .systemInitiated)
return
}
guard let scheduler = backgroundTaskScheduler else { return }

let succeed = scheduler.beginTask { [weak self] in
self?.clientUpdater.disconnect(source: .systemInitiated)
// We need to call `endBackgroundTask` else our app will be killed
self?.cancelBackgroundTaskIfNeeded()
}

if !succeed {
// Can't initiate a background task, close the connection
clientUpdater.disconnect(source: .systemInitiated)
}
}

private func handleAppDidBecomeActive() {
cancelBackgroundTaskIfNeeded()

// If user wants to manage connection manaully, we don't handle reconnection
guard config.shouldConnectAutomatically else { return }
guard connectionStatus != .connected && connectionStatus != .connecting else {
// We are connected or connecting anyway
return
}
clientUpdater.connect()
}

private func cancelBackgroundTaskIfNeeded() {
backgroundTaskScheduler?.endTask()
}
}

extension _ChatClient {
Expand Down Expand Up @@ -396,6 +444,20 @@ extension _ChatClient {
var internetConnection: () -> InternetConnection = { InternetConnection() }

var clientUpdaterBuilder = ChatClientUpdater<ExtraData>.init

var backgroundTaskSchedulerBuilder: () -> BackgroundTaskScheduler? = {
if Bundle.main.isAppExtension {
// No background task scheduler exists for app extensions.
return nil
} else {
#if os(iOS)
return IOSBackgroundTaskScheduler()
#else
// No need for background schedulers on macOS, app continues running when inactive.
return nil
#endif
}
}
}
}

Expand Down
Loading

0 comments on commit 45cc880

Please sign in to comment.