Skip to content

Commit ee0eb49

Browse files
authored
Merge pull request #475 from Iterable/next-version
[MOB-2775] SDK version 6.2.22
2 parents 118fd9e + 59b8ece commit ee0eb49

23 files changed

+464
-155
lines changed

.travis.yml

-15
This file was deleted.

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22
All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

5+
## 6.2.22
6+
#### Added
7+
- In-app message prioritization - Ordering the display of in-app messages based on a priority you select in Iterable when creating in-app campaigns
8+
9+
#### Fixed
10+
- The authentication flow, with JWT, now does the proper order of operations to avoid a false negative when setting the user (with `setEmail` or `setUser`)
11+
- The empty inbox message will now properly wraparound
12+
- An inbox message that has its read state changed will now only animate the unread dot
13+
14+
#### Removed
15+
- Removed device fingerprinting as a cautionary measure for iOS 14.5 policy updates - note: we still keep the system generated UUID
16+
- Removed deferred deep linking feature
17+
518
## 6.2.21
619
#### Added
720
- Support for syncing in-app message read state across multiple devices:

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.2.21"
4+
s.version = "6.2.22"
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.2.21"
4+
s.version = "6.2.22"
55
s.summary = "Iterable's official SDK for iOS"
66

77
s.description = <<-DESC

swift-sdk.xcodeproj/project.pbxproj

+6-10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
5531CDAE22A9C992000D05E2 /* ClassExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5531CDAD22A9C992000D05E2 /* ClassExtensionsTests.swift */; };
1717
5536781F2576FF9000DB3652 /* IterableUtilTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5536781E2576FF9000DB3652 /* IterableUtilTests.swift */; };
1818
556FB1EA244FAF6A00EDF6BD /* InAppPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 556FB1E9244FAF6A00EDF6BD /* InAppPresenter.swift */; };
19+
55705A6925A78BF3009BDEE2 /* InAppPriorityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55705A6825A78BF3009BDEE2 /* InAppPriorityTests.swift */; };
1920
557AE6BF24A56E5E00B57750 /* Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557AE6BE24A56E5E00B57750 /* Auth.swift */; };
2021
5585DF8F22A73390000A32B9 /* IterableInboxViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585DF8E22A73390000A32B9 /* IterableInboxViewControllerTests.swift */; };
2122
55AEA95925F05B7D00B38CED /* InAppMessageProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55AEA95825F05B7D00B38CED /* InAppMessageProcessorTests.swift */; };
@@ -363,6 +364,7 @@
363364
5531CDAD22A9C992000D05E2 /* ClassExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassExtensionsTests.swift; sourceTree = "<group>"; };
364365
5536781E2576FF9000DB3652 /* IterableUtilTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IterableUtilTests.swift; sourceTree = "<group>"; };
365366
556FB1E9244FAF6A00EDF6BD /* InAppPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPresenter.swift; sourceTree = "<group>"; };
367+
55705A6825A78BF3009BDEE2 /* InAppPriorityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = InAppPriorityTests.swift; path = "tests/swift-sdk-swift-tests/InAppPriorityTests.swift"; sourceTree = SOURCE_ROOT; };
366368
557AE6BE24A56E5E00B57750 /* Auth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Auth.swift; sourceTree = "<group>"; };
367369
5585DF8E22A73390000A32B9 /* IterableInboxViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IterableInboxViewControllerTests.swift; sourceTree = "<group>"; };
368370
5585DF9022A877E6000A32B9 /* IterableInboxViewControllerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IterableInboxViewControllerUITests.swift; sourceTree = "<group>"; };
@@ -676,13 +678,6 @@
676678
path = "swift-sdk/misc";
677679
sourceTree = "<group>";
678680
};
679-
55A8F6E92613E0B800184ECC /* Recovered References */ = {
680-
isa = PBXGroup;
681-
children = (
682-
);
683-
name = "Recovered References";
684-
sourceTree = "<group>";
685-
};
686681
AC0248062279132400495FB9 /* Dwifft */ = {
687682
isa = PBXGroup;
688683
children = (
@@ -739,7 +734,6 @@
739734
ACDA975A23159C37004C412E /* inbox-ui-tests-app */,
740735
ACFCA72920EB02DB00BFB277 /* tests */,
741736
5550F22324217CFC0014456A /* misc */,
742-
55A8F6E92613E0B800184ECC /* Recovered References */,
743737
);
744738
sourceTree = "<group>";
745739
};
@@ -961,13 +955,14 @@
961955
55B37FC0229620D20042F13A /* CommerceItemTests.swift */,
962956
55E6F45E238E066400808BCE /* DeepLinkTests.swift */,
963957
551E5C0F234D485A005FEE9E /* DeprecatedFunctionsTests.swift */,
964-
AC750A49234CD67900561902 /* InAppHelperTests.swift */,
965958
55AEA95825F05B7D00B38CED /* InAppMessageProcessorTests.swift */,
959+
ACC3FD9D2536D7A30004A2E0 /* InAppFilePersistenceTests.swift */,
960+
AC750A49234CD67900561902 /* InAppHelperTests.swift */,
966961
AC6FDD8B20F56309005D811E /* InAppParsingTests.swift */,
967962
55B37FED229F59290042F13A /* InAppPersistenceTests.swift */,
968963
55CC257A2462064F00A77FD5 /* InAppPresenterTests.swift */,
964+
55705A6825A78BF3009BDEE2 /* InAppPriorityTests.swift */,
969965
ACA8D1A821965B7D001B1332 /* InAppTests.swift */,
970-
ACC3FD9D2536D7A30004A2E0 /* InAppFilePersistenceTests.swift */,
971966
55B549852397462300243E87 /* InboxImpressionTrackerTests.swift */,
972967
55B37FC722975A840042F13A /* InboxMessageViewModelTests.swift */,
973968
55B5498323973B5C00243E87 /* InboxSessionManagerTests.swift */,
@@ -1767,6 +1762,7 @@
17671762
AC89661E2124FBCE0051A6CD /* IterableAutoRegistrationTests.swift in Sources */,
17681763
ACA8D1A921965B7D001B1332 /* InAppTests.swift in Sources */,
17691764
00B6FACC210E8484007535CF /* APNSTypeCheckerTests.swift in Sources */,
1765+
55705A6925A78BF3009BDEE2 /* InAppPriorityTests.swift in Sources */,
17701766
AC8F35A2239806B500302994 /* InboxViewControllerViewModelTests.swift in Sources */,
17711767
AC995F9A2166EEB50099A184 /* CommonMocks.swift in Sources */,
17721768
5585DF8F22A73390000A32B9 /* IterableInboxViewControllerTests.swift in Sources */,

swift-sdk/Constants.swift

+10
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ public enum Const {
5656
static let payloadExpiration = 24
5757
static let attributionInfoExpiration = 24
5858
}
59+
60+
enum PriorityLevel {
61+
static let critical = 100.0
62+
static let high = 200.0
63+
static let medium = 300.0
64+
static let low = 400.0
65+
66+
static let unassigned = 300.5
67+
}
5968
}
6069

6170
public protocol JsonKeyValueRepresentable {
@@ -106,6 +115,7 @@ public enum JsonKey: String, JsonKeyRepresentable {
106115
case inAppLocation = "location"
107116
case clickedUrl
108117
case read
118+
case priorityLevel
109119

110120
case inboxSessionStart
111121
case inboxSessionEnd

swift-sdk/Internal/AuthManager.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,11 @@ class AuthManager: IterableInternalAuthManagerProtocol {
102102

103103
storeAuthToken()
104104

105+
queueAuthTokenExpirationRefresh(authToken)
106+
105107
if authToken != nil {
106108
onSuccess?(authToken)
107109
}
108-
109-
queueAuthTokenExpirationRefresh(authToken)
110110
}
111111

112112
private func queueAuthTokenExpirationRefresh(_ authToken: String?) {

swift-sdk/Internal/InAppManager+Functions.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ struct MessagesProcessor {
7070
}
7171

7272
private func getFirstProcessableTriggeredMessage() -> IterableInAppMessage? {
73-
messagesMap.values.filter(MessagesProcessor.isProcessableTriggeredMessage).first
73+
messagesMap.values
74+
.filter(MessagesProcessor.isProcessableTriggeredMessage)
75+
.sorted { $0.priorityLevel < $1.priorityLevel }
76+
.first
7477
}
7578

7679
private static func isProcessableTriggeredMessage(_ message: IterableInAppMessage) -> Bool {

swift-sdk/Internal/InAppManager.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol {
220220
.map { self.processMergedMessages(appIsReady: appIsReady, mergeMessagesResult: $0) }
221221
}
222222

223-
// messages are new messages coming from the server
223+
/// `messages` are new messages coming from the server
224224
private func mergeMessages(_ messages: [IterableInAppMessage]) -> MergeMessagesResult {
225225
MessagesObtainedHandler(messagesMap: messagesMap, messages: messages).handle()
226226
}
@@ -232,7 +232,7 @@ class InAppManager: NSObject, IterableInternalInAppManagerProtocol {
232232
messagesMap = mergeMessagesResult.messagesMap
233233
}
234234

235-
// track in app delivery
235+
// track in-app delivery
236236
mergeMessagesResult.deliveredMessages.forEach {
237237
_ = apiClient?.track(inAppDelivery: InAppMessageContext.from(message: $0, location: nil))
238238
}

swift-sdk/Internal/InAppMessageParser.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ struct InAppMessageParser {
104104
let createdAt = parseTime(withKey: .inboxCreatedAt, fromJson: json)
105105
let expiresAt = parseTime(withKey: .inboxExpiresAt, fromJson: json)
106106
let read = json[JsonKey.read.jsonKey] as? Bool ?? false
107+
let priorityLevel = json[JsonKey.priorityLevel.jsonKey] as? Double ?? Const.PriorityLevel.unassigned
107108

108109
return .success(IterableInAppMessage(messageId: messageId,
109110
campaignId: campaignId,
@@ -114,7 +115,8 @@ struct InAppMessageParser {
114115
saveToInbox: saveToInbox,
115116
inboxMetadata: inboxMetadata,
116117
customPayload: customPayload,
117-
read: read))
118+
read: read,
119+
priorityLevel: priorityLevel))
118120
}
119121

120122
private static func parseTime(withKey key: JsonKey, fromJson json: [AnyHashable: Any]) -> Date? {

swift-sdk/Internal/InAppPersistence.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ extension IterableInAppMessage: Codable {
235235
case read
236236
case trigger
237237
case content
238+
case priorityLevel
238239
}
239240

240241
enum ContentCodingKeys: String, CodingKey {
@@ -266,6 +267,7 @@ extension IterableInAppMessage: Codable {
266267

267268
let trigger = (try? container.decode(IterableInAppTrigger.self, forKey: .trigger)) ?? .undefinedTrigger
268269
let content = IterableInAppMessage.decodeContent(from: container)
270+
let priorityLevel = (try? container.decode(Double.self, forKey: .priorityLevel)) ?? Const.PriorityLevel.unassigned
269271

270272
self.init(messageId: messageId,
271273
campaignId: campaignId,
@@ -276,7 +278,8 @@ extension IterableInAppMessage: Codable {
276278
saveToInbox: saveToInbox,
277279
inboxMetadata: inboxMetadata,
278280
customPayload: customPayload,
279-
read: read)
281+
read: read,
282+
priorityLevel: priorityLevel)
280283

281284
self.didProcessTrigger = didProcessTrigger
282285
self.consumed = consumed
@@ -295,6 +298,7 @@ extension IterableInAppMessage: Codable {
295298
try? container.encode(didProcessTrigger, forKey: .didProcessTrigger)
296299
try? container.encode(consumed, forKey: .consumed)
297300
try? container.encode(read, forKey: .read)
301+
try? container.encode(priorityLevel, forKey: .priorityLevel)
298302

299303
if let inboxMetadata = inboxMetadata {
300304
try? container.encode(inboxMetadata, forKey: .inboxMetadata)

swift-sdk/Internal/InboxViewControllerViewModel.swift

+51-4
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,18 @@
66
import Foundation
77
import UIKit
88

9+
enum RowDiff {
10+
case insert(IndexPath)
11+
case delete(IndexPath)
12+
case update(IndexPath)
13+
case sectionInsert(IndexSet)
14+
case sectionDelete(IndexSet)
15+
case sectionUpdate(IndexSet)
16+
}
17+
918
protocol InboxViewControllerViewModelView: AnyObject {
1019
// All these methods should be called on the main thread
11-
func onViewModelChanged(diff: [SectionedDiffStep<Int, InboxMessageViewModel>])
20+
func onViewModelChanged(diffs: [RowDiff])
1221
func onImageLoaded(for indexPath: IndexPath)
1322
var currentlyVisibleRowIndexPaths: [IndexPath] { get }
1423
}
@@ -239,13 +248,51 @@ class InboxViewControllerViewModel: InboxViewControllerViewModelProtocol {
239248
ITBInfo()
240249
newSectionedMessages = sortAndFilter(messages: getMessages())
241250

242-
let diff = Dwifft.diff(lhs: sectionedMessages, rhs: newSectionedMessages)
243-
if diff.count > 0 {
244-
view?.onViewModelChanged(diff: diff)
251+
let dwifftDiffs = Dwifft.diff(lhs: sectionedMessages, rhs: newSectionedMessages)
252+
if dwifftDiffs.count > 0 {
253+
let rowDiffs = Self.dwifftDiffsToRowDiffs(dwifftDiffs: dwifftDiffs)
254+
view?.onViewModelChanged(diffs: rowDiffs)
245255
updateVisibleRows()
246256
}
247257
}
248258

259+
private static func dwifftDiffsToRowDiffs(dwifftDiffs: [SectionedDiffStep<Int, InboxMessageViewModel>]) -> [RowDiff] {
260+
var result = [RowDiff]()
261+
var rowDeletes = [IndexPath: Int]()
262+
var sectionDeletes = [Int: Int]()
263+
264+
for (pos, dwiffDiff) in dwifftDiffs.enumerated() {
265+
switch dwiffDiff {
266+
case let .delete(section, row, _):
267+
let indexPath = IndexPath(row: row, section: section)
268+
result.append(.delete(indexPath))
269+
rowDeletes[indexPath] = pos
270+
case let .insert(section, row, _):
271+
let indexPath = IndexPath(row: row, section: section)
272+
if let pos = rowDeletes[indexPath] {
273+
result.remove(at: pos)
274+
rowDeletes.removeValue(forKey: indexPath)
275+
result.append(.update(indexPath))
276+
} else {
277+
result.append(.insert(indexPath))
278+
}
279+
case let .sectionDelete(section, _):
280+
result.append(.sectionDelete(IndexSet(integer: section)))
281+
sectionDeletes[section] = pos
282+
case let .sectionInsert(section, _):
283+
if let pos = sectionDeletes[section] {
284+
result.remove(at: pos)
285+
sectionDeletes.removeValue(forKey: section)
286+
result.append(.sectionUpdate(IndexSet(integer: section)))
287+
} else {
288+
result.append(.sectionInsert(IndexSet(integer: section)))
289+
}
290+
}
291+
}
292+
293+
return result
294+
}
295+
249296
@objc private func onAppWillEnterForeground(notification _: NSNotification) {
250297
ITBInfo()
251298
if sessionManager.startSessionWhenAppMovesToForeground {

0 commit comments

Comments
 (0)