11import Combine
22import Foundation
33
4- struct StartupClient {
4+ public enum InitializationStatus : Sendable {
5+ case success
6+ case failure( errors: [ Error ] )
7+ }
8+
9+ enum StartupClient {
510 enum BootstrapRoute : BaseRouteType {
611 case fetch( Path )
712
@@ -19,11 +24,13 @@ struct StartupClient {
1924
2025 static var expectedClientType : ClientType ?
2126
22- static var isInitialized : AnyPublisher < Bool , Never > {
27+ static var isInitialized : AnyPublisher < InitializationStatus , Never > {
2328 isInitializedPublisher. eraseToAnyPublisher ( )
2429 }
2530
26- private static let isInitializedPublisher = PassthroughSubject < Bool , Never > ( )
31+ private static let isInitializedPublisher = PassthroughSubject < InitializationStatus , Never > ( )
32+ private static var bootstrapError : Error ?
33+ private static var sessionHydrationError : Error ?
2734
2835 static func start( ) async throws {
2936 if let expectedClientType {
@@ -40,9 +47,18 @@ struct StartupClient {
4047 async let bootstrap : ( ) = fetchAndApplyBootstrap ( )
4148
4249 // Await both tasks to complete
43- _ = try await ( auth, bootstrap)
44-
45- isInitializedPublisher. send ( true )
50+ do {
51+ _ = try await ( auth, bootstrap)
52+ // We allow the calls to silently fail because they have safe fallbacks, but we want to let the developer know if something went wrong
53+ let potentialErrors = [ bootstrapError, sessionHydrationError] . compactMap { $0 }
54+ if !potentialErrors. isEmpty {
55+ isInitializedPublisher. send ( . failure( errors: potentialErrors) )
56+ } else {
57+ isInitializedPublisher. send ( . success)
58+ }
59+ } catch {
60+ isInitializedPublisher. send ( . failure( errors: [ error] ) )
61+ }
4662
4763 StytchConsoleLogger . log ( message: " Stytch SDK initialized for client type: \( clientType) " )
4864 }
@@ -53,9 +69,19 @@ struct StartupClient {
5369 }
5470 switch clientType {
5571 case . consumer:
56- _ = try ? await StytchClient . sessions. authenticate ( parameters: . init( sessionDurationMinutes: nil ) )
72+ do {
73+ _ = try await StytchClient . sessions. authenticate ( parameters: . init( sessionDurationMinutes: nil ) )
74+ sessionHydrationError = nil
75+ } catch {
76+ sessionHydrationError = error
77+ }
5778 case . b2b:
58- _ = try ? await StytchB2BClient . sessions. authenticate ( parameters: . init( sessionDurationMinutes: nil ) )
79+ do {
80+ _ = try await StytchB2BClient . sessions. authenticate ( parameters: . init( sessionDurationMinutes: nil ) )
81+ sessionHydrationError = nil
82+ } catch {
83+ sessionHydrationError = error
84+ }
5985 }
6086 }
6187
@@ -83,26 +109,26 @@ struct StartupClient {
83109 }
84110
85111 @discardableResult static func bootstrap( ) async throws -> BootstrapResponseData {
86- guard let publicToken = StytchClient . stytchClientConfiguration? . publicToken else {
87- throw StytchSDKError . consumerSDKNotConfigured
88- }
89-
90112 // Attempt to fetch the latest bootstrap data from the API using the provided public token.
91113 // If the network request succeeds, extract and use the wrapped response data.
92114 // If the network request fails, fall back to the locally stored bootstrap data.
93115 // If no local data exists, use a predefined default bootstrap data.
94- let bootstrapResponseData : BootstrapResponseData
95- if let bootstrapData = try ? await router. get ( route: . fetch( Path ( rawValue: publicToken) ) ) as BootstrapResponse {
96- bootstrapResponseData = bootstrapData. wrapped
97- } else if let currentBootstrapData = Current . localStorage. bootstrapData {
98- bootstrapResponseData = currentBootstrapData
99- } else {
100- bootstrapResponseData = BootstrapResponseData . defaultBootstrapData
116+ do {
117+ guard let publicToken = StytchClient . stytchClientConfiguration? . publicToken else {
118+ throw StytchSDKError . consumerSDKNotConfigured
119+ }
120+ let updatedBootstrapData = try await router. get ( route: . fetch( Path ( rawValue: publicToken) ) ) as BootstrapResponse
121+ bootstrapError = nil
122+ Current . localStorage. bootstrapData = updatedBootstrapData. wrapped
123+ return updatedBootstrapData. wrapped
124+ } catch {
125+ bootstrapError = error
126+ if let currentBootstrapData = Current . localStorage. bootstrapData {
127+ return currentBootstrapData
128+ } else {
129+ Current . localStorage. bootstrapData = BootstrapResponseData . defaultBootstrapData
130+ return BootstrapResponseData . defaultBootstrapData
131+ }
101132 }
102-
103- // Update the local storage with the resolved bootstrap data before returning it.
104- Current. localStorage. bootstrapData = bootstrapResponseData
105-
106- return bootstrapResponseData
107133 }
108134}
0 commit comments