Skip to main content

Overview

If your app previously used Privy’s Swift 1.Y.Z SDK, follow the migration guide below to upgrade to 2.0. Privy’s 2.Y.Z Swift SDK is Swift 6 compliant and adhere’s to the new strict concurrency standards. We’ve taken this opportunity to also introduce some major API changes, which are outlined below in logical sections.

Initialization

1. App client ID required at initialization

Previously, apps were only required to pass in an appId when initializing the Privy SDK. Now, appClientId is required too. You can retrieve more information on how to retrieve your appClientId here.
Initializing the Privy SDK

2. PrivySdk.initialize can only be called once

It’s important to use a single instance of Privy across the lifetime of your application. Calling PrivySdk.initialize multiple times will result in a fatal error.

3. Privy.awaitReady()

When the Privy SDK is first initialized, the user’s authentication state will be set to notReady until Privy finishes initialization. We’ve added an async privy.awaitReady() function that allows you to await initialization completion. During this time, we suggest you show a loading state to your user.
Calling PrivySDK functions before calling privy.awaitReady() might result in unexpected functionality.
Here’s an example with some pseudocode:

Authentication

AuthState

The AuthState represents the authentication state of your user.

Accessing AuthState

The current auth state can be accessed any time via privy.authState.

Subscribing to AuthState updates

Auth state updates are exposed via privy.authStateStream, which is an AsyncStream.

The PrivyUser

After authenticating a user via any login method, you will receive the PrivyUser object. The PrivyUser represents an authenticated user. All user specific actions, such as creating a wallet or retrieving the user’s access token, are accessed via the PrivyUser. You may retrieve the PrivyUser anytime by calling privy.user. If this value is non-null, there is an authenticated user. If the value is null, there is no authenticated user. The PrivyUser can also be retrieved via the associated type of the “authenticated” auth state:
Use the PrivyUser object to:
  • Get the user’s ID
  • Get the user’s identity token
  • Get the user’s access token
  • Get the user’s linked accounts
  • Get the user’s embedded Ethereum wallets
  • Get the user’s embedded Solana wallets
  • Create an embedded Ethereum wallet
  • Create an embedded Solana wallet
  • Refresh the user
  • Log the user out

Miscellaneous

Errors

We’ve significantly enhanced our error handling. When an SDK function throws an error, it will be a PrivyError, which contains an errorCode and a localizedDescription.

AuthSession

The AuthSession is no longer exposed. Values previously available in AuthSession are now available through different methods:
  • PrivyUser: can be accessed via privy.user as described above.
  • accessToken: can be accessed via privy.user.getAccessToken(). This method will return the user’s access token, refreshing the session if needed.

Session Refresh

To refresh / update a PrivyUser, you’d previously call privy.refreshSession. Now, trigger the refresh via the PrivyUser, specifically, privyUser.refresh.

Logout

To logout an authenticated user, call await privyUser.logout() instead of privy.logout(). Once calling logout, the PrivyUser instance is no longer valid.

Linked Accounts

  • LinkedAccount and its associated types are no longer Codable, Hashable, Identifiable or Equatable.
  • LinkedAccount.embeddedWallet is now split into chain specific values - LinkedAccount.embeddedEthereumWallet and LinkedAccount.embeddedSolanaWallet
  • The chainId property on the embedded wallet linked accounts is removed.
  • firstVerifiedAt and latestVerifiedAt fields are now optional values.

Other type changes

  • firstVerifiedAt, latestVerifiedAt, and createdAt fields now have type Date instead of TimeInterval or Int
  • verifiedAt fields now replaced with firstVerifiedAt and latestVerifiedAt
  • LoginMethod is no longer Equatable

Login with SMS

  • The OtpFlowState enum is no longer exposed. You should manually handle state management based on function results. For example, if LoginWithSms.loginWithCode throws an error, you can catch the error and update your UI accordingly.
  • LoginWithSms.sendCode now throws an error if sending code is unsuccessful, instead of returning false
  • LoginWithSms.loginWithCode now returns PrivyUser
  • LoginWithSms.loginWithCode now requires phone number to be passed in as a parameter (previously was optional)

Login with email

  • The OtpFlowState enum is no longer exposed. You should manually handle state management based on function results. For example, if LoginWithEmail.loginWithCode throws an error, you can catch the error and update your UI accordingly.
  • LoginWithEmail.sendCode now throws an error if sending code is unsuccessful, instead of returning false
  • LoginWithEmail.linkWithCode no longer returns anything, and throws an error if linking fails
  • LoginWithEmail.loginWithCode now returns PrivyUser
  • LoginWithEmail.loginWithCode now requires email to be passed in (no longer optional)

Login with custom auth

  • LoginWithCustomAccessToken.loginWithCustomAccessToken now returns PrivyUser
  • When initializing the PrivySDK, you should now pass the TokenProvider through the PrivyLoginWithCustomAuthConfig field in the PrivyConfig object. This allows Privy to access your user’s access token at initialization while attempting to restore the Privy user’s session.

Login with SIWE

  • SiweFlowState enum is no longer exposed. You should manually handle state management based on function results. For example, if LoginWithSiwe.loginWithSiwe throws an error, you can catch the error and update your UI accordingly.
  • LoginWithSiwe.loginWithSiwe now returns PrivyUser
  • LoginWithSiwe.loginWithSiwe now requires message and params to be passed in (no longer optional)
  • LoginWithSiwe.linkWithSiwe no long returns anything, and throws an error if linking fails
  • LoginWithSiwe.linkWithSiwe now requires message and params to be passed in (no longer optional)

Login with OAuth

  • LoginWithOAuth.login now returns PrivyUser

Embedded wallets

Overview

Previously, all embedded wallet APIs were accessible directly from the privy.embeddedWallet object, which is no longer available. All embedded wallet APIs are now available via the PrivyUser instead. This is because all embedded wallet actions require an authenticated user, so adding the methods inside the authenticated PrivyUser was the most logical. As an example, when creating an Ethereum wallet:

Connecting the wallet

In SDK 1.Y.Z, you had to ensure wallets were connected prior to accessing them by calling privy.connectWallet(). This method is now removed as we handle connected state internally! You may access the user’s embedded wallets at anytime, without ensuring “wallet connected” state. Because you no longer need to manage wallet state, EmbeddedWalletState has been removed.

Ethereum vs Solana

Now, all embedded wallet APIs are chain specific and available via the PrivyUser.

Creating a wallet

Ethereum:
Solana:

Retrieving a wallet

Ethereum:
Solana:

Using a wallet / rpc providers

Instead of grabbing the wallet’s provider via try privy.embeddedWallet.getEthereumProvider(for: wallet.address), the provider is now available directly on the wallet instance. For example: Ethereum:
Further, switching and retrieving the EVM chain is now an async operation:
Solana:

Changing the EVM chain

When utilizing the EmbeddedEthereumWalletProvider, you may specify the EVM Chain by calling provider.switchChain. This was previously named provider.configure.