You control when and where to present studies inside your app.
Basic usage
Present a study by its ID. You can find the ID in the Integration tab of your study settings.
PillowSDK.shared.present(
study: PillowStudy(id: "your-study-id-here")
)
Session resumption
By default, the SDK remembers where a user left off. If a user partially completes a study and you call present(study:) again with the same ID, they resume from where they stopped.
Start a fresh session
To force a new conversation instead of resuming, pass presentation options with forceFreshSession set to true:
PillowSDK.shared.present(
study: PillowStudy(id: "your-study-id-here"),
options: PillowStudyPresentationOptions(
forceFreshSession: true,
skipIfAlreadyExposed: false
)
)
This clears the stored session for that study and starts a new conversation from the beginning.
Skip repeat exposure
To ask the backend to no-op when the current SDK user was already exposed to the same study, pass presentation options:
PillowSDK.shared.present(
study: PillowStudy(id: "your-study-id-here"),
options: PillowStudyPresentationOptions(
forceFreshSession: false,
skipIfAlreadyExposed: true
)
)
skipIfAlreadyExposed defaults to false, so existing calls keep presenting unless you opt in.
Monitor lifecycle
Pass a delegate to present(study:delegate:) to receive lifecycle callbacks:
class MyStudyDelegate: PillowStudyDelegate {
func studyDidPresent(_ study: PillowStudy) {
print("Study appeared on screen")
}
func studyDidSkip(_ study: PillowStudy) {
print("Study was skipped")
}
func studyDidFinish(_ study: PillowStudy) {
print("User finished the study")
}
func studyDidFailToLoad(_ study: PillowStudy, error: Error) {
print("Study failed: \(error.localizedDescription)")
}
}
let studyDelegate = MyStudyDelegate()
PillowSDK.shared.present(
study: PillowStudy(id: "your-study-id-here"),
delegate: studyDelegate
)
Retain your delegate for as long as you need callbacks. The SDK keeps only a weak reference to avoid retain cycles.
In SwiftUI, the common pattern is to retain a small coordinator object in @State or @StateObject and pass that object as the delegate.
import SwiftUI
import PillowSDK
struct ContentView: View {
@State private var studyCoordinator: StudyPresentationCoordinator?
var body: some View {
Button("Start feedback") {
let coordinator = StudyPresentationCoordinator(
onFinished: { _ in
studyCoordinator = nil
},
onFailed: { _, error in
print(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 onFinished: (PillowStudy) -> Void
private let onFailed: (PillowStudy, Error) -> Void
init(
onFinished: @escaping (PillowStudy) -> Void = { _ in },
onFailed: @escaping (PillowStudy, Error) -> Void = { _, _ in }
) {
self.onFinished = onFinished
self.onFailed = onFailed
}
func studyDidFinish(_ study: PillowStudy) {
Task { @MainActor in
onFinished(study)
}
}
func studyDidFailToLoad(_ study: PillowStudy, error: Error) {
Task { @MainActor in
onFailed(study, error)
}
}
}
| Method | Description |
|---|
studyDidPresent(_:) | The study modal appeared on screen |
studyDidSkip(_:) | The study was intentionally skipped and not presented |
studyDidFinish(_:) | The user finished or dismissed the study |
studyDidFailToLoad(_:error:) | The study could not be loaded or presented. Access error.localizedDescription for the cause — for example, a network error or another study is already being shown |
All delegate methods are invoked on the main thread, so you can safely update your UI from them. Implement the methods you need.
Use the delegate to track study engagement, trigger follow-up actions after a conversation, or show a fallback if the study fails to load.
Best practices
- Call
setExternalId() before presenting so the study is linked to the right user
- Copy the study ID from the Integration tab in your study settings
- Test in test mode first — test conversations don’t appear in your dashboard data
Make sure the study is set to live mode before presenting it to real users. Studies in test mode work for testing but data won’t appear in your production dashboard.