> For the complete documentation index, see [llms.txt](https://popin.gitbook.io/popin-developer-hub/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://popin.gitbook.io/popin-developer-hub/popin-ios-sdk-v2-integration-documentation.md).

# Popin iOS SDK v2 – Integration Documentation

PopinCall is an iOS library that enables seamless integration of video calling functionality into your iOS applications. It provides an easy-to-use interface for connecting users with experts or support agents with robust real-time communication.

***

### Requirements

* iOS 15.0+
* Swift 5.0+
* Xcode 14.0+

***

### Installation

#### Swift Package Manager

Add PopinCall as a dependency in your `Package.swift`:

```swift
dependencies: [
    .package(url: "https://github.com/Springr-Creatives/Popin-Library-iOS.git", from: "1.0.0")
]
```

Or in Xcode:

1. Select **File > Add Package Dependencies...**
2. Enter the package URL: `https://github.com/Springr-Creatives/Popin-Library-iOS.git`
3. Select the version you want to use.

***

### Permissions

Add the following to your `Info.plist`:

```xml
<key>NSCameraUsageDescription</key>
<string>This app needs access to camera for video calls</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for video calls</string>
```

#### Background Modes

Enable the following Background Modes in your app's Signing & Capabilities:

* **Audio, AirPlay, and Picture in Picture** - For audio/video streaming during calls
* **Voice over IP** - For VoIP call handling
* **Background fetch** - For downloading content from the network

Or add directly to your `Info.plist`:

```xml
<key>UIBackgroundModes</key>
<array>
    <string>audio</string>
    <string>voip</string>
    <string>fetch</string>
</array>
```

***

### Quick Start

#### 1. Import the Library

```swift
import PopinCall
```

#### 2. Initialize with Configuration

```swift
// Product info to display during the call (optional)
let product = PopinProduct(
    id: "SKU-12345",
    name: "Wireless Headphones",
    image: "https://example.com/product.jpg",
    url: "https://example.com/products/headphones",
    description: "Noise-cancelling wireless headphones",
    extra: "$299.99"
)

// Custom metadata (optional)
let metadata: [String: String] = [
    "source": "ios_app",
    "version": "1.0.0",
    "campaign": "summer_sale"
]

// Build configuration
let config = PopinConfig.Builder()
    .userName("Demo User")
    .contactInfo("demo@example.com")
    .callerId("user-12345")
    .identifier("unique-user-id")
    .sandboxMode(true)
    .product(product)
    .meta(metadata)
    .secondaryProductText("Car details")
    .expertDesignation("Car Expert")
    .initListener(self)
    .eventsListener(self)
    .hideDisconnectButton(false)
    .hideScreenShareButton(false)
    .hideFlipCameraButton(false)
    .hideMuteVideoButton(false)
    .hideMuteAudioButton(false)
    .hideBackButton(false)
    .persistenceMode(true)
    .build()

// Initialize Popin
Popin.initialize(token: YOUR_SELLER_TOKEN, config: config)
```

#### 3. Start a Call

```swift
Popin.shared?.startCall()
```

#### 4. Implement Listeners

```swift
// MARK: - PopinInitListener

extension ViewController: PopinInitListener {
    func onInitComplete(userId: Int) {
        print("Popin initialized, userId: \(userId)")
    }

    func onInitFailed(reason: String) {
        print("Popin init failed: \(reason)")
    }
}

// MARK: - PopinEventsListener

extension ViewController: PopinEventsListener {
    func onPermissionGiven() {
        print("Permission given")
    }

    func onPermissionDenied() {
        print("Permission denied")
    }

    func onCallStart(callID: Int) {
        print("Call started, callID: \(callID)")
    }

    func onCallAbandoned() {
        print("Call abandoned")
    }

    func onQueuePositionChanged(position: Int) {
        print("Queue position: \(position)")
    }

    func onCallMissed() {
        print("Call missed")
    }

    func onCallNetworkFailure(participant: String) {
        print("Network failure — participant: \(participant)")
    }

    func onCallConnected() {
        print("Call connected")
    }

    func onCallFailed() {
        print("Call failed")
    }

    func onCallEnd() {
        print("Call ended")
    }
}
```

***

### Runtime Configuration Updates

```swift
let config = Popin.shared?.getConfig()
config?.product = PopinProduct(
    id: "NEW-SKU",
    name: "New Product",
    image: "https://example.com/new.jpg",
    url: "https://example.com/new",
    description: "Updated product",
    extra: "$199.99"
)
config?.callerId = "new_caller_id"
config?.meta = [
    "source": "ios_app",
    "campaign": "winter_sale"
]

Popin.shared?.startCall()
```

***

#### Setting User Identifier

```swift
Popin.shared?.setIdentifier("unique-user-id", onSuccess: {
    print("Identifier set")
}, onFailure: { error in
    print("Failed to set identifier: \(error)")
})
```

| Parameter    | Type               | Description                                    |
| ------------ | ------------------ | ---------------------------------------------- |
| `identifier` | `String`           | A unique identifier for the user               |
| `onSuccess`  | `() -> Void`       | Called when the identifier is set successfully |
| `onFailure`  | `(String) -> Void` | Called with an error message on failure        |

***

#### Updating User Name & Contact Info

```swift
Popin.shared?.updateUserInfo(
    name: "New Name",
    contactInfo: "new@email.com",
    onSuccess: {
        print("User info updated")
    },
    onFailure: { error in
        print("Failed to update: \(error)")
    }
)
```

| Parameter     | Type               | Description                             |
| ------------- | ------------------ | --------------------------------------- |
| `name`        | `String`           | Updated display name                    |
| `contactInfo` | `String`           | Updated email or phone number           |
| `onSuccess`   | `() -> Void`       | Called when the update succeeds         |
| `onFailure`   | `(String) -> Void` | Called with an error message on failure |

***

### Receiving Incoming Calls via PushKit

#### Step 1: Enable Incoming Calls

```swift
let config = PopinConfig.Builder()
    .enableIncomingCalls(true)
    .build()
```

#### Step 2: Initialize Popin in AppDelegate

```swift
import UIKit
import PopinCall

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let config = PopinConfig.Builder()
            .userName("Demo User")
            .contactInfo("demo@example.com")
            .enableIncomingCalls(true)
            .eventsListener(self)
            .build()

        Popin.initialize(token: YOUR_SELLER_TOKEN, config: config)
        return true
    }
}
```

#### Step 3: Register for VoIP Pushes Early

```swift
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Popin.registerForVoIPPushes()

    let config = PopinConfig.Builder()
        .build()
    Popin.initialize(token: YOUR_SELLER_TOKEN, config: config)
    return true
}
```

***

### API Reference

#### Popin

```swift
Popin.initialize(token: sellerToken, config: config)
Popin.shared?.startCall()
Popin.shared?.cancelCall()

Popin.shared?.setGroup(identifier: "group-id", onSuccess: {
    print("Group set successfully")
}, onFailure: { error in
    print("Failed to set group: \(error)")
})

Popin.shared?.setIdentifier("unique-user-id", onSuccess: {}, onFailure: { _ in })

Popin.shared?.updateUserInfo(name: "New Name", contactInfo: "new@email.com", onSuccess: {}, onFailure: { _ in })

Popin.shared?.getCallMeta(callId: 123, onSuccess: { json in
    print("Call meta: \(json)")
}, onFailure: { error in
    print("Failed: \(error)")
})

let config = Popin.shared?.getConfig()

Popin.registerForVoIPPushes()

Popin.deinitialize()
```

***

#### getCallMeta

```swift
Popin.shared?.getCallMeta(callId: 123, onSuccess: { json in
    print("Call meta: \(json)")
}, onFailure: { error in
    print("Failed: \(error)")
})
```

| Parameter   | Type               | Description                              |
| ----------- | ------------------ | ---------------------------------------- |
| `callId`    | `Int`              | The call ID                              |
| `onSuccess` | `(String) -> Void` | Called with the raw JSON response string |
| `onFailure` | `(String) -> Void` | Called with an error message on failure  |

***

#### deinitialize

```swift
Popin.deinitialize()
```

***

#### setGroup

```swift
Popin.shared?.setGroup(identifier: "group-abc-123", onSuccess: {
    print("Group set successfully")
}, onFailure: { error in
    print("Failed to set group: \(error)")
})
```

| Parameter    | Type               | Description            |
| ------------ | ------------------ | ---------------------- |
| `identifier` | `String`           | The group ID           |
| `onSuccess`  | `() -> Void`       | Called when successful |
| `onFailure`  | `(String) -> Void` | Called with error      |

***

#### setIdentifier

```swift
Popin.shared?.setIdentifier("unique-user-id", onSuccess: {
    print("Identifier set")
}, onFailure: { error in
    print("Failed: \(error)")
})
```

***

#### PopinConfig.Builder

| Method                                 | Default             | Description              |
| -------------------------------------- | ------------------- | ------------------------ |
| `.userName(String)`                    | `""`                | User's display name      |
| `.contactInfo(String)`                 | `""`                | User's contact info      |
| `.callerId(String)`                    | `nil`               | Custom caller identifier |
| `.identifier(String)`                  | `nil`               | Unique user identifier   |
| `.sandboxMode(Bool)`                   | `false`             | Use sandbox              |
| `.product(PopinProduct)`               | `nil`               | Product context          |
| `.meta([String: String])`              | `[:]`               | Metadata                 |
| `.secondaryProductText(String)`        | `"Product details"` | UI label                 |
| `.expertDesignation(String)`           | `"Product expert"`  | Role label               |
| `.initListener(PopinInitListener)`     | `nil`               | Init listener            |
| `.eventsListener(PopinEventsListener)` | `nil`               | Events listener          |
| `.hideDisconnectButton(Bool)`          | `false`             | Hide end call            |
| `.hideScreenShareButton(Bool)`         | `false`             | Hide screen share        |
| `.hideFlipCameraButton(Bool)`          | `true`              | Hide flip camera         |
| `.hideMuteVideoButton(Bool)`           | `false`             | Hide video toggle        |
| `.hideMuteAudioButton(Bool)`           | `false`             | Hide mic toggle          |
| `.hideBackButton(Bool)`                | `false`             | Hide back button         |
| `.persistenceMode(Bool)`               | `true`              | Maintain session         |
| `.enableIncomingCalls(Bool)`           | `false`             | Enable incoming calls    |

***

#### PopinProduct

```swift
PopinProduct(
    id: "SKU-123",
    name: "Product Name",
    image: "https://...",
    url: "https://...",
    description: "Description",
    extra: "$99.99"
)
```

***

#### PopinInitListener

| Method                    | Description              |
| ------------------------- | ------------------------ |
| `onInitComplete(userId:)` | Initialization succeeded |
| `onInitFailed(reason:)`   | Initialization failed    |

***

#### PopinEventsListener

| Method                                | Description         |
| ------------------------------------- | ------------------- |
| `onPermissionGiven()`                 | Permissions granted |
| `onPermissionDenied()`                | Permissions denied  |
| `onCallStart(callID:)`                | Call initiated      |
| `onCallAbandoned()`                   | Call abandoned      |
| `onQueuePositionChanged(position:)`   | Queue updated       |
| `onCallMissed()`                      | Call missed         |
| `onCallNetworkFailure(participant:)`  | Network error       |
| `onCallConnected()`                   | Call active         |
| `onCallFailed()`                      | Call failed         |
| `onCallEnd()`                         | Call ended          |
| `onPipStateChanged(isPipModeActive:)` | PiP state changed   |
