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

# iOS Swift SDK

> Add the PNLight Swift SDK to a native iOS app with Swift Package Manager and initialize it at startup to track installs, send attribution, and render Remote UI.

Use the Swift SDK for native iOS apps.

## Requirements

* iOS 13.0+
* Swift 5.7+
* Swift Package Manager

## Install with Swift Package Manager

Package repository: [pnlight-dev/sdk-swift](https://github.com/pnlight-dev/sdk-swift).

[![Swift SDK version](https://img.shields.io/github/v/tag/pnlight-dev/sdk-swift?label=Swift%20SDK\&sort=semver)](https://github.com/pnlight-dev/sdk-swift/tags)

Add it in Xcode:

<Steps>
  <Step title="Open package settings">
    In Xcode, click **File** and then **Add Packages...**.
  </Step>

  <Step title="Enter the repository URL">
    Enter `https://github.com/pnlight-dev/sdk-swift.git`.
  </Step>

  <Step title="Add the package">
    Add the package to your app target.
  </Step>
</Steps>

## Initialize the SDK

Call initialization during app startup.

```swift theme={null}
import PNLightSDK

await PNLightSDK.shared.initialize(apiKey: "pnlight_sdk_token")
```

## Common calls

```swift theme={null}
let userId = PNLightSDK.shared.getUserId()

await PNLightSDK.shared.addAttribution(
    provider: .appsFlyer,
    data: ["af_status": "Non-organic"],
    identifier: "appsFlyerCustomerId"
)

await PNLightSDK.shared.logEvent("paywall_viewed", eventArgs: [
    "placement": "paywall"
])
```

`logEvent` sends optional in-app events to PNLight. You can review them in **Analytics → App Events**, on the **Events** page, and in customer timelines.

## IDFA after ATT

If PNLight initializes before the App Tracking Transparency prompt completes, call `updateIdfa()` after authorization so PNLight receives the granted IDFA.

```swift theme={null}
import AppTrackingTransparency
import PNLightSDK

await ATTrackingManager.requestTrackingAuthorization()
await PNLightSDK.shared.updateIdfa()
```

## Remote UI

Use `RemoteUiView` in SwiftUI when you want PNLight to render a configured placement.

```swift theme={null}
import PNLightSDK
import SwiftUI

struct PaywallScreen: View {
    var body: some View {
        RemoteUiView(placement: "paywall", cardId: "paywall_card") { action in
            if action.logId == "purchase_button" {
                let productId = action.params["id"] ?? ""
                Task {
                    let success = await purchase(productId)
                    if success {
                        showMainScreen()
                    }
                }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}
```

If you need to fetch the placement config yourself, use `getUIConfig`. On the first app launch, wait 4-5 seconds after AppsFlyer initialization when attribution is required. The SDK also waits for attribution internally when `attributionRequired` is `true`.

```swift theme={null}
try? await Task.sleep(nanoseconds: 5_000_000_000)

let config = await PNLightSDK.shared.getUIConfig(placement: "paywall")
```

Use `getUIConfigResult` when your app needs to tell the difference between no UI for a placement and a loading error.

```swift theme={null}
let result = await PNLightSDK.shared.getUIConfigResult(
    placement: "paywall",
    ignoreCache: true
)
```

See [Remote UI](/sdk/remote-ui) for placement and action guidance.
