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

# Migrate the Swift SDK from 0.3 to 0.4

> Update an application from the OMS Wallet Swift SDK 0.3 to 0.4.

Version 0.4.0 of the Swift SDK changes how the active wallet and session are read, replaces the expiry observer with a session change observer, generates session keys in the Secure Enclave, and renames several parameters and types. This page covers the changes most applications need. The [full migration guide](https://github.com/0xsequence/swift-sdk/blob/master/MIGRATION.md) lists every change.

## Users sign in once after upgrading

Version 0.4.0 discards sessions saved by 0.3 on launch, so each user signs in once after the upgrade. On iOS, the new session's keychain items are readable only while the device is unlocked and are never restored to another device from a backup.

## Session keys are generated in the Secure Enclave

On an iOS device, the P-256 key that signs wallet API requests is generated in the Secure Enclave. It can only sign, only while the device is unlocked, and cannot be exported. There is no fallback to a software key: if the Secure Enclave cannot create the key, sign-in throws `.storageError`. The iOS Simulator and macOS keep using a non-extractable keychain key. Nothing needs configuring.

## Read the active wallet

`omsWallet.wallet.walletAddress` and `omsWallet.wallet.walletId` were removed. Read `omsWallet.wallet.activeWallet`, a `Wallet` or `nil` when signed out, and check its `type` before passing the address to family-specific code.

```swift theme={null}
// 0.3
let address = omsWallet.wallet.walletAddress

// 0.4
if let activeWallet = omsWallet.wallet.activeWallet, activeWallet.type == .ethereum {
    let ethereumAddress = activeWallet.address
}
```

Auth results no longer carry `walletAddress`. `CompleteAuthResult.walletSelected` is now `.walletSelected(wallet:wallets:claimableWallets:credential:)`; read `result.wallet?.address`.

```swift theme={null}
// 0.3
case .walletSelected(let walletAddress, let wallet, let wallets, let credential):

// 0.4
case .walletSelected(let wallet, let wallets, let claimableWallets, let credential):
    let walletAddress = wallet.address
```

## Check `session` for `nil`

`omsWallet.wallet.session` is now `OMSWalletSession?` (renamed from `OMSWalletSessionState`). In 0.3 it was a value whose fields were `nil` when signed out. `session` is non-`nil` exactly when `activeWallet` is, its `expiresAt` and `auth` are no longer optional, and its `walletAddress` was removed; use `activeWallet?.address`.

```swift theme={null}
// 0.3
let email = omsWallet.wallet.session.auth?.email

// 0.4
let email = omsWallet.wallet.session?.auth.email
```

`expiresAt`, and `expiredAt` on an expired session, are now ISO-8601 `String`s instead of `Date`s. Parse it when you need a `Date`; the wallet API may include fractional seconds.

## Replace `addSessionExpiredObserver` with `addSessionChangeObserver`

`addSessionChangeObserver` reports every change of the active wallet or session as an `OMSWalletSessionChangeEvent`: `.signedIn`, `.restored`, `.walletChanged`, `.expiryUpdated`, `.signedOut`, or `.expired`. `.expired` carries the ended session as an `OMSWalletExpiredSession`.

```swift theme={null}
// 0.3
let observation = omsWallet.wallet.addSessionExpiredObserver { event in showSignIn(event.session.auth) }

// 0.4
let observation = omsWallet.wallet.addSessionChangeObserver { change in
    if case .expired(let expired) = change {
        showSignIn(expired.session.auth)
    }
}
```

The method is no longer `@discardableResult`. Keep the returned observation: delivery stops when it is released. See [Sessions and access](https://docs.polygon.technology/wallets/sdk/swift/sessions-and-access.md).

## Renamed parameters and types

| 0.3 | 0.4 |
| - | - |
| `WalletSelectionResult` | `WalletActivationResult` |
| `callContract(network:contract:method:args:)` | `callContract(network:contractAddress:method:args:)`; `args` defaults to `nil` |
| `isValid…Signature(…walletAddress:…)` with `walletAddress` required | `walletAddress` is optional; omit it to verify against the active wallet of that type |
| `Network.polygonAmoy` | `Network.amoy` |
| `network.chainId` | `String(network.id)` |
| `parseUnits(value:)`, `formatUnits(value:)` | Pass `decimals:` explicitly; it no longer defaults to `18`. |

`startOIDCRedirectAuth` now takes `walletSelection` and `sessionLifetimeSeconds` before `loginHint` and `authorizeParams`. Calls that pass both groups must reorder their arguments.

`WalletType` gained `.tron`, `WalletImportPrivateKey` gained `.tron(_:)` and `.tronBytes(_:)`, and `OMSWalletOperation` and `OMSWalletErrorCode` gained cases. Exhaustive `switch` statements over them need the new cases.

Importing a key whose address is already imported now throws `.walletAddressAlreadyImported` instead of `.requestFailed`.

## Behavior changes

These need no code change to compile, but can change what your application sees:

* **Session expiry extends with use.** `session.expiresAt` is the current expiry, not a fixed end time. An idle session ends about 10 days after its last use, and never after the requested lifetime. The default lifetime is now 180 days instead of 7; pass `sessionLifetimeSeconds` for a hard limit. Observe `.expiryUpdated` instead of caching the sign-in expiry.
* **The expiry is checked when it passes.** When `expiresAt` passes, a session past its requested lifetime ends at once. Otherwise the SDK asks the wallet API, once the app is active, and reports `.expired` only if the session ended.
* **Signed requests time out after 30 seconds** with `.requestFailed` (`retryable == true`), counting the time spent waiting behind other signed requests, which are now sent one at a time. A transaction that times out after it was sent still throws `.transactionExecutionUnconfirmed`: check its status before sending it again.
* **Refused transactions fail as request errors.** A transaction the wallet API refuses before submitting it, for example because simulation shows it would revert, throws `.requestFailed` (or `.sessionExpired` when the session has ended) instead of `.transactionExecutionUnconfirmed`. It was never submitted, so there is nothing to check.
* **`callContract` takes a bare method name.** Pass `"transfer"`, not `"transfer(address,uint256)"`. A full signature now throws `.validationError` before any request.
* **Sign-in claims a claimable wallet.** With automatic selection, a user with no wallet of the requested type but a claimable one gets that wallet instead of a new one.
* **Instances of one project share one wallet.** Creating `OMSWallet` again for the same project and environment returns the same `wallet` and `indexer`, so signing out or switching wallets applies to both.
* **Raw string values changed.** `OMSWalletUpstreamService` raw values are now `"waas"` and `"indexer"`, and the OIDC redirect operations use `"wallet.startOidcRedirectAuth"` and `"wallet.handleOidcRedirectCallback"`. Check code that stores or compares these strings.
