Skip to main content
Get the Pillow SDK running in your iOS app in a few minutes.

Prerequisites

  • iOS 16.0+
  • A publishable API key — see Apps and API keys to create one
  • A study set to live mode — copy its ID from the Integration tab

Installation

In Xcode, go to File > Add Package Dependencies and enter the repository URL:
https://github.com/trypillow/pillow-ios-sdk.git
Select version 0.1.3 or later, then add PillowSDK to your target. You only need to add the Swift Package and import PillowSDK in your app code. Do not copy any SDK source files into your project.

Initialize the SDK

Call initialize() once at app startup, typically in your AppDelegate or app entry point:
import PillowSDK

PillowSDK.shared.initialize(publishableKey: "pk_live_...")
Initialize the SDK once during app startup and avoid calling it repeatedly from view code.

Present your first study

Show a study to the user by calling present(study:):
PillowSDK.shared.present(
    study: PillowStudy(id: "your-study-id-here")
)
The study opens as a modal overlay. The user can complete the conversation and dismiss it when done.

Enable microphone (optional)

If your study uses voice input, add the microphone usage description to your Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>This app uses the microphone for voice-based research conversations.</string>
If you skip this step, the microphone button won’t appear in studies that support voice input.

Full example (SwiftUI)

import SwiftUI
import PillowSDK

@main
struct MyApp: App {
    init() {
        PillowSDK.shared.initialize(publishableKey: "pk_live_...")
        PillowSDK.shared.setExternalId(externalId: "user_123")
        PillowSDK.shared.setUserProperty(key: "plan", stringValue: "pro")
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State private var studyCoordinator: StudyPresentationCoordinator?
    @State private var statusMessage = "Ready"

    var body: some View {
        VStack(spacing: 16) {
            Text(statusMessage)

            Button("Start feedback") {
                presentStudy()
            }
        }
    }

    private func presentStudy() {
        let coordinator = StudyPresentationCoordinator(
            onPresented: { study in
                statusMessage = "Study \(study.id) is on screen"
            },
            onFinished: { study in
                statusMessage = "Study \(study.id) finished"
                studyCoordinator = nil
            },
            onFailed: { study, error in
                statusMessage = "Failed to load \(study.id): \(error.localizedDescription)"
                studyCoordinator = nil
            }
        )

        studyCoordinator = coordinator
        PillowSDK.shared.present(
            study: PillowStudy(id: "your-study-id-here"),
            delegate: coordinator
        )
    }
}

private final class StudyPresentationCoordinator: PillowStudyDelegate {
    private let onPresented: (PillowStudy) -> Void
    private let onFinished: (PillowStudy) -> Void
    private let onFailed: (PillowStudy, Error) -> Void

    init(
        onPresented: @escaping (PillowStudy) -> Void = { _ in },
        onFinished: @escaping (PillowStudy) -> Void = { _ in },
        onFailed: @escaping (PillowStudy, Error) -> Void = { _, _ in }
    ) {
        self.onPresented = onPresented
        self.onFinished = onFinished
        self.onFailed = onFailed
    }

    func studyDidPresent(_ study: PillowStudy) {
        Task { @MainActor in
            onPresented(study)
        }
    }

    func studyDidFinish(_ study: PillowStudy) {
        Task { @MainActor in
            onFinished(study)
        }
    }

    func studyDidFailToLoad(_ study: PillowStudy, error: Error) {
        Task { @MainActor in
            onFailed(study, error)
        }
    }
}
If you do not need lifecycle callbacks, you can call present(study:) directly without a delegate.

Full example (UIKit)

import UIKit
import PillowSDK

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        PillowSDK.shared.initialize(publishableKey: "pk_live_...")
        PillowSDK.shared.setExternalId(externalId: "user_123")
    }

    @IBAction func startStudy() {
        PillowSDK.shared.present(
            study: PillowStudy(id: "your-study-id-here")
        )
    }
}

What’s next?

Identify users

Set external IDs and user properties.

Present studies

Control when and how studies appear.

Session management

Handle logout and session lifecycle.