Phonelink

iOS

Integrate Phonelink phone verification in native iOS apps with Swift.

Overview

The PhoneLink Swift SDK uses ASWebAuthenticationSession for secure in-app verification. The user stays within your app — PhoneLink opens a system-managed browser session, the user verifies their phone number, and the result is returned directly via a completion handler.

Install

Swift Package Manager

Add the dependency to your Package.swift:

dependencies: [
    .package(url: "https://github.com/phonelink-sdk/phonelink-swift.git", from: "0.1.0")
]

Or in Xcode: File > Add Package Dependencies and enter the repository URL.

CocoaPods

Podfile
pod 'PhoneLink', '~> 0.1.0'

Then run pod install.

Basic usage

1. Configure

Add PhoneLink.configure to your AppDelegate or App init. Call this once at launch before any verification calls.

import PhoneLink

PhoneLink.configure("your-client-id")

2. Verify

Call PhoneLink.verify to start the verification flow. The SDK opens a secure browser session where the user enters and verifies their phone number.

PhoneLink.verify { result in
    if let phone = result.phoneNumber, let token = result.token {
        // Send token to your server for validation
        print("Verified: \(phone)")
    } else if let error = result.error {
        print("Error: \(error)")
    }
}

How it works

When you call PhoneLink.verify:

  1. The SDK opens the Phonelink verification page in an ASWebAuthenticationSession
  2. The user enters their phone number and completes verification
  3. Phonelink redirects back to your app with a signed JWT token
  4. The browser session closes and the completion handler fires with a PhoneLinkResult

The result contains the verified phoneNumber in E.164 format and a signed token for server-side validation.

Prefilled phone number

If you already have the user's phone number, skip the number entry screen by passing it directly:

PhoneLink.verify(phoneNumber: "+14155551234") { result in
    if let token = result.token {
        // Send token to your server
    }
}

The user will go straight to the verification code screen.

Error handling

The PhoneLinkResult.error property contains a PhoneLinkError if something went wrong:

PhoneLink.verify { result in
    if let error = result.error {
        switch error {
        case .notConfigured:
            print("Call PhoneLink.configure() before verifying")
        case .cancelled:
            print("User dismissed the verification screen")
        case .missingToken:
            print("Verification completed but no token was returned")
        case .sessionFailed(let underlying):
            print("Auth session failed: \(underlying.localizedDescription)")
        }
        return
    }

    // Success
    print("Phone: \(result.phoneNumber ?? "unknown")")
}

Full example

A complete SwiftUI view with state management:

import SwiftUI
import PhoneLink

struct PhoneVerificationView: View {
    @State private var phone: String?
    @State private var error: String?
    @State private var isVerifying = false

    var body: some View {
        VStack(spacing: 16) {
            if let phone {
                Text("Phone verified: \(phone)")
            } else {
                if let error {
                    Text(error)
                        .foregroundStyle(.red)
                }

                Button(action: handleVerify) {
                    if isVerifying {
                        ProgressView()
                    } else {
                        Text("Verify Phone Number")
                    }
                }
                .disabled(isVerifying)
            }
        }
    }

    private func handleVerify() {
        isVerifying = true
        error = nil

        PhoneLink.verify { result in
            DispatchQueue.main.async {
                isVerifying = false

                if let err = result.error {
                    if case .cancelled = err { return }
                    error = "Verification failed. Try again."
                    return
                }

                guard let token = result.token else { return }

                Task {
                    do {
                        var request = URLRequest(url: URL(string: "https://api.myapp.com/verify-phone")!)
                        request.httpMethod = "POST"
                        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
                        request.httpBody = try JSONEncoder().encode(["token": token])

                        let (data, _) = try await URLSession.shared.data(for: request)
                        let response = try JSONDecoder().decode(VerifyResponse.self, from: data)
                        phone = response.phone
                    } catch {
                        self.error = "Server verification failed. Try again."
                    }
                }
            }
        }
    }
}

struct VerifyResponse: Decodable {
    let phone: String
}

Server verification

The result.token contains a signed JWT that should be validated on your server before trusting the phone number. The server-side verification is identical across all platforms — see the Server Verification guide.

Requirements

  • iOS 15.0+
  • Swift 5.9+
  • Xcode 15+

On this page