Implement a custom provider
Our built-in provider creators cover the most common use cases for provider creation:
-
GoogleProvider -
FacebookProvider -
AppleProvider -
WeChat -
WebProvider
This custom provider guide instructs you how to implement a provider that none of the built-ins model such as a social/SSO provider with its own native SDK, or a login flow that none of the built-ins model.
If your provider has no native SDK component and only needs to choose how its ASWebAuthenticationSession returns to the app (custom scheme, universal link, out-of-band), you don’t need a custom provider, instead configure a WebProvider.
The two protocols
A provider is made of two pieces, both found in Sources/Core/Classes/Provider.swift:
-
ProviderCreator: a lightweight factory you list inReachFive(providersCreators:). It carries anameand an optionalvariantand builds the actualProvideronce ReachFive has fetched that provider’s configuration from the backend. Itscreatemethod also receives the sharedReachFiveinstance. See Example: wrapping a native SDK for how to hold that reference safely in theProvideryou return. -
Provider: the object doing the actual work:login,logout, and the app-lifecycle hooks it needs to intercept its own callback.public protocol ProviderCreator { var name: String { get } var variant: String? { get } func create(reachFive: ReachFive, providerConfig: ProviderConfig, clientConfigResponse: ClientConfigResponse) -> Provider } public protocol Provider { var name: String { get } func login(scope: [String]?, origin: String, presenting: Presentation) async throws -> AuthToken func logout() async throws // Default (no-op) implementations provided — override only what your provider needs: func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool func applicationDidBecomeActive(_ application: UIApplication) func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool }
name must match the provider’s name as configured on your ReachFive Console.
This is how ReachFive matches your ProviderCreator against the provider configurations it fetches from the backend.
A configured provider with no matching creator still logs in, falling back to either native Sign in with Apple or a generic web-based provider.
|
Register the custom provider
let reachfive = ReachFive(
sdkConfig: SdkConfig(domain: DOMAIN, clientId: CLIENT_ID),
providersCreators: [MyProvider(variant: "ios")]
)
Example: wrapping a native SDK
This is the shape used by our own (AppleProvider.swift):
-
The creator is a thin factory.
-
The actual
Providercalls into the third-party native SDK, then exchanges its result for a ReachFiveAuthTokenthroughreachFive.authWithCode, which is the same helper used internally by native login.
Your create method receives the shared ReachFive instance, which the SDK also stores in ReachFive.providers.
When you build your Provider, decide how to access it from login:
|
When Use For background on why this happens and how |
public class MyProvider: ProviderCreator {
public var name: String = "my-provider"
public var variant: String?
public init(variant: String? = nil) {
self.variant = variant
}
public func create(reachFive: ReachFive, providerConfig: ProviderConfig, clientConfigResponse: ClientConfigResponse) -> Provider {
ConfiguredMyProvider(reachFive: reachFive, providerConfig: providerConfig)
}
}
class ConfiguredMyProvider: NSObject, Provider {
let name: String
private weak var reachFive: ReachFive? // weak: see the retain-cycle note above
init(reachFive: ReachFive, providerConfig: ProviderConfig) {
self.name = providerConfig.provider
self.reachFive = reachFive
}
func login(scope: [String]?, origin: String, presenting: Presentation) async throws -> AuthToken {
// 1. Drive your native SDK's own login UI/flow here, e.g.:
let code = try await MyNativeSDK.shared.login(presenting: presenting.presentingViewController())
// 2. Exchange its authorization code for a ReachFive AuthToken.
guard let reachFive else { throw ReachFiveError.TechnicalError(reason: "ReachFive instance was deallocated") }
return try await reachFive.authWithCode(code: code, pkce: Pkce.generate())
}
func logout() {
MyNativeSDK.shared.logout()
}
}
Example: a fully custom web flow
If your provider has no native SDK but needs behaviour WebProvider doesn’t offer, drive ASWebAuthenticationSession yourself (or reuse reachFive.webviewLogin(_:), see webviewLogin) inside login, then return the resulting AuthToken the same way.
App-lifecycle hooks
Only implement the hooks your provider actually needs to intercept its own callback; every other hook has a default no-op implementation.
| Hook | When to implement it |
|---|---|
Your provider’s callback arrives through a custom URL scheme ( |
|
Your provider’s native SDK needs an explicit startup call. An example of this is registering itself before the app finishes launching. |
|
Your provider needs to react to the app returning to the foreground. An example of this is resuming a paused native SDK session. |
|
Your provider’s callback arrives as a universal link ( An example of this is an out-of-band flow like Return |