diff --git a/src/docs/DescopeKit.docc/Documentation.md b/src/docs/DescopeKit.docc/Documentation.md index 23be10a..a1d4f87 100644 --- a/src/docs/DescopeKit.docc/Documentation.md +++ b/src/docs/DescopeKit.docc/Documentation.md @@ -42,6 +42,7 @@ on the [Descope website](https://descope.com). - ``DescopeFlow`` - ``DescopeFlowState`` +- ``DescopeFlowHook`` - ``DescopeFlowView`` - ``DescopeFlowViewDelegate`` - ``DescopeFlowViewController`` @@ -64,6 +65,7 @@ on the [Descope website](https://descope.com). - ``UpdateOptions`` - ``DeliveryMethod`` - ``OAuthProvider`` +- ``RevokeType`` ### Development diff --git a/src/flows/FlowHook.swift b/src/flows/FlowHook.swift index a76e5bc..81d1b2f 100644 --- a/src/flows/FlowHook.swift +++ b/src/flows/FlowHook.swift @@ -42,7 +42,7 @@ import WebKit /// ``` /// /// You can also implement your own hooks by subclassing ``DescopeFlowHook`` and -/// overriding the ``execute(coordinator:)`` method. +/// overriding the ``execute(event:coordinator:)`` method. @MainActor open class DescopeFlowHook { @@ -222,7 +222,7 @@ extension DescopeFlowHook { /// } /// ``` /// - /// - Parameter setup: A closure that receives the `UIScrollView` instance as its only parameter. + /// - Parameter closure: A closure that receives the `UIScrollView` instance as its only parameter. /// /// - Returns: A ``DescopeFlowHook`` object that can be added to the ``DescopeFlow/hooks`` array. public static func setupScrollView(_ closure: @escaping (UIScrollView) -> Void) -> DescopeFlowHook { @@ -233,7 +233,7 @@ extension DescopeFlowHook { /// Creates a hook that will run the provided closure when the flow is started /// on the `WKWebView` used to display it. /// - /// - Parameter setup: A closure that receives the `WKWebView` instance as its only parameter. + /// - Parameter closure: A closure that receives the `WKWebView` instance as its only parameter. /// /// - Returns: A ``DescopeFlowHook`` object that can be added to the ``DescopeFlow/hooks`` array. public static func setupWebView(_ closure: @escaping (WKWebView) -> Void) -> DescopeFlowHook { diff --git a/src/internal/others/FlowsV0/FlowsV0Runner.swift b/src/internal/others/FlowsV0/FlowsV0Runner.swift index 41a6b26..ea7d476 100644 --- a/src/internal/others/FlowsV0/FlowsV0Runner.swift +++ b/src/internal/others/FlowsV0/FlowsV0Runner.swift @@ -97,10 +97,6 @@ public class DescopeFlowRunner { /// The host application is expected to intercept this URL via Universal Links and /// resume the running flow with it. /// - /// You can do this by first getting a reference to the current running flow from - /// the ``DescopeFlow/current`` property and then calling the ``resume(with:)`` method - /// with the URL from the Universal Link. - /// /// @main /// struct MyApp: App { /// // ... diff --git a/src/sdk/Callbacks.swift b/src/sdk/Callbacks.swift index 78da13f..87526ad 100644 --- a/src/sdk/Callbacks.swift +++ b/src/sdk/Callbacks.swift @@ -51,6 +51,7 @@ public extension DescopeAuth { /// hasn't already been selected. /// - tenantIds: Provide a non-empty array of tenant IDs and set `dct` to `false` /// to request a specific list of tenants for the user. + /// - refreshJwt: the `refreshJwt` from an active ``DescopeSession``. /// /// - Returns: A list of one or more ``DescopeTenant`` values. func tenants(dct: Bool, tenantIds: [String], refreshJwt: String, completion: @escaping @Sendable (Result<[DescopeTenant], Error>) -> Void) { diff --git a/src/sdk/Routes.swift b/src/sdk/Routes.swift index 9fafd17..ddffac5 100644 --- a/src/sdk/Routes.swift +++ b/src/sdk/Routes.swift @@ -18,6 +18,7 @@ public protocol DescopeAuth: Sendable { /// hasn't already been selected. /// - tenantIds: Provide a non-empty array of tenant IDs and set `dct` to `false` /// to request a specific list of tenants for the user. + /// - refreshJwt: the `refreshJwt` from an active ``DescopeSession``. /// /// - Returns: A list of one or more ``DescopeTenant`` values. func tenants(dct: Bool, tenantIds: [String], refreshJwt: String) async throws -> [DescopeTenant] diff --git a/src/session/Lifecycle.swift b/src/session/Lifecycle.swift index 9810fc5..4c38a4d 100644 --- a/src/session/Lifecycle.swift +++ b/src/session/Lifecycle.swift @@ -5,11 +5,11 @@ import Foundation /// manages its ``DescopeSession`` while the application is running. @MainActor public protocol DescopeSessionLifecycle: AnyObject { - /// Set by the session manager whenever the current active session changes. + /// Holds the latest session value for the session manager. var session: DescopeSession? { get set } - /// Called the session manager to conditionally refresh the active session. - func refreshSessionIfNeeded() async throws + /// Called by the session manager to conditionally refresh the active session. + func refreshSessionIfNeeded() async throws -> Bool } /// The default implementation of the ``DescopeSessionLifecycle`` protocol. @@ -43,10 +43,11 @@ public class SessionLifecycle: DescopeSessionLifecycle { } } - public func refreshSessionIfNeeded() async throws { - guard let current = session, shouldRefresh(current) else { return } + public func refreshSessionIfNeeded() async throws -> Bool { + guard let current = session, shouldRefresh(current) else { return false } let response = try await auth.refreshSession(refreshJwt: current.refreshJwt) session?.updateTokens(with: response) + return true } // Conditional refresh @@ -60,7 +61,7 @@ public class SessionLifecycle: DescopeSessionLifecycle { private var timer: Timer? private func resetTimer() { - if session != nil && stalenessCheckFrequency > 0 { + if stalenessCheckFrequency > 0, let refreshToken = session?.refreshToken, !refreshToken.isExpired { startTimer() } else { stopTimer() @@ -84,9 +85,11 @@ public class SessionLifecycle: DescopeSessionLifecycle { private func periodicRefresh() async { do { - try await refreshSessionIfNeeded() + _ = try await refreshSessionIfNeeded() + } catch DescopeError.networkError { + // allow retries on network errors } catch { - // TODO check for refresh failure to not try again and again after expiry + stopTimer() } } } diff --git a/src/session/Manager.swift b/src/session/Manager.swift index 02fdd3a..e8ab45b 100644 --- a/src/session/Manager.swift +++ b/src/session/Manager.swift @@ -131,9 +131,9 @@ public class DescopeSessionManager { /// - Note: When using a custom ``DescopeSessionManager`` object the exact behavior /// here depends on the `storage` and `lifecycle` objects. public func refreshSessionIfNeeded() async throws { - try await lifecycle.refreshSessionIfNeeded() - if let session { - storage.saveSession(session) + let refreshed = try await lifecycle.refreshSessionIfNeeded() + if refreshed { + saveSession() } } @@ -152,9 +152,7 @@ public class DescopeSessionManager { /// ``DescopeSessionStorage`` protocol. public func updateTokens(with refreshResponse: RefreshResponse) { lifecycle.session?.updateTokens(with: refreshResponse) - if let session { - storage.saveSession(session) - } + saveSession() } /// Updates the active session's user details. @@ -170,8 +168,14 @@ public class DescopeSessionManager { /// but this can be overridden with a custom ``DescopeSessionStorage`` object. public func updateUser(with user: DescopeUser) { lifecycle.session?.updateUser(with: user) - if let session { - storage.saveSession(session) - } + saveSession() + } + + // Internal + + /// Saves the latest session value to the storage. + private func saveSession() { + guard let session else { return } + storage.saveSession(session) } }