Skip to content

Commit 04aecf8

Browse files
committedJan 15, 2025
Merge branch 'master' into feature/MOB-10951-add-mobile-framework-infi
* master: [MOB-10605] prepares version 6.5.9 release (#885) [MOB-9446] Enhance push notification state tracking in SDKs (#881) [MOB-9233] Fix tests for json only in app messages (#883)
2 parents 0b2dc85 + b57c7f5 commit 04aecf8

17 files changed

+180
-27
lines changed
 

‎.github/workflows/build-and-test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on: pull_request
44

55
jobs:
66
run-tests-job:
7-
runs-on: macos-latest
7+
runs-on: macos-14
88

99
steps:
1010
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

‎.github/workflows/e2e.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on: pull_request
44

55
jobs:
66
run-e2e-job:
7-
runs-on: macos-latest
7+
runs-on: macos-14
88

99
steps:
1010
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

‎CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ This project adheres to [Semantic Versioning](http://semver.org/).
77
### Added
88
- Added `mobileFrameworkInfo` configuration option to `IterableConfig` to identify the mobile framework (Flutter, React Native, or Native) being used with the SDK.
99

10+
## [6.5.9]
11+
### Added
12+
- Support for JSON-only in-app messages, JSON-only messages are now handled by the onNewInApp handler and consumed after retrieval
13+
- Enhanced notification state tracking to align with system notification permissions changes
14+
15+
### Changed
16+
- reorganized files and updated documentation url in podspec
17+
1018
## [6.5.8]
1119
### Fixed
1220
- Fixed incorrect tracking of pushOpen for push notifications with Wake App enabled. Tracking now happens only when users tap to open the app.

‎Iterable-iOS-AppExtensions.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22
s.name = "Iterable-iOS-AppExtensions"
33
s.module_name = "IterableAppExtensions"
4-
s.version = "6.5.8"
4+
s.version = "6.5.9"
55
s.summary = "App Extensions for Iterable SDK"
66

77
s.description = <<-DESC

‎Iterable-iOS-SDK.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22
s.name = "Iterable-iOS-SDK"
33
s.module_name = "IterableSDK"
4-
s.version = "6.5.8"
4+
s.version = "6.5.9"
55
s.summary = "Iterable's official SDK for iOS"
66

77
s.description = <<-DESC

‎swift-sdk.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
00B6FACE210E88ED007535CF /* prod-1.mobileprovision in Resources */ = {isa = PBXBuildFile; fileRef = 00B6FACD210E874D007535CF /* prod-1.mobileprovision */; };
1212
00B6FAD1210E8D90007535CF /* dev-1.mobileprovision in Resources */ = {isa = PBXBuildFile; fileRef = 00B6FAD0210E8D90007535CF /* dev-1.mobileprovision */; };
1313
00CB31B621096129004ACDEC /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CB31B4210960C4004ACDEC /* TestUtils.swift */; };
14+
092D01942D3038F600E3066A /* NotificationObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 092D01932D3038F600E3066A /* NotificationObserverTests.swift */; };
1415
1CBFFE1A2A97AEEF00ED57EE /* EmbeddedManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CBFFE162A97AEEE00ED57EE /* EmbeddedManagerTests.swift */; };
1516
1CBFFE1B2A97AEEF00ED57EE /* EmbeddedMessagingProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CBFFE172A97AEEE00ED57EE /* EmbeddedMessagingProcessorTests.swift */; };
1617
1CBFFE1C2A97AEEF00ED57EE /* EmbeddedSessionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CBFFE182A97AEEE00ED57EE /* EmbeddedSessionManagerTests.swift */; };
@@ -544,6 +545,7 @@
544545
00B6FACD210E874D007535CF /* prod-1.mobileprovision */ = {isa = PBXFileReference; lastKnownFileType = file; path = "prod-1.mobileprovision"; sourceTree = "<group>"; };
545546
00B6FAD0210E8D90007535CF /* dev-1.mobileprovision */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dev-1.mobileprovision"; sourceTree = "<group>"; };
546547
00CB31B4210960C4004ACDEC /* TestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = "<group>"; };
548+
092D01932D3038F600E3066A /* NotificationObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationObserverTests.swift; sourceTree = "<group>"; };
547549
1CBFFE162A97AEEE00ED57EE /* EmbeddedManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmbeddedManagerTests.swift; sourceTree = "<group>"; };
548550
1CBFFE172A97AEEE00ED57EE /* EmbeddedMessagingProcessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmbeddedMessagingProcessorTests.swift; sourceTree = "<group>"; };
549551
1CBFFE182A97AEEE00ED57EE /* EmbeddedSessionManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmbeddedSessionManagerTests.swift; sourceTree = "<group>"; };
@@ -938,6 +940,7 @@
938940
552A0AA9280E249C00A80963 /* notification-tests */ = {
939941
isa = PBXGroup;
940942
children = (
943+
092D01932D3038F600E3066A /* NotificationObserverTests.swift */,
941944
55B37FC32297135F0042F13A /* NotificationMetadataTests.swift */,
942945
AC2C667F20D31B1F00D46CC9 /* NotificationResponseTests.swift */,
943946
);
@@ -2190,6 +2193,7 @@
21902193
5588DFD128C0465E000697D7 /* MockAPNSTypeChecker.swift in Sources */,
21912194
00B6FACC210E8484007535CF /* APNSTypeCheckerTests.swift in Sources */,
21922195
AC8F35A2239806B500302994 /* InboxViewControllerViewModelTests.swift in Sources */,
2196+
092D01942D3038F600E3066A /* NotificationObserverTests.swift in Sources */,
21932197
AC995F9A2166EEB50099A184 /* CommonMocks.swift in Sources */,
21942198
5588DFE128C046B7000697D7 /* MockLocalStorage.swift in Sources */,
21952199
1CBFFE1B2A97AEEF00ED57EE /* EmbeddedMessagingProcessorTests.swift in Sources */,

‎swift-sdk/Core/Constants.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ enum Const {
5757
static let deviceId = "itbl_device_id"
5858
static let sdkVersion = "itbl_sdk_version"
5959
static let offlineMode = "itbl_offline_mode"
60-
60+
static let isNotificationsEnabled = "itbl_isNotificationsEnabled"
61+
static let hasStoredNotificationSetting = "itbl_hasStoredNotificationSetting"
62+
6163
static let attributionInfoExpiration = 24
6264
}
6365

‎swift-sdk/Internal/InternalIterableAPI.swift

+53-3
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
176176

177177
// MARK: - API Request Calls
178178

179-
func register(token: Data,
179+
func register(token: String,
180180
onSuccess: OnSuccessHandler? = nil,
181181
onFailure: OnFailureHandler? = nil) {
182182
guard let appName = pushIntegrationName else {
@@ -187,15 +187,15 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
187187
return
188188
}
189189

190-
hexToken = token.hexString()
190+
hexToken = token
191191

192192
let frameworkType = IterableAPIMobileFrameworkDetector.frameworkType()
193193
let mobileFrameworkInfo = config.mobileFrameworkInfo ?? IterableAPIMobileFrameworkInfo(
194194
frameworkType: frameworkType,
195195
iterableSdkVersion: frameworkType == .native ? localStorage.sdkVersion : nil
196196
)
197197

198-
let registerTokenInfo = RegisterTokenInfo(hexToken: token.hexString(),
198+
let registerTokenInfo = RegisterTokenInfo(hexToken: token,
199199
appName: appName,
200200
pushServicePlatform: config.pushPlatform,
201201
apnsType: dependencyContainer.apnsTypeChecker.apnsType,
@@ -216,6 +216,12 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
216216
)
217217
}
218218

219+
func register(token: Data,
220+
onSuccess: OnSuccessHandler? = nil,
221+
onFailure: OnFailureHandler? = nil) {
222+
register(token: token.hexString(), onSuccess: onSuccess, onFailure: onFailure)
223+
}
224+
219225
@discardableResult
220226
func disableDeviceForCurrentUser(withOnSuccess onSuccess: OnSuccessHandler? = nil,
221227
onFailure: OnFailureHandler? = nil) -> Pending<SendRequestValue, SendRequestError> {
@@ -224,12 +230,18 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
224230
onFailure?(errorMessage, nil)
225231
return SendRequestError.createErroredFuture(reason: errorMessage)
226232
}
233+
227234
guard userId != nil || email != nil else {
228235
let errorMessage = "either userId or email must be present"
229236
onFailure?(errorMessage, nil)
230237
return SendRequestError.createErroredFuture(reason: errorMessage)
231238
}
232239

240+
// We need to call register token here so that we can trigger the device registration
241+
// with the updated notification settings
242+
243+
register(token: hexToken)
244+
233245
return requestHandler.disableDeviceForCurrentUser(hexToken: hexToken, withOnSuccess: onSuccess, onFailure: onFailure)
234246
}
235247

@@ -508,6 +520,8 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
508520
private var _userId: String?
509521
private var _successCallback: OnSuccessHandler? = nil
510522
private var _failureCallback: OnFailureHandler? = nil
523+
524+
private let notificationCenter: NotificationCenterProtocol
511525

512526

513527
/// the hex representation of this device token
@@ -674,6 +688,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
674688
localStorage = dependencyContainer.localStorage
675689
inAppDisplayer = dependencyContainer.inAppDisplayer
676690
urlOpener = dependencyContainer.urlOpener
691+
notificationCenter = dependencyContainer.notificationCenter
677692
deepLinkManager = DeepLinkManager(redirectNetworkSessionProvider: dependencyContainer)
678693
}
679694

@@ -706,10 +721,44 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
706721
requestHandler.start()
707722

708723
checkRemoteConfiguration()
724+
725+
addForegroundObservers()
709726

710727
return inAppManager.start()
711728
}
712729

730+
private func addForegroundObservers() {
731+
notificationCenter.addObserver(self,
732+
selector: #selector(onAppDidBecomeActiveNotification(notification:)),
733+
name: UIApplication.didBecomeActiveNotification,
734+
object: nil)
735+
}
736+
737+
@objc private func onAppDidBecomeActiveNotification(notification: Notification) {
738+
guard config.autoPushRegistration else { return }
739+
740+
notificationStateProvider.isNotificationsEnabled { [weak self] systemEnabled in
741+
guard let self = self else { return }
742+
743+
let storedEnabled = self.localStorage.isNotificationsEnabled
744+
let hasStoredPermission = self.localStorage.hasStoredNotificationSetting
745+
746+
if self.isEitherUserIdOrEmailSet() {
747+
if hasStoredPermission && (storedEnabled != systemEnabled) {
748+
if !systemEnabled {
749+
self.disableDeviceForCurrentUser()
750+
} else {
751+
self.notificationStateProvider.registerForRemoteNotifications()
752+
}
753+
}
754+
755+
// Always store the current state
756+
self.localStorage.isNotificationsEnabled = systemEnabled
757+
self.localStorage.hasStoredNotificationSetting = true
758+
}
759+
}
760+
}
761+
713762
private func handle(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
714763
guard let launchOptions = launchOptions else {
715764
return
@@ -780,6 +829,7 @@ final class InternalIterableAPI: NSObject, PushTrackerProtocol, AuthProvider {
780829

781830
deinit {
782831
ITBInfo()
832+
notificationCenter.removeObserver(self)
783833
requestHandler.stop()
784834
}
785835
}

‎swift-sdk/Internal/IterableUserDefaults.swift

+19-1
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,28 @@ class IterableUserDefaults {
6464

6565
var offlineMode: Bool {
6666
get {
67-
return bool(withKey: .offlineMode)
67+
bool(withKey: .offlineMode)
6868
} set {
6969
save(bool: newValue, withKey: .offlineMode)
7070
}
7171
}
7272

73+
var isNotificationsEnabled: Bool {
74+
get {
75+
bool(withKey: .isNotificationsEnabled)
76+
} set {
77+
save(bool: newValue, withKey: .isNotificationsEnabled)
78+
}
79+
}
80+
81+
var hasStoredNotificationSetting: Bool {
82+
get {
83+
bool(withKey: .hasStoredNotificationSetting)
84+
} set {
85+
save(bool: newValue, withKey: .hasStoredNotificationSetting)
86+
}
87+
}
88+
7389
func getAttributionInfo(currentDate: Date) -> IterableAttributionInfo? {
7490
(try? codable(withKey: .attributionInfo, currentDate: currentDate)) ?? nil
7591
}
@@ -196,6 +212,8 @@ class IterableUserDefaults {
196212
static let deviceId = UserDefaultsKey(value: Const.UserDefault.deviceId)
197213
static let sdkVersion = UserDefaultsKey(value: Const.UserDefault.sdkVersion)
198214
static let offlineMode = UserDefaultsKey(value: Const.UserDefault.offlineMode)
215+
static let isNotificationsEnabled = UserDefaultsKey(value: Const.UserDefault.isNotificationsEnabled)
216+
static let hasStoredNotificationSetting = UserDefaultsKey(value: Const.UserDefault.hasStoredNotificationSetting)
199217
}
200218

201219
private struct Envelope: Codable {

‎swift-sdk/Internal/Utilities/LocalStorage.swift

+16
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ struct LocalStorage: LocalStorageProtocol {
6767
}
6868
}
6969

70+
var isNotificationsEnabled: Bool {
71+
get {
72+
iterableUserDefaults.isNotificationsEnabled
73+
} set {
74+
iterableUserDefaults.isNotificationsEnabled = newValue
75+
}
76+
}
77+
78+
var hasStoredNotificationSetting: Bool {
79+
get {
80+
iterableUserDefaults.hasStoredNotificationSetting
81+
} set {
82+
iterableUserDefaults.hasStoredNotificationSetting = newValue
83+
}
84+
}
85+
7086
func getAttributionInfo(currentDate: Date) -> IterableAttributionInfo? {
7187
iterableUserDefaults.getAttributionInfo(currentDate: currentDate)
7288
}

‎swift-sdk/Internal/Utilities/LocalStorageProtocol.swift

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ protocol LocalStorageProtocol {
1919

2020
var offlineMode: Bool { get set }
2121

22+
var isNotificationsEnabled: Bool { get set }
23+
24+
var hasStoredNotificationSetting: Bool { get set }
25+
2226
func getAttributionInfo(currentDate: Date) -> IterableAttributionInfo?
2327

2428
func save(attributionInfo: IterableAttributionInfo?, withExpiration expiration: Date?)

‎swift-sdk/SDK/IterableAPI.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import UIKit
77

88
@objcMembers public final class IterableAPI: NSObject {
99
/// The current SDK version
10-
public static let sdkVersion = "6.5.8"
10+
public static let sdkVersion = "6.5.9"
1111

1212
/// The email of the logged in user that this IterableAPI is using
1313
public static var email: String? {

‎tests/common/MockLocalStorage.swift

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ class MockLocalStorage: LocalStorageProtocol {
2121

2222
var offlineMode: Bool = false
2323

24+
var isNotificationsEnabled: Bool = false
25+
26+
var hasStoredNotificationSetting: Bool = false
27+
2428
func getAttributionInfo(currentDate: Date) -> IterableAttributionInfo? {
2529
guard !MockLocalStorage.isExpired(expiration: attributionInfoExpiration, currentDate: currentDate) else {
2630
return nil

‎tests/unit-tests/AutoRegistrationTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class AutoRegistrationTests: XCTestCase {
2020

2121
func testCallDisableAndEnable() {
2222
let expectation1 = expectation(description: "call register device API")
23+
expectation1.expectedFulfillmentCount = 2
2324
let expectation2 = expectation(description: "call registerForRemoteNotifications twice")
2425
expectation2.expectedFulfillmentCount = 2
2526
let expectation3 = expectation(description: "call disable on user1@example.com")

‎tests/unit-tests/InAppTests.swift

+8-8
Original file line numberDiff line numberDiff line change
@@ -1445,7 +1445,7 @@ class InAppTests: XCTestCase {
14451445
[
14461446
{
14471447
"saveToInbox": false,
1448-
"jsonOnly": 1,
1448+
"jsonOnly": true,
14491449
"customPayload": {"key": "value"},
14501450
"content": {
14511451
"html": "<meta name=\\"viewport\\" content=\\"width=device-width\\">",
@@ -1496,7 +1496,7 @@ class InAppTests: XCTestCase {
14961496
[
14971497
{
14981498
"saveToInbox": false,
1499-
"jsonOnly": 1,
1499+
"jsonOnly": true,
15001500
"messageType": "Mobile",
15011501
"typeOfContent": "Static",
15021502
"customPayload": {
@@ -1579,7 +1579,7 @@ class InAppTests: XCTestCase {
15791579
[
15801580
{
15811581
"saveToInbox": false,
1582-
"jsonOnly": 1,
1582+
"jsonOnly": true,
15831583
"messageType": "Mobile",
15841584
"typeOfContent": "Static",
15851585
"customPayload": {"key": "immediate"},
@@ -1598,7 +1598,7 @@ class InAppTests: XCTestCase {
15981598
},
15991599
{
16001600
"saveToInbox": false,
1601-
"jsonOnly": 1,
1601+
"jsonOnly": true,
16021602
"messageType": "Mobile",
16031603
"typeOfContent": "Static",
16041604
"customPayload": {"key": "never"},
@@ -1640,7 +1640,7 @@ class InAppTests: XCTestCase {
16401640
[
16411641
{
16421642
"saveToInbox": false,
1643-
"jsonOnly": 1,
1643+
"jsonOnly": true,
16441644
"messageType": "Mobile",
16451645
"typeOfContent": "Static",
16461646
"content": {
@@ -1694,7 +1694,7 @@ class InAppTests: XCTestCase {
16941694
[
16951695
{
16961696
"saveToInbox": false,
1697-
"jsonOnly": 1,
1697+
"jsonOnly": true,
16981698
"messageType": "Mobile",
16991699
"typeOfContent": "Static",
17001700
"customPayload": {},
@@ -1748,7 +1748,7 @@ class InAppTests: XCTestCase {
17481748
[
17491749
{
17501750
"saveToInbox": true,
1751-
"jsonOnly": 1,
1751+
"jsonOnly": true,
17521752
"messageType": "Mobile",
17531753
"typeOfContent": "Static",
17541754
"customPayload": {"key": "value"},
@@ -1805,7 +1805,7 @@ class InAppTests: XCTestCase {
18051805
[
18061806
{
18071807
"saveToInbox": false,
1808-
"jsonOnly": 1,
1808+
"jsonOnly": true,
18091809
"messageType": "Mobile",
18101810
"typeOfContent": "Static",
18111811
"customPayload": {

0 commit comments

Comments
 (0)