Skip to content

Commit 261a92d

Browse files
Merge pull request #51 from Iterable/feature/mob-179-default-trigger-type
[MOB-179] - Persist triggers properly
2 parents 7b5f68c + 63d9db8 commit 261a92d

File tree

7 files changed

+209
-46
lines changed

7 files changed

+209
-46
lines changed

Tests/swift-sdk-swift-tests/InAppHelperTests.swift

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,71 @@ class InAppHelperTests: XCTestCase {
239239
XCTAssertEqual(obtained?["bool1"] as? Bool, true)
240240
}
241241

242+
func testInAppPayloadWithNoTrigger() {
243+
let payload = """
244+
{
245+
"inAppMessages" : [
246+
{
247+
"content" : {
248+
"html" : "<a href=\\"http://somewhere.com\\">Click here</a>"
249+
},
250+
"messageId" : "messageIdxxx",
251+
"campaignId" : "campaignIdxxx"
252+
}
253+
]
254+
}
255+
""".toJsonDict()
256+
let messages = InAppHelper.inAppMessages(fromPayload: payload, internalApi: IterableAPI.internalImplementation!)
257+
XCTAssertEqual(messages[0].trigger.type, IterableInAppTriggerType.immediate)
258+
}
259+
260+
func testInAppPayloadWithKnownTrigger() {
261+
let payload = """
262+
{
263+
"inAppMessages" : [
264+
{
265+
"content" : {
266+
"html" : "<a href=\\"http://somewhere.com\\">Click here</a>"
267+
},
268+
"messageId" : "messageIdxxx",
269+
"campaignId" : "campaignIdxxx",
270+
"trigger" : {
271+
"type" : "event",
272+
"something" : "else"
273+
}
274+
}
275+
]
276+
}
277+
""".toJsonDict()
278+
let messages = InAppHelper.inAppMessages(fromPayload: payload, internalApi: IterableAPI.internalImplementation!)
279+
XCTAssertEqual(messages[0].trigger.type, IterableInAppTriggerType.event)
280+
XCTAssertEqual(messages[0].trigger.dict["something"] as? String, "else")
281+
}
282+
283+
func testInAppPayloadWithUnKnownTrigger() {
284+
let payload = """
285+
{
286+
"inAppMessages" : [
287+
{
288+
"content" : {
289+
"html" : "<a href=\\"http://somewhere.com\\">Click here</a>"
290+
},
291+
"messageId" : "messageIdxxx",
292+
"campaignId" : "campaignIdxxx",
293+
"trigger" : {
294+
"type" : "myNewKind",
295+
"myPayload" : {"var1" : "val1"}
296+
}
297+
}
298+
]
299+
}
300+
""".toJsonDict()
301+
let messages = InAppHelper.inAppMessages(fromPayload: payload, internalApi: IterableAPI.internalImplementation!)
302+
XCTAssertEqual(messages[0].trigger.type, IterableInAppTriggerType.never)
303+
let dict = messages[0].trigger.dict as! [String : Any]
304+
TestUtils.validateMatch(keyPath: KeyPath("myPayload.var1"), value: "val1", inDictionary: dict, message: "Expected to find val1")
305+
}
306+
242307
private func createInAppPayload(withExtraInfo extraInfo: [AnyHashable : Any]) -> [AnyHashable : Any] {
243308
return [
244309
"inAppMessages" : [[

Tests/swift-sdk-swift-tests/InAppTests.swift

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -704,17 +704,21 @@ class InAppTests: XCTestCase {
704704

705705
func testFilePersistence() {
706706
let payload = ["inAppMessages" : [
707-
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 1, trigger: .event, expiresAt: Date()),
708-
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 2, trigger: .immediate, expiresAt: Date()),
709-
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 3, trigger: .never),
710-
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 4, trigger: .immediate, expiresAt: Date()),
707+
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 1, trigger: IterableInAppTrigger(dict: ["type" : "event", "details" : "some event details"]), expiresAt: Date()),
708+
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 2, triggerType: .immediate, expiresAt: Date()),
709+
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 3, triggerType: .never),
710+
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 4, trigger: IterableInAppTrigger(dict: ["type" : "newEventType", "nested" : ["var1" : "val1"]]), expiresAt: Date()),
711711
]]
712712

713713
let messages = InAppHelper.inAppMessages(fromPayload: payload, internalApi: IterableAPI.internalImplementation!)
714714
let persister = InAppFilePersister()
715715
persister.persist(messages)
716716
let obtained = persister.getMessages()
717717
XCTAssertEqual(messages.description, obtained.description)
718+
719+
XCTAssertEqual(obtained[3].trigger.type, IterableInAppTriggerType.never)
720+
let dict = obtained[3].trigger.dict as! [String : Any]
721+
TestUtils.validateMatch(keyPath: KeyPath("nested.var1"), value: "val1", inDictionary: dict, message: "Expected to find val1 in persisted dictionary")
718722
persister.clear()
719723
}
720724

@@ -913,10 +917,10 @@ class InAppTests: XCTestCase {
913917
let expectation4 = expectation(description: "call immediate trigger 2")
914918

915919
let payload = ["inAppMessages" : [
916-
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 1, trigger: .event),
917-
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 2, trigger: .immediate),
918-
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 3, trigger: .never),
919-
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 4, trigger: .immediate),
920+
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 1, triggerType: .event),
921+
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 2, triggerType: .immediate),
922+
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 3, triggerType: .never),
923+
TestInAppPayloadGenerator.createOneInAppDictWithUrl(index: 4, triggerType: .immediate),
920924
]]
921925

922926
let mockInAppSynchronizer = MockInAppSynchronizer()
@@ -971,6 +975,12 @@ class InAppTests: XCTestCase {
971975
}
972976
}
973977

978+
extension IterableInAppTrigger {
979+
public override var description: String {
980+
return "type: \(self.type)"
981+
}
982+
}
983+
974984
extension IterableHtmlInAppContent {
975985
open override var description: String {
976986
return IterableUtil.describe("contentType", contentType,

Tests/swift-sdk-swift-tests/TestInAppPayloadGenerator.swift

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,22 @@ import Foundation
99
@testable import IterableSDK
1010

1111
struct TestInAppPayloadGenerator {
12-
static func createPayloadWithUrl(numMessages: Int, trigger: IterableInAppTriggerType = .immediate, expiresAt: Date? = nil) -> [AnyHashable : Any] {
13-
return createPayloadWithUrl(indices: 1...numMessages, trigger: trigger, expiresAt: expiresAt)
12+
static func createPayloadWithUrl(numMessages: Int, triggerType: IterableInAppTriggerType = .immediate, expiresAt: Date? = nil) -> [AnyHashable : Any] {
13+
return createPayloadWithUrl(indices: 1...numMessages, triggerType: triggerType, expiresAt: expiresAt)
1414
}
1515

16-
static func createPayloadWithUrl<T: Sequence>(indices: T, trigger: IterableInAppTriggerType = .immediate, expiresAt: Date? = nil) -> [AnyHashable : Any] where T.Element == Int {
16+
static func createPayloadWithUrl<T: Sequence>(indices: T, triggerType: IterableInAppTriggerType = .immediate, expiresAt: Date? = nil) -> [AnyHashable : Any] where T.Element == Int {
1717
return [
1818
"inAppMessages" : indices.reduce(into: [[AnyHashable : Any]]()) { (result, index) in
19-
result.append(createOneInAppDictWithUrl(index: index, trigger: trigger, expiresAt: expiresAt))
19+
result.append(createOneInAppDictWithUrl(index: index, triggerType: triggerType, expiresAt: expiresAt))
2020
}
2121
]
2222
}
2323

24-
static func createPayloadWithCustomAction(numMessages: Int, trigger: IterableInAppTriggerType = .immediate) -> [AnyHashable : Any] {
24+
static func createPayloadWithCustomAction(numMessages: Int, triggerType: IterableInAppTriggerType = .immediate) -> [AnyHashable : Any] {
2525
return [
2626
"inAppMessages" : (1...numMessages).reduce(into: [[AnyHashable : Any]]()) { (result, index) in
27-
result.append(createOneInAppDictWithCustomAction(index: index, trigger: trigger))
27+
result.append(createOneInAppDictWithCustomAction(index: index, triggerType: triggerType))
2828
}
2929
]
3030
}
@@ -53,38 +53,46 @@ struct TestInAppPayloadGenerator {
5353
return Int(String(campaignId.suffix(1)))!
5454
}
5555

56-
static func createOneInAppDictWithUrl(index: Int, trigger: IterableInAppTriggerType, expiresAt: Date? = nil) -> [AnyHashable : Any] {
56+
static func createOneInAppDictWithUrl(index: Int, trigger: IterableInAppTrigger?, expiresAt: Date? = nil) -> [AnyHashable : Any] {
5757
return createOneInAppDict(withHref: getClickUrl(index: index), index: index, trigger: trigger, expiresAt: expiresAt)
5858
}
5959

60-
static func createOneInAppDictWithCustomAction(index: Int, trigger: IterableInAppTriggerType) -> [AnyHashable : Any] {
61-
return createOneInAppDict(withHref: getCustomActionUrl(index: index), index: index, trigger: trigger, expiresAt: nil)
60+
static func createOneInAppDictWithUrl(index: Int, triggerType: IterableInAppTriggerType, expiresAt: Date? = nil) -> [AnyHashable : Any] {
61+
return createOneInAppDict(withHref: getClickUrl(index: index), index: index, trigger: trigger(fromTriggerType: triggerType), expiresAt: expiresAt)
6262
}
6363

64-
private static func createOneInAppDict(withHref href: String, index: Int, trigger: IterableInAppTriggerType, expiresAt: Date?) -> [AnyHashable : Any] {
64+
static func createOneInAppDictWithCustomAction(index: Int, triggerType: IterableInAppTriggerType) -> [AnyHashable : Any] {
65+
return createOneInAppDict(withHref: getCustomActionUrl(index: index), index: index, trigger: trigger(fromTriggerType: triggerType), expiresAt: nil)
66+
}
67+
68+
private static func createOneInAppDict(withHref href: String, index: Int, trigger: IterableInAppTrigger?, expiresAt: Date?) -> [AnyHashable : Any] {
69+
var dict = createOneInAppDict(withHref: href, index: index)
6570
if let expiresAt = expiresAt {
66-
var dict = createOneInAppDict(withHref: href, index: index, trigger: trigger)
6771
dict["expiresAt"] = toMillisecondsSinceEpoch(date: expiresAt)
68-
return dict
69-
} else {
70-
return createOneInAppDict(withHref: href, index: index, trigger: trigger)
7172
}
73+
if let trigger = trigger {
74+
dict["trigger"] = trigger.dict
75+
}
76+
return dict
7277
}
7378

74-
private static func createOneInAppDict(withHref href: String, index: Int, trigger: IterableInAppTriggerType) -> [AnyHashable : Any] {
79+
private static func createOneInAppDict(withHref href: String, index: Int) -> [AnyHashable : Any] {
7580
return [
7681
"content" : [
7782
"html" : "<a href='\(href)'>Click Here</a>",
7883
"inAppDisplaySettings" : ["backgroundAlpha" : 0.5, "left" : ["percentage" : 60], "right" : ["percentage" : 60], "bottom" : ["displayOption" : "AutoExpand"], "top" : ["displayOption" : "AutoExpand"]],
7984
"payload" : ["channelName" : "inBox", "title" : "Product 1 Available", "date" : "2018-11-14T14:00:00:00.32Z"]
8085
],
8186
"messageId" : getMessageId(index: index),
82-
"campaignId" : getCampaignId(index: index),
83-
"trigger" : ["type" : String(describing: trigger)]
87+
"campaignId" : getCampaignId(index: index)
8488
]
8589
}
8690

8791
private static func toMillisecondsSinceEpoch(date: Date) -> Int {
8892
return Int(date.timeIntervalSince1970 * 1000)
8993
}
94+
95+
private static func trigger(fromTriggerType triggerType: IterableInAppTriggerType) -> IterableInAppTrigger {
96+
return IterableInAppTrigger(dict: ["type" : String(describing: triggerType)])
97+
}
9098
}

swift-sdk/Internal/InAppHelper.swift

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ class InAppDisplayer : InAppDisplayerProtocol {
3838
}
3939
}
4040

41+
extension IterableInAppTriggerType {
42+
static let defaultTriggerType = IterableInAppTriggerType.immediate // default is what is chosen by default
43+
static let undefinedTriggerType = IterableInAppTriggerType.never // undefined is what we select if payload has new trigger type
44+
}
45+
4146
class InAppSilentPushSynchronizer : InAppSynchronizerProtocol {
4247
weak var internalApi: IterableAPIInternal?
4348
weak var inAppSyncDelegate: InAppSynchronizerDelegate?
@@ -304,7 +309,7 @@ struct InAppHelper {
304309
let channelName: String
305310
let messageId: String
306311
let campaignId: String
307-
let trigger: IterableInAppTriggerType
312+
let trigger: IterableInAppTrigger
308313
let expiresAt: Date?
309314
let edgeInsets: UIEdgeInsets
310315
let backgroundAlpha: Double
@@ -377,24 +382,12 @@ struct InAppHelper {
377382
return Date(timeIntervalSince1970: seconds)
378383
}
379384

380-
private static func parseTrigger(fromTriggerElement element: [AnyHashable : Any]?) -> IterableInAppTriggerType {
385+
private static func parseTrigger(fromTriggerElement element: [AnyHashable : Any]?) -> IterableInAppTrigger {
381386
guard let element = element else {
382-
return .immediate
383-
}
384-
guard let triggerTypeString = element[.ITBL_IN_APP_TRIGGER_TYPE] as? String else {
385-
return .immediate
386-
}
387-
388-
switch triggerTypeString.lowercased() {
389-
case String(describing: IterableInAppTriggerType.immediate).lowercased():
390-
return .immediate
391-
case String(describing: IterableInAppTriggerType.event).lowercased():
392-
return .event
393-
case String(describing: IterableInAppTriggerType.never).lowercased():
394-
return .never
395-
default:
396-
return .immediate
387+
return .defaultTrigger // if element is missing return default which is immediate
397388
}
389+
390+
return IterableInAppTrigger(dict: element)
398391
}
399392

400393
private static func parseExtraInfo(fromContent content: [AnyHashable : Any]) -> [AnyHashable : Any]? {

swift-sdk/Internal/InAppManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ extension InAppManager : InAppSynchronizerDelegate {
298298
}
299299

300300
private func getFirstProcessableMessage() -> IterableInAppMessage? {
301-
return messagesMap.values.filter({ $0.processed == false && $0.trigger == .immediate }).first
301+
return messagesMap.values.filter({ $0.processed == false && $0.trigger.type == .immediate }).first
302302
}
303303

304304
private func updateMessage(_ message: IterableInAppMessage, processed: Bool, consumed: Bool = false) {

swift-sdk/Internal/InAppPersistence.swift

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,72 @@ extension IterableInAppTriggerType : CustomStringConvertible {
4949
}
5050
}
5151

52+
extension IterableInAppTriggerType {
53+
// Internal
54+
static func from(string: String) -> IterableInAppTriggerType {
55+
switch string.lowercased() {
56+
case String(describing: IterableInAppTriggerType.immediate).lowercased():
57+
return .immediate
58+
case String(describing: IterableInAppTriggerType.event).lowercased():
59+
return .event
60+
case String(describing: IterableInAppTriggerType.never).lowercased():
61+
return .never
62+
default:
63+
return .undefinedTriggerType // if string is not known
64+
}
65+
}
66+
}
67+
68+
extension IterableInAppTrigger {
69+
static let defaultTrigger = IterableInAppTrigger(dict: createDefaultTriggerDict())
70+
static let undefinedTrigger = IterableInAppTrigger(dict: createUndefinedTriggerDict())
71+
72+
private static func createDefaultTriggerDict() -> [AnyHashable : Any] {
73+
return [.ITBL_IN_APP_TRIGGER_TYPE : String(describing: IterableInAppTriggerType.defaultTriggerType)]
74+
}
75+
76+
private static func createUndefinedTriggerDict() -> [AnyHashable : Any] {
77+
return [.ITBL_IN_APP_TRIGGER_TYPE : String(describing: IterableInAppTriggerType.undefinedTriggerType)]
78+
}
79+
}
80+
81+
extension IterableInAppTrigger : Codable {
82+
enum CodingKeys: String, CodingKey {
83+
case data
84+
}
85+
86+
public convenience init(from decoder: Decoder) {
87+
guard let container = try? decoder.container(keyedBy: CodingKeys.self) else {
88+
self.init(dict: IterableInAppTrigger.createDefaultTriggerDict())
89+
return
90+
}
91+
92+
guard let data = (try? container.decode(Data.self, forKey: .data)) else {
93+
self.init(dict: IterableInAppTrigger.createDefaultTriggerDict())
94+
return
95+
}
96+
97+
do {
98+
if let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [AnyHashable : Any] {
99+
self.init(dict: dict)
100+
} else {
101+
self.init(dict: IterableInAppTrigger.createDefaultTriggerDict())
102+
}
103+
104+
} catch (let error) {
105+
ITBError(error.localizedDescription)
106+
self.init(dict: IterableInAppTrigger.createDefaultTriggerDict())
107+
}
108+
}
109+
110+
public func encode(to encoder: Encoder) throws {
111+
var container = encoder.container(keyedBy: CodingKeys.self)
112+
if let data = try? JSONSerialization.data(withJSONObject: dict, options: []) {
113+
try? container.encode(data, forKey: .data)
114+
}
115+
}
116+
}
117+
52118
extension IterableInAppMessage : Codable {
53119
enum CodingKeys: String, CodingKey {
54120
case messageId
@@ -86,7 +152,7 @@ extension IterableInAppMessage : Codable {
86152
let messageId = (try? container.decode(String.self, forKey: .messageId)) ?? ""
87153
let campaignId = (try? container.decode(String.self, forKey: .campaignId)) ?? ""
88154
let channelName = (try? container.decode(String.self, forKey: .channelName)) ?? ""
89-
let trigger = (try? container.decode(IterableInAppTriggerType.self, forKey: .trigger)) ?? .immediate
155+
let trigger = (try? container.decode(IterableInAppTrigger.self, forKey: .trigger)) ?? .undefinedTrigger
90156
let expiresAt = (try? container.decode(Date.self, forKey: .expiresAt))
91157
let content = (try? container.decode(IterableHtmlInAppContent.self, forKey: .content)) ?? IterableHtmlInAppContent(edgeInsets: .zero, backgroundAlpha: 0.0, html: "")
92158
let extraInfoData = try? container.decode(Data.self, forKey: .extraInfo)

0 commit comments

Comments
 (0)