> ## Documentation Index
> Fetch the complete documentation index at: https://docs.magic.link/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS SDK Reference

> The Magic SDK for iOS is your entry-point to secure, passwordless authentication for your mobile app. This guide will cover some important topics for getting started with iOS APIs and to make the most of Magic's features.

## Overview

## Getting Started

The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic **publishable** key.

### Performance improvement (optional)

We use service workers for for better performance on web3 operations. If you'd like to take advantage of this performance boost, you'd have to enable app bound domains. To do that, add the following to your `Info.plist` and rebuild your app:

```xml plist icon="file-xml" theme={null}
<key>WKAppBoundDomains</key>
<array>
  <string>https://auth.magic.link</string>
</array>
```

### Constructor

`Magic()`

|            |                                           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ---------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Parameter  | Type                                      | Definition                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `apiKey`   | String                                    | Your **publishable** API Key retrieved from the Magic Dashboard.                                                                                                                                                                                                                                                                                                                                                                                                               |
| `network?` | `EthNetwork` \| `CustomNodeConfiguration` | (`EthNetwork`): A representation of the connected Ethereum network (`.mainnet` or `.goerli`).<br /><br />⁠(`CustomNodeConfiguration`): A custom Ethereum Node configuration with the following shape:<br /><br />`rpcUrl` (String): A URL pointing to your custom Ethereum Node.<br /><br />⁠`chainId?` (Number): Some Node infrastructures require you to pass an explicit chain ID. If you are aware that your Node requires this configuration, pass it here as an integer. |
| `locale?`  | `Locale`                                  | Customize the language of Magic's modal, email and confirmation screen. See [Localization](/embedded-wallets/authentication/customization/localization) for more.                                                                                                                                                                                                                                                                                                              |

### Initialization

In `AppDelegate`

```swift Swift icon="swift" theme={null}
import MagicSDK
import UIKit

@UIApplicationMain
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // assign the newly created Magic instance to shared property
    // Test key defaults to "goerli", live key defaults to "mainnet"
    Magic.shared = Magic(apiKey: "PUBLISHABLE_API_KEY")
    // Construct with an API key and options:
    Magic.shared = Magic(apiKey: "PUBLISHABLE_API_KEY", ethNetwork: .goerli)
    // Construct with an API key and custom node options:
    let customNode = CustomNodeConfiguration(rpcUrl: "https://polgyon-rpc.com", chainId: 137)
    Magic.shared = Magic(apiKey: "PUBLISHABLE_API_KEY", customNode: customNode)
    // Construct with an API key and custom locale
    Magic.shared = Magic(apiKey: "PUBLISHABLE_API_KEY", locale: "es")

    return true
}
```

## Auth Module

The Auth Module and its members are accessible on the Magic SDK instance by the `auth` property.

### `loginWithSMS`

Authenticate a user passwordlessly using a one-time code sent to the specified phone number.

[List of Currently Blocked Country Codes](/home/faq#questions-around-sms)

**Arguments**

* `configuration` (LoginWithSMSConfiguration):
  * `phoneNumber` (string): The user phone number to log in with
  * `showUI` (boolean): If `true`, show an out-of-the-box pending UI while the request is in flight

**Returns**

* `Promise<string | null>`: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.

**Example**

**Closure**

```swift Swift icon="swift" theme={null}
import MagicSDK

class LoginViewController: UIViewController {
    @IBOutlet weak var phoneNumberInput: UITextField!
    let magic = Magic.shared

    @IBAction func login() {
        guard let magic = magic else { return }
        guard let phoneNumber = self.phoneNumberInput.text else { return }

        let configuration = LoginWithSMSConfiguration(phoneNumber: phoneNumber, showUI: true)

        magic.auth.loginWithSMS(configuration, response: { response in
            guard let token = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", token)
        })
    }
}
```

**Promise**

```swift Swift icon="swift" theme={null}
import MagicSDK

class LoginViewController: UIViewController {
    @IBOutlet weak var phoneNumberInput: UITextField!
    let magic = Magic.shared

    @IBAction func login() {
        guard let magic = magic else { return }

        let configuration = LoginWithSMSConfiguration(phoneNumber: phoneNumber)

        magic.auth.loginWithSMS(configuration).done({ result in
            print(result) // DIDToken
        }).catch({
            print(error) // handle Error
        })
    }
}
```

### `loginWithEmailOTP`

Authenticate a user passwordlessly using an email one-time code sent to the specified user's email address.

**Arguments**

* `configuration` (LoginWithEmailOTPConfiguration):
  * `email` (string): The user email to log in with

**Returns**

* `Promise<string | null>`: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.

**Example**

**Closure**

```swift Swift icon="swift" theme={null}
import MagicSDK

class LoginViewController: UIViewController {
    @IBOutlet weak var emailInput: UITextField!
    let magic = Magic.shared

    @IBAction func login() {
        guard let magic = magic else { return }
        guard let email = self.emailInput.text else { return }

        let configuration = LoginWithEmailOTPConfiguration(email: email)

        magic.auth.loginWithEmailOTP(configuration, response: { response in
            guard let token = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", token)
        })
    }
}
```

**Promise**

```swift Swift icon="swift" theme={null}
import MagicSDK

class LoginViewController: UIViewController {
    @IBOutlet weak var emailInput: UITextField!
    let magic = Magic.shared

    @IBAction func login() {
        guard let magic = magic else { return }

        let configuration = LoginWithEmailOTPConfiguration(email: self.emailInput.text!)

        magic.auth.loginWithEmailOTP(configuration).done({ result in
            print(result) // DIDToken
        }).catch({
            print(error) // handle Error
        })
    }
}
```

### Headless Email OTP (`showUI: false`)

If you'd rather build your own UI for the OTP, MFA, and device-verification prompts instead of using Magic's out-of-the-box modal, pass `showUI: false` in `LoginWithEmailOTPConfiguration`. The SDK then returns a `MagicEventPromise<String>` instead of immediately resolving — subscribe to it for inbound events from the relayer, and `emit` outbound events (like the OTP the user typed) back to it.

**Arguments**

* `configuration` (LoginWithEmailOTPConfiguration):
  * `email` (string): The user email to log in with
  * `showUI` (boolean): Pass `false` to drive the flow with your own UI
* `eventLog` (boolean): If `true`, logs each event received/emitted to the console

**Returns**

* `MagicEventPromise<String>`: An event-driven promise. Call `.on(eventName:)` / `.onPersistent(eventName:)` to subscribe, `.emit(eventType:arg:)` to respond, and `.done` / `.catch` / `.onError` to handle the final result.

**Events**

Events are grouped into four enums under `AuthModule`, mirroring the `@magic-sdk/types` design:

**`LoginWithEmailOTPEventOnReceived`** — inbound email OTP events

| Case                 | Raw value              | Meaning                                             |
| -------------------- | ---------------------- | --------------------------------------------------- |
| `emailOTPSent`       | `email-otp-sent`       | OTP email was sent — prompt the user for the code   |
| `invalidEmailOTP`    | `invalid-email-otp`    | Submitted OTP was invalid — may fire multiple times |
| `expiredEmailOTP`    | `expired-email-otp`    | OTP expired                                         |
| `loginThrottled`     | `login-throttled`      | Too many attempts — client should back off          |
| `maxAttemptsReached` | `max-attempts-reached` | No retries left                                     |

**`LoginWithEmailOTPEventEmit`** — outbound email OTP events

| Case             | Raw value          | Meaning                              |
| ---------------- | ------------------ | ------------------------------------ |
| `verifyEmailOTP` | `verify-email-otp` | Emit with the OTP the user entered   |
| `cancel`         | `cancel`           | Emit to cancel the in-progress login |

**`MFAEventOnReceived`** — inbound MFA events (reusable across flows)

| Case                     | Raw value                   | Meaning                                                       |
| ------------------------ | --------------------------- | ------------------------------------------------------------- |
| `mfaSentHandle`          | `mfa-sent-handle`           | User has MFA enabled — prompt for authenticator code          |
| `invalidMfaOTP`          | `invalid-mfa-otp`           | Submitted MFA code was invalid — may fire multiple times      |
| `recoveryCodeSentHandle` | `recovery-code-sent-handle` | Prompt for MFA recovery code                                  |
| `invalidRecoveryCode`    | `invalid-recovery-code`     | Submitted recovery code was invalid — may fire multiple times |
| `recoveryCodeSuccess`    | `recovery-code-success`     | Recovery code accepted                                        |

**`MFAEventEmit`** — outbound MFA events (reusable across flows)

| Case                 | Raw value              | Meaning                                           |
| -------------------- | ---------------------- | ------------------------------------------------- |
| `verifyMFACode`      | `verify-mfa-code`      | Emit with the authenticator code the user entered |
| `verifyRecoveryCode` | `verify-recovery-code` | Emit with the recovery code the user entered      |
| `lostDevice`         | `lost-device`          | Emit to switch from MFA to recovery-code flow     |
| `cancel`             | `cancel`               | Emit to cancel the in-progress MFA challenge      |

**`DeviceVerificationEventOnReceived`** — inbound device-verification events

| Case                            | Raw value                          | Meaning                                           |
| ------------------------------- | ---------------------------------- | ------------------------------------------------- |
| `deviceNeedsApproval`           | `device-needs-approval`            | New device detected — user must approve via email |
| `deviceVerificationEmailSent`   | `device-verification-email-sent`   | Device approval email was sent                    |
| `deviceApproved`                | `device-approved`                  | Device was approved — login will continue         |
| `deviceVerificationLinkExpired` | `device-verification-link-expired` | Device approval link expired                      |

**`DeviceVerificationEventEmit`** — outbound device-verification events

| Case          | Raw value      | Meaning                                      |
| ------------- | -------------- | -------------------------------------------- |
| `deviceRetry` | `device-retry` | Emit to resend the device verification email |

Use `.on(eventName:)` for events that fire once, and `.onPersistent(eventName:)` for events that may fire repeatedly (e.g. `invalidEmailOTP` on each wrong attempt) so the subscription isn't torn down after the first occurrence.

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class LoginViewController: UIViewController {
    @IBOutlet weak var emailInput: UITextField!
    let magic = Magic.shared

    private var emailOTPHandle: MagicEventPromise<String>?

    @IBAction func loginHeadless() {
        guard let magic = magic, let email = emailInput.text else { return }

        let configuration = LoginWithEmailOTPConfiguration(email: email, showUI: false)

        emailOTPHandle = magic.auth.loginWithEmailOTP(configuration, eventLog: true)

            // — Email OTP —
            .on(eventName: AuthModule.LoginWithEmailOTPEventOnReceived.emailOTPSent.rawValue) { [weak self] in
                // Prompt the user for the OTP they received by email, then:
                let otp = "123456" // value collected from your own UI
                self?.emailOTPHandle?.emit(eventType: AuthModule.LoginWithEmailOTPEventEmit.verifyEmailOTP.rawValue, arg: otp)
            }
            .onPersistent(eventName: AuthModule.LoginWithEmailOTPEventOnReceived.invalidEmailOTP.rawValue) { [weak self] in
                // Invalid OTP — re-prompt; fires on each wrong attempt
                let otp = "123456"
                self?.emailOTPHandle?.emit(eventType: AuthModule.LoginWithEmailOTPEventEmit.verifyEmailOTP.rawValue, arg: otp)
            }
            .on(eventName: AuthModule.LoginWithEmailOTPEventOnReceived.expiredEmailOTP.rawValue) { [weak self] in
                // OTP expired — restart the login flow
            }
            .on(eventName: AuthModule.LoginWithEmailOTPEventOnReceived.loginThrottled.rawValue) { [weak self] in
                // Too many attempts — ask the user to wait before retrying
            }
            .on(eventName: AuthModule.LoginWithEmailOTPEventOnReceived.maxAttemptsReached.rawValue) { [weak self] in
                // No retries left — end the flow
            }

            // — MFA —
            .on(eventName: AuthModule.MFAEventOnReceived.mfaSentHandle.rawValue) { [weak self] in
                // User has MFA enabled — prompt for their authenticator code, then:
                let code = "123456" // value collected from your own UI
                self?.emailOTPHandle?.emit(eventType: AuthModule.MFAEventEmit.verifyMFACode.rawValue, arg: code)
            }
            .onPersistent(eventName: AuthModule.MFAEventOnReceived.invalidMfaOTP.rawValue) { [weak self] in
                // Invalid MFA code — re-prompt or switch to recovery:
                self?.emailOTPHandle?.emit(eventType: AuthModule.MFAEventEmit.lostDevice.rawValue)
            }
            .on(eventName: AuthModule.MFAEventOnReceived.recoveryCodeSentHandle.rawValue) { [weak self] in
                // Prompt for MFA recovery code, then:
                let code = "RECOVERY-CODE" // value collected from your own UI
                self?.emailOTPHandle?.emit(eventType: AuthModule.MFAEventEmit.verifyRecoveryCode.rawValue, arg: code)
            }
            .onPersistent(eventName: AuthModule.MFAEventOnReceived.invalidRecoveryCode.rawValue) { [weak self] in
                // Invalid recovery code — re-prompt or cancel
                self?.emailOTPHandle?.emit(eventType: AuthModule.MFAEventEmit.cancel.rawValue)
            }
            .on(eventName: AuthModule.MFAEventOnReceived.recoveryCodeSuccess.rawValue) { [weak self] in
                // Recovery code accepted — login will complete via .done
            }

            // — Device verification —
            .on(eventName: AuthModule.DeviceVerificationEventOnReceived.deviceNeedsApproval.rawValue) { [weak self] in
                // New device — ask the user to check their inbox to approve
            }
            .on(eventName: AuthModule.DeviceVerificationEventOnReceived.deviceVerificationEmailSent.rawValue) { [weak self] in
                // Device approval email sent — waiting for the user to click it
            }
            .on(eventName: AuthModule.DeviceVerificationEventOnReceived.deviceApproved.rawValue) { [weak self] in
                // Device approved — login will continue automatically
            }
            .on(eventName: AuthModule.DeviceVerificationEventOnReceived.deviceVerificationLinkExpired.rawValue) { [weak self] in
                // Approval link expired — retry
                self?.emailOTPHandle?.emit(eventType: AuthModule.DeviceVerificationEventEmit.deviceRetry.rawValue)
            }

        emailOTPHandle?
            .onError { error in
                print("Error:", error.localizedDescription)
            }
            .done { didToken in
                print("Result", didToken) // DIDToken — login complete
            }
            .catch { error in
                print(error)
            }
    }
}
```

<Note>
  Cancel an in-progress headless login at any time: `emailOTPHandle?.emit(eventType: AuthModule.LoginWithEmailOTPEventEmit.cancel.rawValue)`.
</Note>

## Wallet Module

The Wallet Module and its members are accessible on the Magic SDK instance by the `wallet` property.

### `connectWithUI`

Renders a simple login form UI to collect the user's email address and authenticate them passwordlessly using a one-time passcode (OTP) sent to their email address they input.

**Arguments**

* None

**Returns**

* A `promiEvent` which returns an `String[]` when resolved: An array of user accounts that are connected, with the first element being the current public address of the user. You can read more on PromiEvents [here](#promievents).

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {
    let magic = Magic.shared

    func logout() {
        guard let magic = magic else { return }

        // Assuming user is logged in
        magic.wallet.connectWithUI(response: { response in
            guard let result = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", result)
        })
    }
}
```

### `showUI`

Displays the fully [navigable wallet](/embedded-wallets/wallets/customization/widget-ui) to the user that adheres to the toggled configurations on your developer dashboard’s Widget UI tab. ⁠ ⁠This is only supported for users who login with email or Google. User must be signed in for this method to return or else it will throw an error.

**Arguments**

* None

**Returns**

* `Promise` which resolves when the user closes the window

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {
    let magic = Magic.shared

    func logout() {
        guard let magic = magic else { return }

        // Assuming user is logged in
        magic.wallet.showUI(response: { response in
            guard let result = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", result)
        })
    }
}
```

## User Module

The User Module and its members are accessible on the Magic SDK instance by the `user` property.

### `updateEmail`

Initiates the update email flow that allows a user to change to a new email.

**Arguments**

* `configuration` (UpdateEmailConfiguration):
  * `email` (string): The user email to update with
  * `showUI` (boolean): If `true`, show an out-of-the-box pending UI while the request is in flight

**Returns**

* `Promise<Bool>`: The promise resolves with a true boolean value if update email is successful and rejects with a specific error code if the request fails

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {

    let magic = Magic.shared

    // Initiates the flow to update a user's current email to a new one.
    func updateEmail() {

        guard let magic = magic else { return }

        // Assuming user is logged in
        let configuration = UpdateEmailConfiguration(email: "new_user_email@example.com", showUI: true)
        magic.user.updateEmail(configuration, response: { response in
            guard let result = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", result)
        })
    }

}
```

### `getIdToken`

Generates a Decentralized Id Token which acts as a proof of authentication to resource servers.

**Arguments**

* `configuration` (GetIdTokenConfiguration):
  * `lifespan` (number): will set the lifespan of the generated token. Defaults to 900s (15 mins).

**Returns**

* `Promise<String>`: Base64-encoded string representation of a JSON tuple representing `[proof, claim]`

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {

    let magic = Magic.shared

    func getIdToken() {
        guard let magic = magic else { return }

        // Assuming user is logged in
        let configuration = GetIdTokenConfiguration(lifespan: 900)

        magic.user.getIdToken(configuration, response: { response in
            guard let token = response.result
                else { return print(response.error.debugDescription) }
            print(token)
        })
    }

}
```

### `generateIdToken`

Generates a Decentralized Id Token with optional serialized data.

**Arguments**

* `configuration` (GenerateIdTokenConfiguration):
  * `lifespan` (number): will set the lifespan of the generated token. Defaults to 900s (15 mins).
  * `attachment` (str): will set a signature of serialized data in the generated token. Defaults to `"none"`.

**Returns**

* `Promise<String>`: Base64-encoded string representation of a JSON tuple representing `[proof, claim]`

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {
    let magic = Magic.shared

    func generateIdToken() {
        guard let magic = magic else { return }

        // Assuming user is logged in
        let configuration = GenerateIdTokenConfiguration(lifespan: 900, attachment: "none")

        magic.user.generateIdToken(configuration, response: { response in
            guard let token = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", token)
        })
    }
}
```

### `getInfo`

Retrieves information for the authenticated user.

**Arguments**

* *None*

**Returns**

* `Promise<UserInfo>` - The issuer, email and cryptographic [public address](https://support.blockchain.com/hc/en-us/articles/4417082520724-What-are-public-and-private-keys-and-how-do-they-work-) of the authenticated user.
  * `issuer` (string): The Decentralized ID of the user. In server-side use-cases, we recommend this value to be used as the user ID in your own tables.
  * `email` (string): Email address of the authenticated user
  * `phoneNumber` (string): Phone number of the authenticated user
  * `publicAddress` (string): The authenticated user's public address (a.k.a.: public key). Currently, this value is associated with the Ethereum blockchain.
  * `isMfaEnabled` (boolean): A boolean indicating if user has multi-factor authentication enabled
  * `recoveryFactors` (RecoveryFactor):
    * `value` (string): Recovery value such as phone number
    * `type` (RecoveryMethodType): The method used for account recovery

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {
    let magic = Magic.shared

    func getInfo() {
        guard let magic = magic else { return }

        // Assuming user is logged in
        magic.user.getInfo(response: { response in
            guard let metadata = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", metadata)
        })
    }
}
```

### `isLoggedIn`

Checks if a user is currently logged in to the Magic SDK.

**Arguments**

* *None*

**Returns**

* `Promise<Bool>`: A boolean value indicating if a user is logged in

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {
    let magic = Magic.shared

    func isLoggedIn() {
        guard let magic = magic else { return }

        magic.user.isLoggedIn(response: { response in
            guard let result = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", result)
        })
    }
}
```

### logout

Logs out the currently authenticated Magic user.

**Arguments**

* *None*

**Returns**

* `Promise<Bool>`: A boolean value indicating if a user has been logged out

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {
    let magic = Magic.shared

    func logout() {
        guard let magic = magic else { return }

        // Assuming user is logged in
        magic.user.logout(response: { response in
            guard let result = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", result)
        })
    }
}
```

### `showSettings`

Displays an iframe with the current user’s settings. Allows for users to update their email address, enable multi-factor authentication, and add a recovery factor.

<Note>
  Access to [MFA](/embedded-wallets/authentication/features/mfa) and [account
  recovery](/embedded-wallets/authentication/features/account-recovery) require paid add-ons.
</Note>

**Arguments**

* None

**Returns**

* `Promise` which resolves when the user closes the window

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {
    let magic = Magic.shared

    func logout() {
        guard let magic = magic else { return }

        // Assuming user is logged in
        magic.user.showSettings(response: { response in
            guard let result = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", result)
        })
    }
}
```

### `updatePhoneNumber`

Initiates the update phone number flow that allows a user to change their phone number.

**Arguments**

* `configuration`:
  * `phoneNumber` (str): The user phone number to update with

**Returns**

* `PromiEvent<boolean>`: The promise resolves with a true boolean value if update email is successful and rejects with a specific error code if the request fails

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {
    let magic = Magic.shared

    func logout() {
        guard let magic = magic else { return }

        // Assuming user is logged in
        let configuration = (phoneNumber: phoneNumber)
        magic.user.updatePhoneNumber(configuration, response: { response in
            guard let result = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", result)
        })
    }
}
```

### `recoverAccount`

Initiates the account recovery flow that allows a user to recover their account using their email address.

**Arguments**

* `configuration` (RecoverAccountConfiguration):
  * `email` (str): The user email address to recover account with

**Returns**

* `PromiEvent<boolean>`: The promise resolves with a true boolean value if the account recovery is successful and rejects with a specific error code if the request fails

**Example**

```swift Swift icon="swift" theme={null}
import MagicSDK

class MagicViewController: UIViewController {
    let magic = Magic.shared

    func logout() {
        guard let magic = magic else { return }

        // Assuming user is logged in
        let configuration = RecoverAccountConfiguration(email: "recovery_email@example.com")
        magic.user.recoverAccount(configuration, response: { response in
            guard let result = response.result
                else { return print("Error:", response.error.debugDescription) }
            print("Result", result)
        })
    }
}
```

## Error Handling

There are three types of error class to be aware of when working with Magic's iOS SDK:

### Provider

```swift Swift icon="swift" theme={null}
public enum ProviderError: Swift.Error {
  // The provider is not configured with an authDelegate
  case encodingFailed(Swift.Error?)
  // Decoding the JSON-RPC request failed
  case decodingFailed(json: String)
  // Convert string failed
  case invalidJsonResponse(json: String)
  // Missing callback
  case missingPayloadCallback(json: String)
}
```

### Network

```swift Swift icon="swift" theme={null}
public enum Error: Swift.Error {
  /// The response did not include expected results
  case unexpectedResponse(Swift.Error?)
  /// The server returned an unexpected response code
  case invalidResponseCode
}
```

### Events

```swift Swift icon="swift" theme={null}
enum Error: Swift.Error{
  case eventCallbackMissing
}
```

## Resources

* [GitHub](https://github.com/magiclabs/magic-ios)
* [Demo](https://github.com/magiclabs/magic-ios-demo)
* [Goerli Testnet Faucet](https://goerlifaucet.com)
