Skip to content

Commit afd37c6

Browse files
authored
feat: add support for surveys on iOS (#325)
* feat: load remote config and feature flags (#283) * feat: load remote config and feature flags * fix: cleanup * fix: redundant assignment * fix: hasFeatureFlags * fix: lint * feat: add survey domain models (#284) * feat: add survey domain models * fix: reuse date formatter * fix: improve test fixtures and model consistency * Merge branch 'main' into feat/mobile-surveys-merge-main (#313) * chore: session replay ga (#286) * fix: xcframework builds (#288) * fix: xcframework builds * fix: import access level * feat: add sample xcframework project * fix: build examples * chore: update CHANGELOG * Update version * chore: skip creating a visionOS simulator if not needed (#289) * chore: skip creating a visionOS simulator if not needed * fix: external sdk example * Update version * feat: add survey domain models * Merge branch 'main' into feat/mobile-surveys-merge-main # Conflicts: # CHANGELOG.md # Makefile # PostHog.podspec # PostHog.xcodeproj/project.pbxproj # PostHog/PostHogApi.swift # PostHog/PostHogSDK.swift # PostHog/PostHogVersion.swift # PostHogTests/PostHogFeatureFlagsTest.swift # PostHogTests/PostHogSessionManagerTest.swift # PostHogTests/TestUtils/MockApplicationLifecyclePublisher.swift # PostHogTests/TestUtils/TestPostHog.swift * fix: lint * feat: add base survey integration (#314) * feat: add survey domain models * Merge branch 'main' into feat/mobile-surveys-merge-main # Conflicts: # CHANGELOG.md # Makefile # PostHog.podspec # PostHog.xcodeproj/project.pbxproj # PostHog/PostHogApi.swift # PostHog/PostHogSDK.swift # PostHog/PostHogVersion.swift # PostHogTests/PostHogFeatureFlagsTest.swift # PostHogTests/PostHogSessionManagerTest.swift # PostHogTests/TestUtils/MockApplicationLifecyclePublisher.swift # PostHogTests/TestUtils/TestPostHog.swift * feat: base surveys integration * feat: base survey display * fix: builds * fix: test * fix: tests * fix: remote config tests * feat: add survey UI and display logic (#320) * feat: add survey domain models * Merge branch 'main' into feat/mobile-surveys-merge-main # Conflicts: # CHANGELOG.md # Makefile # PostHog.podspec # PostHog.xcodeproj/project.pbxproj # PostHog/PostHogApi.swift # PostHog/PostHogSDK.swift # PostHog/PostHogVersion.swift # PostHogTests/PostHogFeatureFlagsTest.swift # PostHogTests/PostHogSessionManagerTest.swift # PostHogTests/TestUtils/MockApplicationLifecyclePublisher.swift # PostHogTests/TestUtils/TestPostHog.swift * feat: base surveys integration * feat: base survey display * fix: builds * fix: test * fix: tests * fix: remote config tests * feat: add survey ui components * feat: survey question type display and next question logic * feat: send survey seen, sent, dismissed events * feat: add event-activated surveys * fix: empty question description * fix: hit test on iOS 18 * fix: safe area inset * refactor: move display related to logic to display manager * fix: handle case displayThankYouMessage is false * fix: tests * fix: handle optional questions * fix: build * feat: public api * chore: update CHANGELOG * feat: iOS15 presentation detents (#324) * feat: add survey domain models * Merge branch 'main' into feat/mobile-surveys-merge-main # Conflicts: # CHANGELOG.md # Makefile # PostHog.podspec # PostHog.xcodeproj/project.pbxproj # PostHog/PostHogApi.swift # PostHog/PostHogSDK.swift # PostHog/PostHogVersion.swift # PostHogTests/PostHogFeatureFlagsTest.swift # PostHogTests/PostHogSessionManagerTest.swift # PostHogTests/TestUtils/MockApplicationLifecyclePublisher.swift # PostHogTests/TestUtils/TestPostHog.swift * feat: base surveys integration * feat: base survey display * fix: builds * fix: test * fix: tests * fix: remote config tests * feat: add survey ui components * feat: survey question type display and next question logic * feat: send survey seen, sent, dismissed events * feat: add event-activated surveys * fix: empty question description * fix: hit test on iOS 18 * fix: safe area inset * refactor: move display related to logic to display manager * fix: handle case displayThankYouMessage is false * fix: tests * feat: add backport for iOS 15 presentation detents * fix: lint * fix: sheet height * fix: remove ViewBuilder attribute * feat: add configuration option for remoteConfig * chore: prefix survey models * fix: cache needed integrations in sdk instance * chore: split survey models to multiple files * chore: refactor remote config callback
1 parent 889f89a commit afd37c6

File tree

72 files changed

+9598
-592
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+9598
-592
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Next
22

3+
- feat: add support for surveys on iOS ([#325](https://github.com/PostHog/posthog-ios/pull/325))
4+
35
## 3.21.0 - 2025-03-28
46

57
- fix: visionOS builds ([#291](https://github.com/PostHog/posthog-ios/pull/291))

PostHog.xcodeproj/project.pbxproj

+242-12
Large diffs are not rendered by default.

PostHog.xcodeproj/xcshareddata/xcschemes/PostHog.xcscheme

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
shouldUseLaunchSchemeArgsEnv = "YES">
3030
<Testables>
3131
<TestableReference
32-
skipped = "NO">
32+
skipped = "NO"
33+
parallelizable = "NO">
3334
<BuildableReference
3435
BuildableIdentifier = "primary"
3536
BlueprintIdentifier = "3AC745BE296D6FE60025C109"
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// PostHogSurvey.swift
3+
// PostHog
4+
//
5+
// Created by Yiannis Josephides on 20/01/2025.
6+
//
7+
8+
import Foundation
9+
10+
/// Represents the main survey object containing metadata, questions, conditions, and appearance settings.
11+
/// see: posthog-js/posthog-surveys-types.ts
12+
struct PostHogSurvey: Decodable, Identifiable {
13+
/// The unique identifier for the survey
14+
let id: String
15+
/// The name of the survey
16+
let name: String
17+
/// Type of the survey (e.g., "popover")
18+
let type: PostHogSurveyType
19+
/// The questions asked in the survey
20+
let questions: [PostHogSurveyQuestion]
21+
/// Multiple feature flag keys. Must all (AND) evaluate to true for the survey to be shown (optional)
22+
let featureFlagKeys: [PostHogSurveyFeatureFlagKeyValue]?
23+
/// Linked feature flag key. Must evaluate to true for the survey to be shown (optional)
24+
let linkedFlagKey: String?
25+
/// Targeting feature flag key. Must evaluate to true for the survey to be shown (optional)
26+
let targetingFlagKey: String?
27+
/// Internal targeting flag key. Must evaluate to true for the survey to be shown (optional)
28+
let internalTargetingFlagKey: String?
29+
/// Conditions for displaying the survey (optional)
30+
let conditions: PostHogSurveyConditions?
31+
/// Appearance settings for the survey (optional)
32+
let appearance: PostHogSurveyAppearance?
33+
/// The iteration number for the survey (optional)
34+
let currentIteration: Int?
35+
/// The start date for the current iteration of the survey (optional)
36+
let currentIterationStartDate: Date?
37+
/// Start date of the survey (optional)
38+
let startDate: Date?
39+
/// End date of the survey (optional)
40+
let endDate: Date?
41+
}
42+
43+
struct PostHogSurveyFeatureFlagKeyValue: Equatable, Decodable {
44+
let key: String
45+
let value: String?
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// PostHogSurveyAppearance.swift
3+
// PostHog
4+
//
5+
// Created by Ioannis Josephides on 08/04/2025.
6+
//
7+
8+
import Foundation
9+
10+
/// Represents the appearance settings for the survey, such as colors, fonts, and layout
11+
struct PostHogSurveyAppearance: Decodable {
12+
public let position: PostHogSurveyAppearancePosition?
13+
public let fontFamily: String?
14+
public let backgroundColor: String?
15+
public let submitButtonColor: String?
16+
public let submitButtonText: String?
17+
public let submitButtonTextColor: String?
18+
public let descriptionTextColor: String?
19+
public let ratingButtonColor: String?
20+
public let ratingButtonActiveColor: String?
21+
public let ratingButtonHoverColor: String?
22+
public let whiteLabel: Bool?
23+
public let autoDisappear: Bool?
24+
public let displayThankYouMessage: Bool?
25+
public let thankYouMessageHeader: String?
26+
public let thankYouMessageDescription: String?
27+
public let thankYouMessageDescriptionContentType: PostHogSurveyTextContentType?
28+
public let thankYouMessageCloseButtonText: String?
29+
public let borderColor: String?
30+
public let placeholder: String?
31+
public let shuffleQuestions: Bool?
32+
public let surveyPopupDelaySeconds: TimeInterval?
33+
// widget options
34+
public let widgetType: PostHogSurveyAppearanceWidgetType?
35+
public let widgetSelector: String?
36+
public let widgetLabel: String?
37+
public let widgetColor: String?
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//
2+
// PostHogSurveyConditions.swift
3+
// PostHog
4+
//
5+
// Created by Ioannis Josephides on 08/04/2025.
6+
//
7+
8+
import Foundation
9+
10+
/// Represents conditions for displaying the survey, such as URL or event-based triggers
11+
struct PostHogSurveyConditions: Decodable {
12+
/// Target URL for the survey (optional)
13+
let url: String?
14+
/// The match type for the url condition (optional)
15+
let urlMatchType: PostHogSurveyMatchType?
16+
/// CSS selector for displaying the survey (optional)
17+
let selector: String?
18+
/// Device type based conditions for displaying the survey (optional)
19+
let deviceTypes: [String]?
20+
/// The match type for the device type condition (optional)
21+
let deviceTypesMatchType: PostHogSurveyMatchType?
22+
/// Minimum wait period before showing the survey again (optional)
23+
let seenSurveyWaitPeriodInDays: Int?
24+
/// Event-based conditions for displaying the survey (optional)
25+
let events: PostHogSurveyEventConditions?
26+
/// Action-based conditions for displaying the survey (optional)
27+
let actions: PostHogSurveyActionsConditions?
28+
}
29+
30+
/// Represents event-based conditions for displaying the survey
31+
struct PostHogSurveyEventConditions: Decodable {
32+
public let repeatedActivation: Bool?
33+
/// List of events that trigger the survey
34+
public let values: [PostHogEventCondition]
35+
}
36+
37+
/// Represents action-based conditions for displaying the survey
38+
struct PostHogSurveyActionsConditions: Decodable {
39+
/// List of events that trigger the survey
40+
public let values: [PostHogEventCondition]
41+
}
42+
43+
/// Represents a single event condition used in survey targeting
44+
struct PostHogEventCondition: Decodable, Equatable {
45+
/// Name of the event (e.g., "content loaded")
46+
public let name: String
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// PostHogSurveyEnums.swift
3+
// PostHog
4+
//
5+
// Created by Ioannis Josephides on 08/04/2025.
6+
//
7+
8+
import Foundation
9+
10+
// MARK: - Supporting Types
11+
12+
enum PostHogSurveyType: String, Decodable {
13+
case popover, api, widget
14+
}
15+
16+
enum PostHogSurveyQuestionType: String, Decodable {
17+
case open
18+
case link
19+
case rating
20+
case multipleChoice = "multiple_choice"
21+
case singleChoice = "single_choice"
22+
}
23+
24+
enum PostHogSurveyTextContentType: String, Decodable {
25+
case html, text
26+
}
27+
28+
enum PostHogSurveyMatchType: String, Decodable {
29+
case regex
30+
case notRegex = "not_regex"
31+
case exact
32+
case isNot = "is_not"
33+
case iContains = "icontains"
34+
case notIContains = "not_icontains"
35+
}
36+
37+
enum PostHogSurveyAppearancePosition: String, Decodable {
38+
case left, right, center
39+
}
40+
41+
enum PostHogSurveyAppearanceWidgetType: String, Decodable {
42+
case button, tab, selector
43+
}
44+
45+
enum PostHogSurveyRatingDisplayType: String, Decodable {
46+
case number, emoji
47+
}
48+
49+
enum PostHogSurveyQuestionBranchingType: String, Decodable {
50+
case nextQuestion = "next_question"
51+
case end
52+
case responseBased = "response_based"
53+
case specificQuestion = "specific_question"
54+
}
55+
56+
enum PostHogSurveyResponse {
57+
case link(String)
58+
case rating(Int?)
59+
case openEnded(String?)
60+
case singleChoice(String?)
61+
case multipleChoice([String]?)
62+
}

0 commit comments

Comments
 (0)