Get Started
API Documentation
- Web SDK
- Android
- iOS
Hawcx iOS SDK
Next-generation passwordless authentication for your iOS applications
Overview
Hawcx SDK provides next-generation passwordless authentication for your iOS applications. With Hawcx, you can implement secure, frictionless authentication that works seamlessly across all user devices.
Passwordless Authentication
Eliminate password vulnerabilities and user friction
Multi-Device Support
Enable users to securely access their accounts across all their devices
Enterprise-Grade Security
Protect user accounts with advanced security protocols
Web Login Approval
Allow users to approve web logins from their mobile device
Biometric Integration
Leverage Face ID and Touch ID for additional security
Architecture
Quick Start
Installation
dependencies: [
.package(url: "https://github.com/hawcx/hawcx_ios_sdk", .upToNextMajor(from: "3.7.0"))
]
Or add it directly in Xcode:
- Select File > Add Packages…
- Enter the package URL:
https://github.com/hawcx/hawcx_ios_sdk
- Select “Up to Next Major Version” with version “3.7.0”
- Click “Add Package”
dependencies: [
.package(url: "https://github.com/hawcx/hawcx_ios_sdk", .upToNextMajor(from: "3.7.0"))
]
Or add it directly in Xcode:
- Select File > Add Packages…
- Enter the package URL:
https://github.com/hawcx/hawcx_ios_sdk
- Select “Up to Next Major Version” with version “3.7.0”
- Click “Add Package”
- Download the latest [HawcxSDK.xcframework] (https://github.com/hawcx/hawcx_ios_sdk/releases/latest)
- Drag and drop the framework into your Xcode project
- In project settings, under “General” > “Frameworks, Libraries, and Embedded Content”, set to “Embed & Sign”
Initialize SDK
import HawcxSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize Hawcx SDK with your API key
HawcxInitializer.shared.initialize(apiKey: "YOUR_API_KEY")
return true
}
}
Implement authentication using methods below:
Core Features
class AuthManager: SignUpCallback {
private let signUp = SignUp(apiKey: "YOUR_API_KEY")
func registerUser(email: String) {
signUp.signUp(userid: email, callback: self)
}
func verifyOTP(otp: String) {
signUp.handleVerifyOTP(otp: otp, callback: self)
}
// MARK: - SignUpCallback Methods
func showError(signUpErrorCode: SignUpErrorCode, errorMessage: String) {
// Handle error based on signUpErrorCode
print("Error: \(errorMessage)")
}
func onSuccessfulSignUp() {
// Handle successful registration
print("Registration successful!")
}
func onGenerateOTPSuccess() {
// Show OTP entry UI
print("OTP sent to user's email")
}
}
class AuthManager: SignUpCallback {
private let signUp = SignUp(apiKey: "YOUR_API_KEY")
func registerUser(email: String) {
signUp.signUp(userid: email, callback: self)
}
func verifyOTP(otp: String) {
signUp.handleVerifyOTP(otp: otp, callback: self)
}
// MARK: - SignUpCallback Methods
func showError(signUpErrorCode: SignUpErrorCode, errorMessage: String) {
// Handle error based on signUpErrorCode
print("Error: \(errorMessage)")
}
func onSuccessfulSignUp() {
// Handle successful registration
print("Registration successful!")
}
func onGenerateOTPSuccess() {
// Show OTP entry UI
print("OTP sent to user's email")
}
}
import SwiftUI
import HawcxSDK
struct SignUpView: View {
@StateObject private var viewModel = SignUpViewModel()
@State private var email = ""
@State private var otp = ""
@State private var showOtpField = false
var body: some View {
VStack(spacing: 20) {
Text("Create Account")
.font(.largeTitle)
.fontWeight(.bold)
if !showOtpField {
// Email entry
TextField("Email Address", text: $email)
.keyboardType(.emailAddress)
.autocapitalization(.none)
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
Button("Sign Up") {
viewModel.registerUser(email: email)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
} else {
// OTP Verification
Text("Enter the verification code sent to \(email)")
.multilineTextAlignment(.center)
TextField("6-Digit Code", text: $otp)
.keyboardType(.numberPad)
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
Button("Verify") {
viewModel.verifyOTP(otp: otp)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
.padding()
.alert(isPresented: $viewModel.showAlert) {
Alert(title: Text(viewModel.alertTitle),
message: Text(viewModel.alertMessage),
dismissButton: .default(Text("OK")))
}
.onReceive(viewModel.$otpGenerated) { generated in
showOtpField = generated
}
}
}
class SignUpViewModel: ObservableObject, SignUpCallback {
private let signUp = SignUp(apiKey: "YOUR_API_KEY")
@Published var otpGenerated = false
@Published var showAlert = false
@Published var alertTitle = ""
@Published var alertMessage = ""
func registerUser(email: String) {
signUp.signUp(userid: email, callback: self)
}
func verifyOTP(otp: String) {
signUp.handleVerifyOTP(otp: otp, callback: self)
}
// MARK: - SignUpCallback
func showError(signUpErrorCode: SignUpErrorCode, errorMessage: String) {
DispatchQueue.main.async {
self.alertTitle = "Error"
self.alertMessage = errorMessage
self.showAlert = true
}
}
func onSuccessfulSignUp() {
DispatchQueue.main.async {
self.alertTitle = "Success"
self.alertMessage = "Account created successfully!"
self.showAlert = true
}
}
func onGenerateOTPSuccess() {
DispatchQueue.main.async {
self.otpGenerated = true
}
}
}
class AuthManager: SignInCallback {
private let signIn = SignIn(apiKey: "YOUR_API_KEY")
func authenticateUser(email: String) {
signIn.signIn(userid: email, callback: self)
}
// MARK: - SignInCallback Methods
func showError(signInErrorCode: SignInErrorCode, errorMessage: String) {
// Handle authentication error
print("Error: \(errorMessage)")
}
func onSuccessfulLogin(_ email: String) {
// User successfully authenticated
print("Login successful for \(email)")
}
func navigateToRegistration(for email: String) {
// User doesn't exist, show registration UI
print("User not found, redirecting to registration")
}
func initiateAddDeviceRegistrationFlow(for email: String) {
// Current device needs to be added to account
print("New device detected, initiate device registration")
}
}
class AuthManager: SignInCallback {
private let signIn = SignIn(apiKey: "YOUR_API_KEY")
func authenticateUser(email: String) {
signIn.signIn(userid: email, callback: self)
}
// MARK: - SignInCallback Methods
func showError(signInErrorCode: SignInErrorCode, errorMessage: String) {
// Handle authentication error
print("Error: \(errorMessage)")
}
func onSuccessfulLogin(_ email: String) {
// User successfully authenticated
print("Login successful for \(email)")
}
func navigateToRegistration(for email: String) {
// User doesn't exist, show registration UI
print("User not found, redirecting to registration")
}
func initiateAddDeviceRegistrationFlow(for email: String) {
// Current device needs to be added to account
print("New device detected, initiate device registration")
}
}
import SwiftUI
import HawcxSDK
struct SignInView: View {
@StateObject private var viewModel = SignInViewModel()
@State private var email = ""
@State private var navigateToSignUp = false
@State private var navigateToAddDevice = false
@State private var navigateToHome = false
var body: some View {
NavigationView {
VStack(spacing: 20) {
Text("Log In")
.font(.largeTitle)
.fontWeight(.bold)
TextField("Email Address", text: $email)
.keyboardType(.emailAddress)
.autocapitalization(.none)
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
Button("Sign In") {
viewModel.authenticateUser(email: email)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
NavigationLink(destination: SignUpView(), isActive: $navigateToSignUp) { EmptyView() }
NavigationLink(destination: AddDeviceView(), isActive: $navigateToAddDevice) { EmptyView() }
NavigationLink(destination: HomeView(), isActive: $navigateToHome) { EmptyView() }
}
.padding()
.alert(isPresented: $viewModel.showAlert) {
Alert(title: Text(viewModel.alertTitle),
message: Text(viewModel.alertMessage),
dismissButton: .default(Text("OK")))
}
.onReceive(viewModel.$needsSignUp) { needsSignUp in
if needsSignUp {
navigateToSignUp = true
viewModel.needsSignUp = false
}
}
.onReceive(viewModel.$needsAddDevice) { needsAddDevice in
if needsAddDevice {
navigateToAddDevice = true
viewModel.needsAddDevice = false
}
}
.onReceive(viewModel.$isLoggedIn) { isLoggedIn in
if isLoggedIn {
navigateToHome = true
viewModel.isLoggedIn = false
}
}
}
}
}
class SignInViewModel: ObservableObject, SignInCallback {
private let signIn = SignIn(apiKey: "YOUR_API_KEY")
@Published var needsSignUp = false
@Published var needsAddDevice = false
@Published var isLoggedIn = false
@Published var showAlert = false
@Published var alertTitle = ""
@Published var alertMessage = ""
@Published var userEmail = ""
func authenticateUser(email: String) {
self.userEmail = email
signIn.signIn(userid: email, callback: self)
}
// MARK: - SignInCallback
func showError(signInErrorCode: SignInErrorCode, errorMessage: String) {
DispatchQueue.main.async {
self.alertTitle = "Error"
self.alertMessage = errorMessage
self.showAlert = true
}
}
func onSuccessfulLogin(_ email: String) {
DispatchQueue.main.async {
self.isLoggedIn = true
}
}
func navigateToRegistration(for email: String) {
DispatchQueue.main.async {
self.needsSignUp = true
}
}
func initiateAddDeviceRegistrationFlow(for email: String) {
DispatchQueue.main.async {
self.needsAddDevice = true
}
}
}
class DeviceManager: AddDeviceCallback {
private let addDeviceManager = AddDeviceManager(apiKey: "YOUR_API_KEY")
func addDevice(email: String) {
addDeviceManager.startAddDeviceFlow(userid: email, callback: self)
}
func verifyOTP(otp: String) {
addDeviceManager.handleVerifyOTP(otp: otp)
}
// MARK: - AddDeviceCallback Methods
func showError(addDeviceErrorCode: AddDeviceErrorCode, errorMessage: String) {
// Handle error
print("Error: \(errorMessage)")
}
func onAddDeviceSuccess() {
// Device successfully added
print("Device successfully added to account!")
}
func onGenerateOTPSuccess() {
// Show OTP entry UI
print("OTP sent to user's email")
}
}
class DeviceManager: AddDeviceCallback {
private let addDeviceManager = AddDeviceManager(apiKey: "YOUR_API_KEY")
func addDevice(email: String) {
addDeviceManager.startAddDeviceFlow(userid: email, callback: self)
}
func verifyOTP(otp: String) {
addDeviceManager.handleVerifyOTP(otp: otp)
}
// MARK: - AddDeviceCallback Methods
func showError(addDeviceErrorCode: AddDeviceErrorCode, errorMessage: String) {
// Handle error
print("Error: \(errorMessage)")
}
func onAddDeviceSuccess() {
// Device successfully added
print("Device successfully added to account!")
}
func onGenerateOTPSuccess() {
// Show OTP entry UI
print("OTP sent to user's email")
}
}
import SwiftUI
import HawcxSDK
struct AddDeviceView: View {
@StateObject private var viewModel = AddDeviceViewModel()
@State private var email = ""
@State private var otp = ""
@State private var showOtpField = false
@State private var navigateToHome = false
var body: some View {
VStack(spacing: 20) {
Text("Add This Device")
.font(.largeTitle)
.fontWeight(.bold)
if !showOtpField {
// Email entry
TextField("Email Address", text: $email)
.keyboardType(.emailAddress)
.autocapitalization(.none)
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
Button("Continue") {
viewModel.addDevice(email: email)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
} else {
// OTP Verification
Text("Enter the verification code sent to \(email)")
.multilineTextAlignment(.center)
TextField("6-Digit Code", text: $otp)
.keyboardType(.numberPad)
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
Button("Verify") {
viewModel.verifyOTP(otp: otp)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
NavigationLink(destination: HomeView(), isActive: $navigateToHome) { EmptyView() }
}
.padding()
.alert(isPresented: $viewModel.showAlert) {
Alert(title: Text(viewModel.alertTitle),
message: Text(viewModel.alertMessage),
dismissButton: .default(Text("OK")))
}
.onReceive(viewModel.$otpGenerated) { generated in
showOtpField = generated
}
.onReceive(viewModel.$deviceAdded) { added in
if added {
navigateToHome = true
viewModel.deviceAdded = false
}
}
}
}
class AddDeviceViewModel: ObservableObject, AddDeviceCallback {
private let addDeviceManager = AddDeviceManager(apiKey: "YOUR_API_KEY")
@Published var otpGenerated = false
@Published var deviceAdded = false
@Published var showAlert = false
@Published var alertTitle = ""
@Published var alertMessage = ""
func addDevice(email: String) {
addDeviceManager.startAddDeviceFlow(userid: email, callback: self)
}
func verifyOTP(otp: String) {
addDeviceManager.handleVerifyOTP(otp: otp)
}
// MARK: - AddDeviceCallback
func showError(addDeviceErrorCode: AddDeviceErrorCode, errorMessage: String) {
DispatchQueue.main.async {
self.alertTitle = "Error"
self.alertMessage = errorMessage
self.showAlert = true
}
}
func onAddDeviceSuccess() {
DispatchQueue.main.async {
self.alertTitle = "Success"
self.alertMessage = "Device successfully added to your account!"
self.showAlert = true
self.deviceAdded = true
}
}
func onGenerateOTPSuccess() {
DispatchQueue.main.async {
self.otpGenerated = true
}
}
}
class WebLoginManager: WebLoginCallback {
private let webLoginManager = WebLoginManager(apiKey: "YOUR_API_KEY")
func verifyPin(pin: String, accessToken: String) {
webLoginManager.webLogin(accessToken: accessToken, pin: pin, callback: self)
}
func approveLogin(token: String, accessToken: String) {
webLoginManager.webApprove(accessToken: accessToken, token: token, callback: self)
}
// MARK: - WebLoginCallback Methods
func showError(webLoginErrorCode: WebLoginErrorCode, errorMessage: String) {
// Handle error
print("Error: \(errorMessage)")
}
func onSuccess() {
// Web login successfully approved
print("Web login approved!")
}
}
class WebLoginManager: WebLoginCallback {
private let webLoginManager = WebLoginManager(apiKey: "YOUR_API_KEY")
func verifyPin(pin: String, accessToken: String) {
webLoginManager.webLogin(accessToken: accessToken, pin: pin, callback: self)
}
func approveLogin(token: String, accessToken: String) {
webLoginManager.webApprove(accessToken: accessToken, token: token, callback: self)
}
// MARK: - WebLoginCallback Methods
func showError(webLoginErrorCode: WebLoginErrorCode, errorMessage: String) {
// Handle error
print("Error: \(errorMessage)")
}
func onSuccess() {
// Web login successfully approved
print("Web login approved!")
}
}
import SwiftUI
import HawcxSDK
struct WebLoginView: View {
@StateObject private var viewModel = WebLoginViewModel()
@State private var pin = ""
@State private var showApprovalScreen = false
@State private var navigateBack = false
var body: some View {
NavigationView {
VStack(spacing: 20) {
if !showApprovalScreen {
Text("Web Login")
.font(.largeTitle)
.fontWeight(.bold)
Text("Enter the 7-digit PIN shown on the web login screen")
.multilineTextAlignment(.center)
TextField("7-Digit PIN", text: $pin)
.keyboardType(.numberPad)
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
Button("Verify PIN") {
viewModel.verifyPin(pin: pin, accessToken: "YOUR_ACCESS_TOKEN")
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
} else {
Text("Approve Login")
.font(.largeTitle)
.fontWeight(.bold)
Text("Someone is trying to log in to your account. Verify it's you.")
.multilineTextAlignment(.center)
VStack(alignment: .leading, spacing: 10) {
Text("Browser: \(viewModel.sessionDetails?.browserWithVersion ?? "Unknown")")
Text("Device: \(viewModel.sessionDetails?.deviceType ?? "Unknown")")
Text("Location: \(viewModel.sessionDetails?.city ?? ""), \(viewModel.sessionDetails?.country ?? "")")
Text("IP Address: \(viewModel.sessionDetails?.ipDetails ?? "Unknown")")
}
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
HStack(spacing: 20) {
Button("Deny") {
viewModel.denyLogin()
}
.padding()
.background(Color.red)
.foregroundColor(.white)
.cornerRadius(8)
Button("Approve") {
viewModel.approveLogin()
}
.padding()
.background(Color.green)
.foregroundColor(.white)
.cornerRadius(8)
}
}
NavigationLink(destination: HomeView(), isActive: $navigateBack) { EmptyView() }
}
.padding()
.alert(isPresented: $viewModel.showAlert) {
Alert(title: Text(viewModel.alertTitle),
message: Text(viewModel.alertMessage),
dismissButton: .default(Text("OK")))
}
.onReceive(viewModel.$showApproval) { show in
showApprovalScreen = show
}
.onReceive(viewModel.$loginProcessed) { processed in
if processed {
navigateBack = true
viewModel.loginProcessed = false
}
}
}
}
}
// WebLoginViewModel and related code...
import LocalAuthentication
func authenticateWithBiometrics(for username: String, completion: @escaping (Bool) -> Void) {
let context = LAContext()
var error: NSError?
// Check if biometric authentication is available
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Log in to your account"
// Authenticate using biometrics
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
DispatchQueue.main.async {
if success {
// Biometric authentication successful, proceed with Hawcx authentication
self.signIn.signIn(userid: username, callback: self)
} else {
// Biometric authentication failed
completion(false)
}
}
}
} else {
// Biometrics not available
completion(false)
}
}
class SessionManager: DevSessionCallback {
private let deviceSession = DevSession(apiKey: "YOUR_API_KEY")
func fetchDeviceDetails() {
deviceSession.GetDeviceDetails(callback: self)
}
// MARK: - DevSessionCallback Methods
func onSuccess() {
// Device details successfully retrieved
// Access device data from UserDefaults
if let data = UserDefaults.standard.data(forKey: "devDetails") {
do {
let devices = try JSONDecoder().decode([DeviceSessionInfo].self, from: data)
// Process device information
print("Retrieved \(devices.count) devices")
} catch {
print("Error decoding device details: \(error)")
}
}
}
func showError() {
// Error fetching device details
print("Error fetching device details")
}
}
class SessionManager: DevSessionCallback {
private let deviceSession = DevSession(apiKey: "YOUR_API_KEY")
func fetchDeviceDetails() {
deviceSession.GetDeviceDetails(callback: self)
}
// MARK: - DevSessionCallback Methods
func onSuccess() {
// Device details successfully retrieved
// Access device data from UserDefaults
if let data = UserDefaults.standard.data(forKey: "devDetails") {
do {
let devices = try JSONDecoder().decode([DeviceSessionInfo].self, from: data)
// Process device information
print("Retrieved \(devices.count) devices")
} catch {
print("Error decoding device details: \(error)")
}
}
}
func showError() {
// Error fetching device details
print("Error fetching device details")
}
}
import SwiftUI
import HawcxSDK
struct DeviceSessionView: View {
@StateObject private var viewModel = DeviceSessionViewModel()
var body: some View {
VStack(spacing: 20) {
Text("Device Management")
.font(.largeTitle)
.fontWeight(.bold)
if viewModel.isLoading {
ProgressView("Loading device details...")
} else {
List(viewModel.devices, id: \.deviceId) { device in
VStack(alignment: .leading, spacing: 8) {
Text(device.deviceName)
.font(.headline)
Text("Last active: \(device.lastActiveDate)")
.font(.subheadline)
.foregroundColor(.secondary)
Text("Device ID: \(device.deviceId)")
.font(.caption)
.foregroundColor(.secondary)
}
.padding(.vertical, 8)
}
}
Button("Refresh Device List") {
viewModel.fetchDeviceDetails()
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding()
.alert(isPresented: $viewModel.showAlert) {
Alert(title: Text(viewModel.alertTitle),
message: Text(viewModel.alertMessage),
dismissButton: .default(Text("OK")))
}
}
}
class DeviceSessionViewModel: ObservableObject, DevSessionCallback {
private let deviceSession = DevSession(apiKey: "YOUR_API_KEY")
@Published var devices: [DeviceSessionInfo] = []
@Published var isLoading = false
@Published var showAlert = false
@Published var alertTitle = ""
@Published var alertMessage = ""
func fetchDeviceDetails() {
isLoading = true
deviceSession.GetDeviceDetails(callback: self)
}
// MARK: - DevSessionCallback
func onSuccess() {
DispatchQueue.main.async {
self.isLoading = false
if let data = UserDefaults.standard.data(forKey: "devDetails") {
do {
self.devices = try JSONDecoder().decode([DeviceSessionInfo].self, from: data)
} catch {
self.alertTitle = "Error"
self.alertMessage = "Failed to decode device details: \(error.localizedDescription)"
self.showAlert = true
}
}
}
}
func showError() {
DispatchQueue.main.async {
self.isLoading = false
self.alertTitle = "Error"
self.alertMessage = "Failed to fetch device details"
self.showAlert = true
}
}
}
// SignUp Error Handling
func showError(signUpErrorCode: SignUpErrorCode, errorMessage: String) {
switch signUpErrorCode {
case .userAlreadyExists:
// Show login option instead
print("User already exists. Please log in.")
case .verifyOtpFailed:
// Show OTP retry UI
print("Invalid OTP. Please try again.")
case .generateOtpFailed:
// Show retry option
print("Failed to generate OTP. Please try again.")
case .networkError:
// Show connectivity error
print("Network error. Please check your connection.")
case .unknownError:
// Show generic error
print("An unexpected error occurred: \(errorMessage)")
default:
print("Error: \(errorMessage)")
}
}
// SignIn Error Handling
func showError(signInErrorCode: SignInErrorCode, errorMessage: String) {
switch signInErrorCode {
case .userNotFound:
// Show registration option
print("User not found. Please sign up.")
case .networkError:
// Show connectivity error
print("Network error. Please check your connection.")
case .addDeviceRequired:
// Redirect to Add Device flow
print("This device needs to be added to your account.")
default:
print("Error: \(errorMessage)")
}
}
// SignUp Error Handling
func showError(signUpErrorCode: SignUpErrorCode, errorMessage: String) {
switch signUpErrorCode {
case .userAlreadyExists:
// Show login option instead
print("User already exists. Please log in.")
case .verifyOtpFailed:
// Show OTP retry UI
print("Invalid OTP. Please try again.")
case .generateOtpFailed:
// Show retry option
print("Failed to generate OTP. Please try again.")
case .networkError:
// Show connectivity error
print("Network error. Please check your connection.")
case .unknownError:
// Show generic error
print("An unexpected error occurred: \(errorMessage)")
default:
print("Error: \(errorMessage)")
}
}
// SignIn Error Handling
func showError(signInErrorCode: SignInErrorCode, errorMessage: String) {
switch signInErrorCode {
case .userNotFound:
// Show registration option
print("User not found. Please sign up.")
case .networkError:
// Show connectivity error
print("Network error. Please check your connection.")
case .addDeviceRequired:
// Redirect to Add Device flow
print("This device needs to be added to your account.")
default:
print("Error: \(errorMessage)")
}
}
import SwiftUI
import HawcxSDK
struct ErrorHandlingView: View {
@StateObject private var viewModel = ErrorHandlingViewModel()
var body: some View {
VStack(spacing: 20) {
Text("Error Handling")
.font(.largeTitle)
.fontWeight(.bold)
// SignUp Error Example
VStack(alignment: .leading, spacing: 10) {
Text("SignUp Errors")
.font(.headline)
Button("Test User Already Exists") {
viewModel.testSignUpError(.userAlreadyExists)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
Button("Test OTP Verification Failed") {
viewModel.testSignUpError(.verifyOtpFailed)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(12)
// SignIn Error Example
VStack(alignment: .leading, spacing: 10) {
Text("SignIn Errors")
.font(.headline)
Button("Test User Not Found") {
viewModel.testSignInError(.userNotFound)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
Button("Test Add Device Required") {
viewModel.testSignInError(.addDeviceRequired)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(12)
}
.padding()
.alert(isPresented: $viewModel.showAlert) {
Alert(title: Text(viewModel.alertTitle),
message: Text(viewModel.alertMessage),
dismissButton: .default(Text("OK")))
}
}
}
class ErrorHandlingViewModel: ObservableObject {
@Published var showAlert = false
@Published var alertTitle = ""
@Published var alertMessage = ""
func testSignUpError(_ errorCode: SignUpErrorCode) {
switch errorCode {
case .userAlreadyExists:
alertTitle = "User Already Exists"
alertMessage = "This email is already registered. Please log in instead."
case .verifyOtpFailed:
alertTitle = "Invalid OTP"
alertMessage = "The verification code is incorrect. Please try again."
case .generateOtpFailed:
alertTitle = "OTP Generation Failed"
alertMessage = "We couldn't send a verification code. Please try again."
case .networkError:
alertTitle = "Network Error"
alertMessage = "Please check your internet connection and try again."
case .unknownError:
alertTitle = "Unknown Error"
alertMessage = "An unexpected error occurred. Please try again later."
default:
alertTitle = "Error"
alertMessage = "An error occurred during sign up."
}
showAlert = true
}
func testSignInError(_ errorCode: SignInErrorCode) {
switch errorCode {
case .userNotFound:
alertTitle = "User Not Found"
alertMessage = "This email is not registered. Please sign up first."
case .networkError:
alertTitle = "Network Error"
alertMessage = "Please check your internet connection and try again."
case .addDeviceRequired:
alertTitle = "Add Device Required"
alertMessage = "This device needs to be added to your account."
default:
alertTitle = "Error"
alertMessage = "An error occurred during sign in."
}
showAlert = true
}
}
class SessionManager: DevSessionCallback {
private let deviceSession = DevSession(apiKey: "YOUR_API_KEY")
func fetchDeviceDetails() {
deviceSession.GetDeviceDetails(callback: self)
}
// MARK: - DevSessionCallback Methods
func onSuccess() {
// Device details successfully retrieved
// Access device data from UserDefaults
if let data = UserDefaults.standard.data(forKey: "devDetails") {
do {
let devices = try JSONDecoder().decode([DeviceSessionInfo].self, from: data)
// Process device information
print("Retrieved \(devices.count) devices")
} catch {
print("Error decoding device details: \(error)")
}
}
}
func showError() {
// Error fetching device details
print("Error fetching device details")
}
}
class SessionManager: DevSessionCallback {
private let deviceSession = DevSession(apiKey: "YOUR_API_KEY")
func fetchDeviceDetails() {
deviceSession.GetDeviceDetails(callback: self)
}
// MARK: - DevSessionCallback Methods
func onSuccess() {
// Device details successfully retrieved
// Access device data from UserDefaults
if let data = UserDefaults.standard.data(forKey: "devDetails") {
do {
let devices = try JSONDecoder().decode([DeviceSessionInfo].self, from: data)
// Process device information
print("Retrieved \(devices.count) devices")
} catch {
print("Error decoding device details: \(error)")
}
}
}
func showError() {
// Error fetching device details
print("Error fetching device details")
}
}
// SignUp Error Handling
func showError(signUpErrorCode: SignUpErrorCode, errorMessage: String) {
switch signUpErrorCode {
case .userAlreadyExists:
// Show login option instead
print("User already exists. Please log in.")
case .verifyOtpFailed:
// Show OTP retry UI
print("Invalid OTP. Please try again.")
case .generateOtpFailed:
// Show retry option
print("Failed to generate OTP. Please try again.")
case .networkError:
// Show connectivity error
print("Network error. Please check your connection.")
case .unknownError:
// Show generic error
print("An unexpected error occurred: \(errorMessage)")
default:
print("Error: \(errorMessage)")
}
}
// SignIn Error Handling
func showError(signInErrorCode: SignInErrorCode, errorMessage: String) {
switch signInErrorCode {
case .userNotFound:
// Show registration option
print("User not found. Please sign up.")
case .networkError:
// Show connectivity error
print("Network error. Please check your connection.")
case .addDeviceRequired:
// Redirect to Add Device flow
print("This device needs to be added to your account.")
default:
print("Error: \(errorMessage)")
}
}
Troubleshooting
Solution: Ensure your API key is correct and check if you need to configure App Transport Security (ATS) settings in your Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Solution:
- Ensure you’re using the most recent OTP (codes typically expire after 5-10 minutes)
- Verify there are no leading/trailing spaces in the OTP input
- Check if the OTP has exactly 6 digits
Solution:
-
Add the necessary permission in Info.plist:
<key>NSFaceIDUsageDescription</key> <string>We use Face ID for secure authentication</string>
-
Check if the device supports biometrics:
let context = LAContext() var error: NSError? let canUse = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
Error Codes
Error Code | Description |
---|---|
userAlreadyExists | User is already registered in the system |
verifyOtpFailed | Invalid or expired OTP |
generateOtpFailed | Failed to generate or send OTP |
networkError | Connection or server error |
unknownError | Unspecified error |
Error Code | Description |
---|---|
userAlreadyExists | User is already registered in the system |
verifyOtpFailed | Invalid or expired OTP |
generateOtpFailed | Failed to generate or send OTP |
networkError | Connection or server error |
unknownError | Unspecified error |
Error Code | Description |
---|---|
userNotFound | User is not registered in the system |
networkError | Connection or server error |
addDeviceRequired | Current device not registered to account |
unknownError | Unspecified error |
Error Code | Description |
---|---|
verifyOtpFailed | Invalid or expired OTP |
generateOtpFailed | Failed to generate or send OTP |
networkError | Connection or server error |
unknownError | Unspecified error |
Try it out!
SDK
Check out our iOS SDK and try it yourself!.
Example App
Check out our example app on GitHub for a complete implementation.