Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.cocoapods.example.NotificationServiceExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -508,7 +508,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.cocoapods.example.NotificationServiceExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -654,7 +654,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.cocoapods.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down Expand Up @@ -687,7 +687,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.cocoapods.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -705,7 +705,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = G3793W2RJ2;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.cocoapods.example.CocoapodsExampleUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
Expand All @@ -723,7 +723,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = G3793W2RJ2;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.cocoapods.example.CocoapodsExampleUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
Expand Down
8 changes: 4 additions & 4 deletions Examples/KlaviyoSwiftExamples/CocoapodsExample/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ platform :ios, '13.0'
use_frameworks!

target 'CocoapodsExample' do
pod 'KlaviyoSwift', '5.2.2'
pod 'KlaviyoForms', '5.2.2'
pod 'KlaviyoLocation', '5.2.2'
pod 'KlaviyoSwift', '5.3.0'
pod 'KlaviyoForms', '5.3.0'
pod 'KlaviyoLocation', '5.3.0'
end

target 'NotificationServiceExtension' do
pod 'KlaviyoSwiftExtension', '5.2.2'
pod 'KlaviyoSwiftExtension', '5.3.0'
end
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.spm.example.SPMExample.NotificationServiceExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand All @@ -458,7 +458,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.spm.example.SPMExample.NotificationServiceExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -609,7 +609,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.spm.example.SPMExample;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -641,7 +641,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.spm.example.SPMExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -659,7 +659,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.spm.example.SPMExampleTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
Expand All @@ -677,7 +677,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.spm.example.SPMExampleTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
Expand All @@ -693,7 +693,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.spm.example.SPMExampleUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
Expand All @@ -709,7 +709,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 5.2.2;
MARKETING_VERSION = 5.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.klaviyo.spm.example.SPMExampleUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
Expand Down
2 changes: 1 addition & 1 deletion KlaviyoCore.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "KlaviyoCore"
s.version = "5.2.3"
s.version = "5.3.0"
s.summary = "Core functionalities for the Klaviyo SDK"
s.description = <<-DESC
Core functionalities and utilities for the Klaviyo SDK.
Expand Down
4 changes: 2 additions & 2 deletions KlaviyoForms.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "KlaviyoForms"
s.version = "5.2.3"
s.version = "5.3.0"
s.summary = "Klaviyo forms is a new way to engage with your app users"
s.description = <<-DESC
Use Klaviyo forms to include in app forms in your app and engage user with marketing content
Expand All @@ -19,5 +19,5 @@ Pod::Spec.new do |s|
]
}
s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS' => '-package-name KlaviyoSwift -package-name KlaviyoCore' }
s.dependency 'KlaviyoSwift', '~> 5.2.3'
s.dependency 'KlaviyoSwift', '~> 5.3.0'
end
4 changes: 2 additions & 2 deletions KlaviyoLocation.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "KlaviyoLocation"
s.version = "5.2.3"
s.version = "5.3.0"
s.summary = "Location services and geofencing for the Klaviyo SDK"
s.description = <<-DESC
Use KlaviyoLocation to enable location-based tracking and geofencing capabilities in your iOS applications.
Expand All @@ -14,6 +14,6 @@ Pod::Spec.new do |s|
s.platform = :ios, '13.0'
s.source_files = 'Sources/KlaviyoLocation/**/*.swift'
s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS' => '-package-name KlaviyoLocation -package-name KlaviyoSwift -package-name KlaviyoCore' }
s.dependency 'KlaviyoSwift', '~> 5.2.3'
s.dependency 'KlaviyoSwift', '~> 5.3.0'
s.frameworks = 'CoreLocation'
end
4 changes: 2 additions & 2 deletions KlaviyoSwift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "KlaviyoSwift"
s.version = "5.2.3"
s.version = "5.3.0"
s.summary = "Incorporate Klaviyo's event and person tracking and push notifications functionality into iOS applications"

s.description = <<-DESC
Expand All @@ -17,6 +17,6 @@ Pod::Spec.new do |s|
s.source_files = 'Sources/KlaviyoSwift/**/*.swift'
s.resource_bundles = {"KlaviyoSwift" => ["Sources/KlaviyoSwift/PrivacyInfo.xcprivacy"]}
s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS' => '-package-name KlaviyoSwift -package-name KlaviyoCore' }
s.dependency 'KlaviyoCore', '~> 5.2.3'
s.dependency 'KlaviyoCore', '~> 5.3.0'
s.dependency 'AnyCodable-FlightSchool'
end
2 changes: 1 addition & 1 deletion KlaviyoSwiftExtension.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "KlaviyoSwiftExtension"
s.version = "5.2.3"
s.version = "5.3.0"
Comment thread
cursor[bot] marked this conversation as resolved.
s.summary = "Incorporate Klaviyo's rich push notifications functionality into your iOS applications"

s.description = <<-DESC
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
- [Autoclearing](#autoclearing)
- [Handling Other Badging Sources](#handling-other-badging-sources)
- [Silent Push Notifications](#silent-push-notifications)
- [Handling background notifications (content-available)](#handling-background-notifications-content-available)
- [Custom Data](#custom-data)
- [Deep Linking](#deep-linking)
- [Adding link-handling logic](#adding-link-handling-logic)
Expand Down Expand Up @@ -427,7 +428,7 @@ func application(_ application: UIApplication, didReceiveRemoteNotification user
// Access custom key-value pairs from the top level
if let customData = userInfo["key_value_pairs"] as? [String: String] {
// Process your custom key-value pairs here
for (key, value) in kvPairs {
for (key, value) in customData {
print("Key: \(key), Value: \(value)")
}
} else {
Expand All @@ -438,6 +439,14 @@ func application(_ application: UIApplication, didReceiveRemoteNotification user

> ℹ️ Silent push notifications are not supported by the iOS simulator. To test silent push notifications, please use a real device.

#### Handling background notifications (content-available)

Klaviyo can send a **standard** push (title, body, and other visible notification UI) whose APNs payload also includes **`content-available: 1`**. That is different from a [silent push](#silent-push-notifications): silent pushes never show an alert, while this is a normal user-visible notification that *additionally* asks iOS to wake your app in the background so you can refresh data or run other work from the same `userInfo`.

You still handle the visible notification through [`UNUserNotificationCenterDelegate`](https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate) when the message is presented or opened. The background wake for `content-available` uses the same app delegate path as silent push: `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)`. Implement that method (see the example under [Silent Push Notifications](#silent-push-notifications)) and finish by calling `completionHandler` with `.newData`, `.noData`, or `.failed` when your background work completes. Use the same Background Modes / remote-notification setup called out in that section (Apple’s [background updates](https://developer.apple.com/documentation/usernotifications/pushing-background-updates-to-your-app) guide).

> ℹ️ Background wakes are best-effort and may be throttled. As with [silent push notifications](#silent-push-notifications), test this functionality on a physical device because the Simulator does not support the full remote-notification background path.

#### Custom Data
Klaviyo messages can also include key-value pairs (custom data) for both standard and silent push notifications. You can access these key-value pairs using the `key_value_pairs` key on the [`userInfo`](https://developer.apple.com/documentation/foundation/nsnotification/1409222-userinfo) dictionary associated with the notification (for silent pushes, see the example above; for standard pushes, see [`NotificationService.swift`](https://github.com/klaviyo/klaviyo-swift-sdk/blob/master/Examples/KlaviyoSwiftExamples/SPMExample/NotificationServiceExtension/NotificationService.swift) in the example app). This enables you to extract additional information from the push payload and handle it appropriately - for instance, by triggering background processing, logging analytics events, or dynamically updating app content.

Expand Down
2 changes: 1 addition & 1 deletion Sources/KlaviyoCore/Utils/Version.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
import Foundation

public let __klaviyoSwiftName = "swift"
public let __klaviyoSwiftVersion = "5.2.3"
public let __klaviyoSwiftVersion = "5.3.0"
42 changes: 8 additions & 34 deletions Sources/KlaviyoForms/InAppForms/IAFPresentationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ class IAFPresentationManager {

private var configuration: InAppFormsConfig?
private var assetSource: String?
private var hasInvokedDismissed = false
private(set) var currentFormId: String?
private(set) var currentFormName: String?

private var formEventTask: Task<Void, Never>?
private var delayedPresentationTask: Task<Void, Never>?
Expand Down Expand Up @@ -186,10 +183,10 @@ class IAFPresentationManager {
Logger.webViewLogger.info("✅ Handshake confirmed from webview, starting profile observation")
}
startProfileObservation()
case let .present(formId, formName):
presentForm(formId: formId, formName: formName)
case let .dismiss(formId, formName):
dismissForm(formId: formId, formName: formName)
case .present:
presentForm()
case .dismiss:
dismissForm()
case .abort:
destroyWebviewAndListeners()
}
Expand Down Expand Up @@ -375,7 +372,7 @@ class IAFPresentationManager {

// MARK: - View Lifecycle

private func presentForm(formId: String? = nil, formName: String? = nil) {
private func presentForm() {
guard let viewController else {
if #available(iOS 14.0, *) {
Logger.webViewLogger.warning("KlaviyoWebViewController is nil; ignoring `presentForm()` request")
Expand All @@ -402,35 +399,21 @@ class IAFPresentationManager {
delayedPresentationTask = Task { @MainActor in
try? await Task.sleep(nanoseconds: 2_000_000_000)
try? Task.checkCancellation()
self.presentForm(formId: formId, formName: formName)
self.presentForm()
}
} else {
if topController.isKlaviyoVC || topController.hasKlaviyoVCInStack {
if #available(iOS 14.0, *) {
Logger.webViewLogger.warning("In-App Form is already being presented; ignoring request")
}
} else {
hasInvokedDismissed = false
currentFormId = formId
currentFormName = formName
topController.present(viewController, animated: false) { [weak self] in
guard let self, !self.hasInvokedDismissed else { return }
self.invokeLifecycleHandler(for: .formShown(formId: formId, formName: formName))
}
topController.present(viewController, animated: false, completion: nil)
}
}
}

func dismissForm(formId: String? = nil, formName: String? = nil) {
func dismissForm() {
guard let viewController else { return }
// Fall back to the context captured at present time if the bridge sends nil identifiers
// (e.g. fender rollback or companion PR not yet deployed)
let effectiveFormId = formId ?? currentFormId
let effectiveFormName = formName ?? currentFormName
if !hasInvokedDismissed {
invokeLifecycleHandler(for: .formDismissed(formId: effectiveFormId, formName: effectiveFormName))
hasInvokedDismissed = true
}
viewController.dismiss(animated: false)
}

Expand All @@ -439,17 +422,8 @@ class IAFPresentationManager {
func destroyWebView() {
guard let viewController else { return }

// Invoke lifecycle handler if form was visible
// This covers timeout-based and programmatic dismissals
if viewController.presentingViewController != nil && !hasInvokedDismissed {
invokeLifecycleHandler(for: .formDismissed(formId: currentFormId, formName: currentFormName))
hasInvokedDismissed = true
}

viewController.dismiss(animated: false, completion: nil)

currentFormId = nil
currentFormName = nil
self.viewController = nil
viewModel = nil
}
Expand Down
Loading
Loading