Overview
OwnID Boost is designed to enhance your existing native login and registration screens by adding OwnID as an add-on. It places OwnID side-by-side with the traditional password field, giving users the option to choose a passwordless experience without altering your current flows.Prerequisites
- Complete the iOS OwnID SDK configuration guide
Implementation
Login
The OwnID SDK offers two options to present the passwordless login button:- Side-by-side to password field (out-of-the box)
- Your custom button (you control the UI)
Implementing side-by-side button
Implementing side-by-side button
1
Create Login Handler
Copy
Ask AI
final class OwnIDLogin: LoginPerformer {
func login(
payload: OwnIDCoreSDK.OwnID.CoreSDK.Payload,
loginId: String
) -> OwnIDCoreSDK.OwnID.LoginResultPublisher {
// Add your login logic here
return // Your publisher
}
}
2
Set Up Login View Model
Copy
Ask AI
final class LoginViewModel: ObservableObject {
@Published var email = ""
private var bag = Set<AnyCancellable>()
var ownIDViewModel: OwnID.FlowsSDK.LoginView.ViewModel!
init() {
ownIDViewModel = OwnID.FlowsSDK.LoginView.ViewModel(
loginPerformer: OwnIDLogin(),
loginIdPublisher: $email.eraseToAnyPublisher()
)
subscribeToEvents()
}
private func subscribeToEvents() {
ownIDViewModel.integrationEventPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] event in
switch event {
case .success(let event):
switch event {
case .loggedIn:
// Handle success
case .loading:
// Show loading
}
case .failure(let error):
// Handle error
}
}
.store(in: &bag)
}
}
3
Create Login View
Side-by-Side button
Copy
Ask AI
struct LoginView: View {
@StateObject private var viewModel = LoginViewModel()
var body: some View {
VStack(spacing: 16) {
TextField("Email", text: $viewModel.email)
.textContentType(.emailAddress)
.autocapitalization(.none)
OwnID.FlowsSDK.LoginView(viewModel: viewModel.ownIDViewModel)
}
.padding()
}
}
Implementing your custom button
Implementing your custom button
1
Create Login Handler
Copy
Ask AI
final class OwnIDLogin: LoginPerformer {
func login(
payload: OwnIDCoreSDK.OwnID.CoreSDK.Payload,
loginId: String
) -> OwnIDCoreSDK.OwnID.LoginResultPublisher {
// Add your login logic here
return // Your publisher
}
}
2
Set Up Login View Model
- Create an instance of the OwnID view model
- Define a
PassthroughSubjectto handle login actions - Subscribe the
ownIDViewModelto this publisher - Call
send()on the subject from your button’s action to initiate the login process
Copy
Ask AI
final class LoginViewModel: ObservableObject {
@Published var email = ""
private var bag = Set<AnyCancellable>()
var ownIDViewModel: OwnID.FlowsSDK.LoginView.ViewModel!
private let resultPublisher = PassthroughSubject<Void, Never>()
init() {
ownIDViewModel = OwnID.FlowsSDK.LoginView.ViewModel(
loginPerformer: OwnIDLogin(),
loginIdPublisher: $email.eraseToAnyPublisher()
)
ownIDViewModel.subscribe(to: resultPublisher.eraseToAnyPublisher())
subscribeToEvents()
}
func handleCustomButtonAction() {
resultPublisher.send()
}
private func subscribeToEvents() {
ownIDViewModel.integrationEventPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] event in
switch event {
case .success(let event):
switch event {
case .loggedIn:
// Handle success
case .loading:
// Show loading
}
case .failure(let error):
// Handle error
}
}
.store(in: &bag)
}
}
3
Create Your Custom Button
Example (do your own - no OwnID code)
Copy
Ask AI
var body: some View {
CustomButton() {
viewModel.handleCustomButtonAction()
}
}
Registration
In the registration flow, the only supported button is side-by-side
1
Create Registration Handler
Copy
Ask AI
final class OwnIDRegistration: RegistrationPerformer {
func register(
configuration: OwnIDCoreSDK.OwnID.FlowsSDK.RegistrationConfiguration,
parameters: any OwnIDCoreSDK.RegisterParameters
) -> OwnIDCoreSDK.OwnID.RegistrationResultPublisher {
let ownIdData = configuration.payload.data
// Add your registration logic here
return // Your publisher
}
}
2
Set Up View Model
Copy
Ask AI
final class RegistrationViewModel: ObservableObject {
@Published var email = ""
private var bag = Set<AnyCancellable>()
var ownIDViewModel: OwnID.FlowsSDK.RegisterView.ViewModel!
init() {
ownIDViewModel = OwnID.FlowsSDK.RegisterView.ViewModel(
registrationPerformer: OwnIDRegistration(),
loginPerformer: OwnIDLogin(),
loginIdPublisher: $email.eraseToAnyPublisher()
)
subscribeToEvents()
}
private func subscribeToEvents() {
ownIDViewModel.integrationEventPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] event in
switch event {
case .success(let event):
switch event {
case .readyToRegister:
self?.ownIDViewModel.register(registerParameters: params)
case .userRegisteredAndLoggedIn:
break
// Handle success
case .loading:
break
// Show loading
case .resetTapped:
break
// Handle reset
}
case .failure(let error):
break
// Handle error
}
}
.store(in: &bag)
}
}
3
Create Registration View
Copy
Ask AI
struct RegistrationView: View {
@StateObject private var viewModel = RegistrationViewModel()
var body: some View {
VStack(spacing: 16) {
TextField("Email", text: $viewModel.email)
.textContentType(.emailAddress)
.autocapitalization(.none)
OwnID.FlowsSDK.RegisterView(viewModel: viewModel.ownIDViewModel)
}
.padding()
}
}
Returning Users Experience
In some cases, you might want to start the login flow for returning users programmatically. For example, when a returning user opens the app or opens the login page. For that, you can use theauth() function provided by the LoginViewModel:
Example
Copy
Ask AI
ownIDViewModel.auth(onlyReturningUser: true)
Customization
Button Styling
Side-by-Side Button
Copy
Ask AI
let orConfig = OwnID.UISDK.OrViewConfig(
textSize: 20,
textColor: .red
)
let loaderConfig = OwnID.UISDK.LoaderViewConfig(
spinnerColor: .red,
circleColor: .green
)
let buttonConfig = OwnID.UISDK.IconButtonViewConfig(
widgetPosition: .trailing,
height: 40,
iconColor: .red,
borderColor: .red,
backgroundColor: .white,
orViewConfig: orConfig,
loaderViewConfig: loaderConfig
)
let config = OwnID.UISDK.VisualLookConfig(iconButtonConfig: buttonConfig)
OwnID.FlowsSDK.LoginView(
viewModel: viewModel.ownIDViewModel,
visualConfig: config
)
Error Handling
The SDK usesOwnID.CoreSDK.Error for all errors:
Copy
Ask AI
switch error {
case .flowCancelled(let flow):
// User cancelled the OwnID flow
// Usually safe to ignore
case .userError(let errorModel):
// Show errorModel.userMessage to the user
// This message is already localized
case .integrationError(underlying: Swift.Error):
// Handle integration error
// Log for debugging
}
Advanced Configuration
Copy
Ask AI
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>OwnIDAppID</key>
<string>your-app-id</string>
<key>OwnIDEnv</key>
<string>uat</string>
<key>OwnIDRegion</key>
<string>us</string>
<key>EnableLogging</key>
<true/>
</dict>
</plist>
Copy
Ask AI
<key>OwnIDRegion</key>
<string>eu</string>
It is strongly recommended to disable logging in production builds.
Copy
Ask AI
OwnID.CoreSDK.logger.isEnabled = true
Language
By default, the SDK uses the device’s locale settings. You can override this behavior to specify supported languages.Copy
Ask AI
OwnID.CoreSDK.configure(
userFacingSDK: info,
supportedLanguages: ["en","fr"]
)
UIKit Examples
Copy
Ask AI
final class RegisterViewController: UIViewController {
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var ownIDContainerView: UIView!
private var ownIDViewModel: OwnID.FlowsSDK.RegisterView.ViewModel!
private var bag = Set<AnyCancellable>()
private lazy var ownIDButton: UIHostingController<OwnID.FlowsSDK.RegisterView> = {
let ownIDView = OwnID.FlowsSDK.RegisterView(viewModel: ownIDViewModel)
let ownIDVC = UIHostingController(rootView: ownIDView)
ownIDVC.view.translatesAutoresizingMaskIntoConstraints = false
return ownIDVC
}()
override func viewDidLoad() {
super.viewDidLoad()
let emailPublisher = NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: emailTextField)
.map({ ($0.object as? UITextField)?.text ?? "" })
ownIDViewModel = OwnID.FlowsSDK.RegisterView.ViewModel(registrationPerformer: OwnIDRegistration(),
loginPerformer: OwnIDLogin(),
loginIdPublisher: emailPublisher.eraseToAnyPublisher())
subscribe(to: ownIDViewModel.integrationEventPublisher)
addChild(ownIDButton)
ownIDContainerView.addSubview(ownIDButton.view)
ownIDButton.didMove(toParent: self)
}
private func subscribe(to eventsPublisher: OwnID.RegistrationPublisher) {
eventsPublisher
.receive(on: DispatchQueue.main)
.sink { [unowned self] event in
switch event {
case .success(let event):
switch event {
// Event when user successfully finishes OwnID registration flow
case .readyToRegister:
// Call register(:) function and
// pass the custom user's parameters if needed
// Event when OwnID creates
// account in your system
// and logs in user
case .userRegisteredAndLoggedIn:
// User is registered and logged in with OwnID
case .loading:
// Display loading indicator according to your designs
case .resetTapped:
// Event when user select "Undo" option in ready-to-register state
}
case .failure(let error):
// Handle OwnID.CoreSDK.Error here
}
}
.store(in: &bag)
}
}