Skip to content

Commit 19fb463

Browse files
authored
Merge pull request #464 from Iterable/MOB-2769-6.2.21
[MOB-2769] version 6.2.21
2 parents 04101b9 + 8ea5f99 commit 19fb463

25 files changed

+572
-188
lines changed

.github/pull_request_template.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## 🔹 JIRA Ticket(s) if any
2+
3+
* [MOB-XXXX](https://iterable.atlassian.net/browse/MOB-XXXX)
4+
5+
## ✏️ Description
6+
7+
> Please provide a brief description of what this pull request does.

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@
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.21
6+
#### Added
7+
- Support for syncing in-app message read state across multiple devices:
8+
- When the SDK fetches in-app messages from Iterable, it examines each
9+
message's `read` field to determine if it has already been read.
10+
- The SDK's default implementation no longer automatically displays in-app
11+
messages that have already been seen on another device (even if those
12+
messages were _not_ configured to go directly to the inbox).
13+
- When you view a message, the SDK calls [`POST /api/events/trackInAppOpen`](https://api.iterable.com/api/docs#events_trackInAppOpen)
14+
to create an `inAppOpen` event on the user's Iterable profile. Previous
15+
versions of the SDK made this same API call, but the call now also causes
16+
Iterable to set the message's `read` field to `true`.
17+
- Previous versions of the SDK will correctly sync a message's read / unread
18+
indicator for the default implementation of a mobile inbox. However, these
19+
older SDK versions will not automatically suppress messages that have
20+
already been seen on another device (as this version of the SDK will).
21+
- Support for the display of a custom message (title and body) in an empty
22+
mobile inbox. For more details, see [Customizing Mobile Inbox on iOS](https://support.iterable.com/hc/articles/360039091471#empty-state)
23+
524
## 6.2.20
625
#### Added
726
- Added callback to initialize method needed for React Native. This change should have no effect for iOS SDK.

Iterable-iOS-AppExtensions.podspec

Lines changed: 1 addition & 1 deletion
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.20"
4+
s.version = "6.2.21"
55
s.summary = "App Extensions for Iterable SDK"
66

77
s.description = <<-DESC

Iterable-iOS-SDK.podspec

Lines changed: 1 addition & 1 deletion
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.20"
4+
s.version = "6.2.21"
55
s.summary = "Iterable's official SDK for iOS"
66

77
s.description = <<-DESC

swift-sdk.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
556FB1EA244FAF6A00EDF6BD /* InAppPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 556FB1E9244FAF6A00EDF6BD /* InAppPresenter.swift */; };
1919
557AE6BF24A56E5E00B57750 /* Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557AE6BE24A56E5E00B57750 /* Auth.swift */; };
2020
5585DF8F22A73390000A32B9 /* IterableInboxViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585DF8E22A73390000A32B9 /* IterableInboxViewControllerTests.swift */; };
21+
55AEA95925F05B7D00B38CED /* InAppMessageProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55AEA95825F05B7D00B38CED /* InAppMessageProcessorTests.swift */; };
2122
55B3119B251015CF0056E4FC /* AuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55298B222501A5AB00190BAE /* AuthManager.swift */; };
2223
55B37FC1229620D20042F13A /* CommerceItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55B37FC0229620D20042F13A /* CommerceItemTests.swift */; };
2324
55B37FC42297135F0042F13A /* NotificationMetadataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55B37FC32297135F0042F13A /* NotificationMetadataTests.swift */; };
@@ -366,6 +367,7 @@
366367
557AE6BE24A56E5E00B57750 /* Auth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Auth.swift; sourceTree = "<group>"; };
367368
5585DF8E22A73390000A32B9 /* IterableInboxViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IterableInboxViewControllerTests.swift; sourceTree = "<group>"; };
368369
5585DF9022A877E6000A32B9 /* IterableInboxViewControllerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IterableInboxViewControllerUITests.swift; sourceTree = "<group>"; };
370+
55AEA95825F05B7D00B38CED /* InAppMessageProcessorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppMessageProcessorTests.swift; sourceTree = "<group>"; };
369371
55B37FC0229620D20042F13A /* CommerceItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommerceItemTests.swift; sourceTree = "<group>"; };
370372
55B37FC32297135F0042F13A /* NotificationMetadataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationMetadataTests.swift; sourceTree = "<group>"; };
371373
55B37FC5229752DD0042F13A /* OrderedDictionaryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedDictionaryTests.swift; sourceTree = "<group>"; };
@@ -676,6 +678,13 @@
676678
path = "swift-sdk/misc";
677679
sourceTree = "<group>";
678680
};
681+
55A8F6E92613E0B800184ECC /* Recovered References */ = {
682+
isa = PBXGroup;
683+
children = (
684+
);
685+
name = "Recovered References";
686+
sourceTree = "<group>";
687+
};
679688
AC0248062279132400495FB9 /* Dwifft */ = {
680689
isa = PBXGroup;
681690
children = (
@@ -732,6 +741,7 @@
732741
ACDA975A23159C37004C412E /* inbox-ui-tests-app */,
733742
ACFCA72920EB02DB00BFB277 /* tests */,
734743
5550F22324217CFC0014456A /* misc */,
744+
55A8F6E92613E0B800184ECC /* Recovered References */,
735745
);
736746
sourceTree = "<group>";
737747
};
@@ -955,6 +965,7 @@
955965
55E6F45D238E066400808BCE /* DeferredDeepLinkTests.swift */,
956966
551E5C0F234D485A005FEE9E /* DeprecatedFunctionsTests.swift */,
957967
AC750A49234CD67900561902 /* InAppHelperTests.swift */,
968+
55AEA95825F05B7D00B38CED /* InAppMessageProcessorTests.swift */,
958969
AC6FDD8B20F56309005D811E /* InAppParsingTests.swift */,
959970
55B37FED229F59290042F13A /* InAppPersistenceTests.swift */,
960971
55CC257A2462064F00A77FD5 /* InAppPresenterTests.swift */,
@@ -1752,6 +1763,7 @@
17521763
buildActionMask = 2147483647;
17531764
files = (
17541765
ACA8D1A62196309C001B1332 /* Common.swift in Sources */,
1766+
55AEA95925F05B7D00B38CED /* InAppMessageProcessorTests.swift in Sources */,
17551767
ACC362B824D17005002C67BA /* IterableRequestTests.swift in Sources */,
17561768
AC2C668720D3435700D46CC9 /* IterableActionRunnerTests.swift in Sources */,
17571769
00CB31B621096129004ACDEC /* TestUtils.swift in Sources */,

swift-sdk/Internal/CoreDataUtil.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ struct CoreDataUtil {
6262
} else if let boolValue = columnValue as? Bool {
6363
return NSPredicate(format: "%K == %@", columnName, NSNumber(value: boolValue))
6464
} else {
65-
fatalError("unsuppored value: \(columnValue)")
65+
ITBError("unsuppored value: \(columnValue) for columnPredicate")
66+
return NSPredicate()
6667
}
6768
}
6869
}

swift-sdk/Internal/InAppCalculations.swift

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,17 +102,6 @@ struct InAppCalculations {
102102
}
103103
}
104104

105-
static func adjustedPadding(from padding: UIEdgeInsets) -> UIEdgeInsets {
106-
var insetPadding = padding
107-
if insetPadding.left + insetPadding.right >= 100 {
108-
ITBError("Can't display an in-app with padding > 100%. Defaulting to 0 for padding left/right")
109-
insetPadding.left = 0
110-
insetPadding.right = 0
111-
}
112-
113-
return insetPadding
114-
}
115-
116105
static func calculateWebViewPosition(safeAreaInsets: UIEdgeInsets,
117106
parentPosition: ViewPosition,
118107
paddingLeft: CGFloat,

swift-sdk/Internal/InAppContentParser.swift

Lines changed: 109 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import Foundation
99
import UIKit
1010

11+
typealias PaddingParser = HtmlContentParser.InAppDisplaySettingsParser.PaddingParser
12+
typealias Padding = PaddingParser.Padding
13+
1114
enum InAppContentParseResult {
1215
case success(content: IterableInAppContent)
1316
case failure(reason: String)
@@ -41,7 +44,7 @@ private protocol ContentFromJsonParser {
4144
}
4245

4346
struct HtmlContentParser {
44-
static func getPadding(fromInAppSettings settings: [AnyHashable: Any]?) -> UIEdgeInsets {
47+
static func getPadding(fromInAppSettings settings: [AnyHashable: Any]?) -> Padding {
4548
InAppDisplaySettingsParser.PaddingParser.getPadding(fromInAppSettings: settings)
4649
}
4750

@@ -101,6 +104,65 @@ struct HtmlContentParser {
101104
case bottom
102105
}
103106

107+
enum PaddingValue: Equatable {
108+
case percent(value: Int)
109+
case autoExpand
110+
111+
func toCGFloat() -> CGFloat {
112+
switch self {
113+
case .percent(value: let value):
114+
return CGFloat(value)
115+
case .autoExpand:
116+
return CGFloat(-1)
117+
}
118+
}
119+
120+
static func from(cgFloat: CGFloat) -> PaddingValue {
121+
switch cgFloat {
122+
case -1:
123+
return .autoExpand
124+
default:
125+
return .percent(value: Int(cgFloat))
126+
}
127+
}
128+
}
129+
130+
struct Padding: Equatable {
131+
static let zero = Padding(top: .percent(value: 0),
132+
left: 0,
133+
bottom: .percent(value: 0),
134+
right: 0)
135+
let top: PaddingValue
136+
let left: Int
137+
let bottom: PaddingValue
138+
let right: Int
139+
140+
func adjusted() -> Padding {
141+
if left + right >= 100 {
142+
return Padding(top: top,
143+
left: 0,
144+
bottom: bottom,
145+
right: 0)
146+
} else {
147+
return self
148+
}
149+
}
150+
151+
func toEdgeInsets() -> UIEdgeInsets {
152+
UIEdgeInsets(top: top.toCGFloat(),
153+
left: CGFloat(left),
154+
bottom: bottom.toCGFloat(),
155+
right: CGFloat(right))
156+
}
157+
158+
static func from(edgeInsets: UIEdgeInsets) -> Padding {
159+
Padding(top: PaddingValue.from(cgFloat: edgeInsets.top),
160+
left: Int(edgeInsets.left),
161+
bottom: PaddingValue.from(cgFloat: edgeInsets.bottom),
162+
right: Int(edgeInsets.right))
163+
}
164+
}
165+
104166
enum PaddingKey: String, JsonKeyRepresentable {
105167
var jsonKey: String {
106168
rawValue
@@ -114,50 +176,75 @@ struct HtmlContentParser {
114176

115177
/// `settings` json looks like the following
116178
/// {"bottom": {"displayOption": "AutoExpand"}, "left": {"percentage": 60}, "right": {"percentage": 60}, "top": {"displayOption": "AutoExpand"}}
117-
static func getPadding(fromInAppSettings settings: [AnyHashable: Any]?) -> UIEdgeInsets {
118-
UIEdgeInsets(top: getEdgePadding(fromInAppSettings: settings, edge: .top),
119-
left: getEdgePadding(fromInAppSettings: settings, edge: .left),
120-
bottom: getEdgePadding(fromInAppSettings: settings, edge: .bottom),
121-
right: getEdgePadding(fromInAppSettings: settings, edge: .right))
179+
static func getPadding(fromInAppSettings settings: [AnyHashable: Any]?) -> Padding {
180+
Padding(top: getEdgePaddingValue(fromInAppSettings: settings, edge: .top),
181+
left: getEdgePadding(fromInAppSettings: settings, edge: .left),
182+
bottom: getEdgePaddingValue(fromInAppSettings: settings, edge: .bottom),
183+
right: getEdgePadding(fromInAppSettings: settings, edge: .right))
122184
}
123185

124186
/// json comes in as
125187
/// `{"displayOption": "AutoExpand"}`
126188
/// or `{"percentage": 60}`
127-
/// returns `-1` for `AutoExpand` type
128-
static func decodePadding(_ value: Any?) -> Int {
189+
static func decodePaddingValue(_ value: Any?) -> PaddingValue {
129190
guard let dict = value as? [AnyHashable: Any] else {
130-
return 0
191+
return .percent(value: 0)
131192
}
132193

133194
if let displayOption = dict.getValue(for: PaddingKey.displayOption) as? String, displayOption == Self.displayOptionAutoExpand {
134-
return -1
195+
return .autoExpand
135196
} else {
136197
if let percentage = dict.getValue(for: PaddingKey.percentage) as? NSNumber {
137-
return percentage.intValue
198+
return .percent(value: Int(truncating: percentage))
138199
}
139200

201+
return .percent(value: 0)
202+
}
203+
}
204+
205+
/// json comes in as
206+
/// `{"percentage": 60}`
207+
static func decodePadding(_ value: Any?) -> Int {
208+
guard let dict = value as? [AnyHashable: Any] else {
140209
return 0
141210
}
211+
212+
if let percentage = dict.getValue(for: PaddingKey.percentage) as? NSNumber {
213+
return Int(truncating: percentage)
214+
}
215+
216+
return 0
142217
}
143-
144-
static func location(fromPadding padding: UIEdgeInsets) -> IterableMessageLocation {
145-
if padding.top == 0, padding.bottom == 0 {
218+
219+
static func location(fromPadding padding: Padding) -> IterableMessageLocation {
220+
if case .percent(let topPadding) = padding.top,
221+
case .percent(let bottomPadding) = padding.bottom,
222+
topPadding == 0,
223+
bottomPadding == 0 {
146224
return .full
147-
} else if padding.top == 0, padding.bottom < 0 {
225+
} else if case .autoExpand = padding.bottom,
226+
case .percent(let topPadding) = padding.top,
227+
topPadding == 0 {
148228
return .top
149-
} else if padding.top < 0, padding.bottom == 0 {
229+
} else if case .autoExpand = padding.top,
230+
case .percent(let bottomPadding) = padding.bottom,
231+
bottomPadding == 0 {
150232
return .bottom
151233
} else {
152234
return .center
153235
}
154236
}
155-
237+
238+
private static func getEdgePaddingValue(fromInAppSettings settings: [AnyHashable: Any]?,
239+
edge: PaddingEdge) -> PaddingValue {
240+
settings?.getValue(for: edge)
241+
.map(decodePaddingValue(_:)) ?? .percent(value: 0)
242+
}
243+
156244
private static func getEdgePadding(fromInAppSettings settings: [AnyHashable: Any]?,
157-
edge: PaddingEdge) -> CGFloat {
245+
edge: PaddingEdge) -> Int {
158246
settings?.getValue(for: edge)
159-
.map(decodePadding(_:))
160-
.map { CGFloat($0) } ?? .zero
247+
.map(decodePadding(_:)) ?? 0
161248
}
162249
}
163250
}
@@ -174,12 +261,12 @@ extension HtmlContentParser: ContentFromJsonParser {
174261
}
175262

176263
let inAppDisplaySettings = json[JsonKey.InApp.inAppDisplaySettings] as? [AnyHashable: Any]
177-
let edgeInsets = getPadding(fromInAppSettings: inAppDisplaySettings)
264+
let padding = getPadding(fromInAppSettings: inAppDisplaySettings)
178265

179266
let shouldAnimate = inAppDisplaySettings.map(Self.parseShouldAnimate(fromInAppSettings:)) ?? false
180267
let backgroundColor = inAppDisplaySettings.flatMap(Self.parseBackgroundColor(fromInAppSettings:))
181268

182-
return .success(content: IterableHtmlInAppContent(edgeInsets: edgeInsets,
269+
return .success(content: IterableHtmlInAppContent(edgeInsets: padding.toEdgeInsets(),
183270
html: html,
184271
shouldAnimate: shouldAnimate,
185272
backgroundColor: backgroundColor))

swift-sdk/Internal/InAppDisplayer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class InAppDisplayer: InAppDisplayerProtocol {
4242
*/
4343
@discardableResult static func showIterableHtmlMessage(_ htmlString: String,
4444
messageMetadata: IterableInAppMessageMetadata? = nil,
45-
padding: UIEdgeInsets = .zero) -> ShowResult {
45+
padding: Padding = .zero) -> ShowResult {
4646
guard !InAppPresenter.isPresenting else {
4747
return .notShown("In-app notification is being presented.")
4848
}
@@ -131,7 +131,7 @@ class InAppDisplayer: InAppDisplayerProtocol {
131131

132132
return showIterableHtmlMessage(content.html,
133133
messageMetadata: metadata,
134-
padding: content.edgeInsets)
134+
padding: content.padding)
135135
}
136136

137137
// deprecated - will be removed in version 6.3.x or above

swift-sdk/Internal/InAppHelper.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
// Ported to Swift by Tapash Majumder on 6/7/18.
44
// Copyright © 2018 Iterable. All rights reserved.
55
//
6-
// Utility Methods for in-app
7-
// All classes/structs are internal.
86

97
import UIKit
108

11-
// This is Internal Struct, no public methods
9+
/// Utility Methods for in-app
10+
/// All classes/structs are internal.
11+
1212
struct InAppHelper {
1313
static func getInAppMessagesFromServer(apiClient: ApiClientProtocol, number: Int) -> Future<[IterableInAppMessage], SendRequestError> {
1414
apiClient.getInAppMessages(NSNumber(value: number)).map {

0 commit comments

Comments
 (0)