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 or universal link), you don’t need a custom provider, instead configure a WebProvider. That includes providers that hand the flow off to their own native app.
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. -
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
The creator is a thin factory.
The actual Provider calls into the third-party native SDK, then exchanges its result for a ReachFive AuthToken.
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 ( Return |