vibes-react-native 5.0.0 → 5.0.1-rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/Vibes.swift +2 -3
- package/ios/VibesClient.swift +3 -4
- package/ios/VibesPluginLogger.swift +2 -5
- package/package.json +3 -2
- package/vendor/VibesPush/LICENSE +13 -0
- package/vendor/VibesPush/Sources/APIDefinition.swift +502 -0
- package/vendor/VibesPush/Sources/Actions.swift +24 -0
- package/vendor/VibesPush/Sources/AppObserver.swift +137 -0
- package/vendor/VibesPush/Sources/AuthTokenBehavior.swift +17 -0
- package/vendor/VibesPush/Sources/CombinedLogger.swift +53 -0
- package/vendor/VibesPush/Sources/ConsoleLogger.swift +97 -0
- package/vendor/VibesPush/Sources/Credential.swift +41 -0
- package/vendor/VibesPush/Sources/CredentialManager.swift +24 -0
- package/vendor/VibesPush/Sources/Date+iso8601.swift +34 -0
- package/vendor/VibesPush/Sources/Date+logPrefix.swift +20 -0
- package/vendor/VibesPush/Sources/DeviceToken.swift +15 -0
- package/vendor/VibesPush/Sources/DeviceType.swift +143 -0
- package/vendor/VibesPush/Sources/Dictionary+merged.swift +11 -0
- package/vendor/VibesPush/Sources/Event.swift +83 -0
- package/vendor/VibesPush/Sources/EventCollection.swift +36 -0
- package/vendor/VibesPush/Sources/EventTracker.swift +39 -0
- package/vendor/VibesPush/Sources/FoundationExtensions.swift +16 -0
- package/vendor/VibesPush/Sources/GitTag.swift +27 -0
- package/vendor/VibesPush/Sources/HTTP/HTTPBehavior.swift +19 -0
- package/vendor/VibesPush/Sources/HTTP/HTTPMethod.swift +20 -0
- package/vendor/VibesPush/Sources/HTTP/HTTPResource.swift +69 -0
- package/vendor/VibesPush/Sources/HTTP/HTTPResourceClient.swift +190 -0
- package/vendor/VibesPush/Sources/HTTP/JSONParser.swift +91 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/AssociatePersonOperation.swift +56 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/BaseOperation.swift +153 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/EventOperation.swift +73 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/GetGitTagsOperation.swift +61 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/GetInboxMessageOperation.swift +59 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/GetInboxMessagesOperation.swift +57 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/GetPersonOperation.swift +53 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/GetVibesAppInfoOperation.swift +45 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/MigrationCallbackOperation.swift +45 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/RegisterDeviceOperation.swift +66 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/RegisterPushOperation.swift +73 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/TrackProductActionOperation.swift +75 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/TrackPurchaseActionOperation.swift +81 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/UnregisterDeviceOperation.swift +64 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/UnregisterPushOperation.swift +72 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/UpdateDeviceOperationPatch.swift +57 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/UpdateDeviceOperationPut.swift +59 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/UpdateMessageOperation.swift +50 -0
- package/vendor/VibesPush/Sources/HTTP/Operations/VibesQueueOperation.swift +76 -0
- package/vendor/VibesPush/Sources/HTTP/ResourceResult.swift +38 -0
- package/vendor/VibesPush/Sources/HTTPURLResponse+cURL.swift +16 -0
- package/vendor/VibesPush/Sources/InboxMessage.swift +219 -0
- package/vendor/VibesPush/Sources/Info.plist +26 -0
- package/vendor/VibesPush/Sources/InputStream+readToString.swift +19 -0
- package/vendor/VibesPush/Sources/KeychainStorage.swift +64 -0
- package/vendor/VibesPush/Sources/LocalObject.swift +25 -0
- package/vendor/VibesPush/Sources/LocalStorageKeys.swift +19 -0
- package/vendor/VibesPush/Sources/LocalStorageType.swift +47 -0
- package/vendor/VibesPush/Sources/LocalValue.swift +67 -0
- package/vendor/VibesPush/Sources/PersistentEventStorage.swift +81 -0
- package/vendor/VibesPush/Sources/Person.swift +98 -0
- package/vendor/VibesPush/Sources/Product.swift +103 -0
- package/vendor/VibesPush/Sources/Purchase.swift +108 -0
- package/vendor/VibesPush/Sources/TrackedEventType.swift +48 -0
- package/vendor/VibesPush/Sources/URLRequest+cURL.swift +36 -0
- package/vendor/VibesPush/Sources/UserDefaultsStorage.swift +32 -0
- package/vendor/VibesPush/Sources/Vibes.swift +1069 -0
- package/vendor/VibesPush/Sources/VibesAPI.swift +81 -0
- package/vendor/VibesPush/Sources/VibesAPIDelegate.swift +41 -0
- package/vendor/VibesPush/Sources/VibesAppInfo.swift +77 -0
- package/vendor/VibesPush/Sources/VibesConfiguration.swift +108 -0
- package/vendor/VibesPush/Sources/VibesLogger.swift +65 -0
- package/vendor/VibesPush/Sources/VibesResult.swift +78 -0
- package/vibes-react-native.podspec +6 -2
|
@@ -0,0 +1,1069 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
// swiftlint:disable line_length
|
|
3
|
+
|
|
4
|
+
/// A closure used as a completion handler for interacting with the Vibes
|
|
5
|
+
/// object.
|
|
6
|
+
public typealias VibesCompletion<A> = (VibesResult<A>) -> Void
|
|
7
|
+
|
|
8
|
+
/// Type Alias for Get Inbox Message callback
|
|
9
|
+
public typealias GetInboxMessageCallback = (_ message: InboxMessage?, _ error: Error?) -> Void
|
|
10
|
+
|
|
11
|
+
/// Type Alias for Get Git Tags callback
|
|
12
|
+
public typealias GetGitTagsCallback = (_ tags: [GitTag], _ error: Error?) -> Void
|
|
13
|
+
|
|
14
|
+
/// Type Alias for Get Inbox Messages callback
|
|
15
|
+
public typealias GetInboxMessagesCallback = (_ messages: [InboxMessage], _ error: Error?) -> Void
|
|
16
|
+
|
|
17
|
+
/// Type Alias for update Inbox Message callback
|
|
18
|
+
public typealias UpdateMessageCallback = (_ messages: InboxMessage?, _ error: Error?) -> Void
|
|
19
|
+
|
|
20
|
+
/// Type Alias for Get Vibes Person callback
|
|
21
|
+
public typealias GetPersonCallback = (_ person: Person?, _ error: Error?) -> Void
|
|
22
|
+
|
|
23
|
+
/// Type Alias for Getting App info callback
|
|
24
|
+
public typealias GetVibesAppInfoCallback = (_ status: VibesAppInfo?, _ error: Error?) -> Void
|
|
25
|
+
|
|
26
|
+
/// Type Alias for Track Product Action callback
|
|
27
|
+
public typealias TrackProductActionCallback = (_ error: Error?) -> Void
|
|
28
|
+
|
|
29
|
+
/// Type Alias for Track Purchase Action callback
|
|
30
|
+
public typealias TrackPurchaseActionCallback = (_ error: Error?) -> Void
|
|
31
|
+
|
|
32
|
+
/// Type of storage to store credential
|
|
33
|
+
@objc public enum VibesStorageEnum: Int {
|
|
34
|
+
case KEYCHAIN
|
|
35
|
+
case USERDEFAULTS
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private var vibesInstance: Vibes!
|
|
39
|
+
|
|
40
|
+
// swiftlint:disable type_body_length
|
|
41
|
+
/// The main entry point for usage of the Vibes API.
|
|
42
|
+
@objc public class Vibes: NSObject, EventTracker {
|
|
43
|
+
/// The released version of the Vibes SDK
|
|
44
|
+
public static let SDK_VERSION = "5.0.0"
|
|
45
|
+
|
|
46
|
+
/// The app ID
|
|
47
|
+
public static let CURRENT_APP_ID = "CURRENT_APP_ID"
|
|
48
|
+
|
|
49
|
+
/// Default configuration
|
|
50
|
+
public let configuration: VibesConfiguration
|
|
51
|
+
|
|
52
|
+
/// Register push status
|
|
53
|
+
public static let REGISTER_PUSH_STATUS = "VibesRegisterPushStatus"
|
|
54
|
+
|
|
55
|
+
/// An object that can communicate with the Vibes API.
|
|
56
|
+
internal var api: VibesAPIType
|
|
57
|
+
|
|
58
|
+
/// The API to use when communicating tracking info with Vibes.
|
|
59
|
+
internal var trackingApi: VibesAPIType
|
|
60
|
+
|
|
61
|
+
// The application ID provided by Vibes to identify this application.
|
|
62
|
+
private let appId: String
|
|
63
|
+
|
|
64
|
+
/// An object that handles managing credentials
|
|
65
|
+
let credentialManager: CredentialManager
|
|
66
|
+
|
|
67
|
+
/// An object that allows for persisting data locally
|
|
68
|
+
internal var storage: LocalStorageType
|
|
69
|
+
|
|
70
|
+
/// Queue for executing http requests
|
|
71
|
+
fileprivate lazy var queue: OperationQueue = {
|
|
72
|
+
let operationQueue = OperationQueue()
|
|
73
|
+
operationQueue.maxConcurrentOperationCount = 1 // Serial queue operation
|
|
74
|
+
return operationQueue
|
|
75
|
+
}()
|
|
76
|
+
|
|
77
|
+
/// List of Vibes delegates
|
|
78
|
+
@objc public weak var delegate: VibesAPIDelegate?
|
|
79
|
+
|
|
80
|
+
/// Logger for the configured singleton, or `nil` before `configure`.
|
|
81
|
+
internal static var configuredLogger: CombinedLogger? {
|
|
82
|
+
return vibesInstance?.configuration.logger
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// The shared (singleton) instance of Vibes.
|
|
86
|
+
@objc public internal(set) static var shared: Vibes! {
|
|
87
|
+
get {
|
|
88
|
+
if vibesInstance == nil {
|
|
89
|
+
fatalError("You must call Vibes.configure before using Vibes.shared")
|
|
90
|
+
}
|
|
91
|
+
return vibesInstance
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
set(newValue) {
|
|
95
|
+
vibesInstance = newValue
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// The currently set push token, if available. This is stored in local
|
|
100
|
+
/// storage.
|
|
101
|
+
@objc public internal(set) var pushToken: String? {
|
|
102
|
+
get {
|
|
103
|
+
return storage.get(LocalStorageKeys.pushToken)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
set(newToken) {
|
|
107
|
+
storage.set(newToken, for: LocalStorageKeys.pushToken)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// A boolean to show pushToken needs to be updated.
|
|
112
|
+
/// Boolean indicating whether our servers should be notified of any changes to the APNS token.
|
|
113
|
+
/// This defaults to true, such that calling `setPushToken:fromData` or `setPushToken:token`
|
|
114
|
+
/// will automatically notify our server of the new change.
|
|
115
|
+
/// To disable this behavior, the app developer should call `disablePush()` before updating the
|
|
116
|
+
/// APNS token, and call `registerPush()` when appropriate.
|
|
117
|
+
private var isPushEnabled: Bool = true
|
|
118
|
+
|
|
119
|
+
/// Trick to detect wheter the application has been started for
|
|
120
|
+
/// executing unit tests. In that case, we don't want to send
|
|
121
|
+
/// events to Vibes backend.
|
|
122
|
+
public static let isRunningThroughUnitTest: Bool = {
|
|
123
|
+
if let testClass = NSClassFromString("XCTestCase") {
|
|
124
|
+
return true
|
|
125
|
+
} else {
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
}()
|
|
129
|
+
|
|
130
|
+
/// An observer of the application lifecycle. This is used for automatic
|
|
131
|
+
/// tracking of launch and clickthru events.
|
|
132
|
+
private let appObserver: AppObserver
|
|
133
|
+
|
|
134
|
+
/// A utility object to persist events locally until such time as they can be
|
|
135
|
+
/// sent to Vibes.
|
|
136
|
+
private let eventStorage: PersistentEventStorage
|
|
137
|
+
|
|
138
|
+
fileprivate var credential: VibesCredential? {
|
|
139
|
+
return credentialManager.currentCredential
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
///
|
|
143
|
+
/// Add a new Vibes delegate to the list of current delegates.
|
|
144
|
+
/// - parameters:
|
|
145
|
+
/// - delegate: VibesAPIDelegate
|
|
146
|
+
///
|
|
147
|
+
@objc public func set(delegate: VibesAPIDelegate) {
|
|
148
|
+
self.delegate = delegate
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
///
|
|
152
|
+
/// Get the storage according the storage type choosen by the user.
|
|
153
|
+
/// - parameters:
|
|
154
|
+
/// - type: VibesStorageType
|
|
155
|
+
/// - returns:
|
|
156
|
+
/// - LocalStorageType
|
|
157
|
+
///
|
|
158
|
+
internal class func getStorageWith(type: VibesStorageEnum) -> LocalStorageType {
|
|
159
|
+
switch type {
|
|
160
|
+
case .KEYCHAIN: return KeychainStorage(service: "Vibes")
|
|
161
|
+
case .USERDEFAULTS: return UserDefaultsStorage()
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
///
|
|
166
|
+
/// Initialize this object.
|
|
167
|
+
///
|
|
168
|
+
/// - parameters:
|
|
169
|
+
/// - appId: The application ID provided by Vibes to identify this
|
|
170
|
+
/// application
|
|
171
|
+
/// - configuration: The Vibes Configuration object
|
|
172
|
+
init(appId: String,
|
|
173
|
+
configuration: VibesConfiguration? = nil) {
|
|
174
|
+
self.configuration = configuration ?? VibesConfiguration()
|
|
175
|
+
self.appId = appId
|
|
176
|
+
let storageToUse = type(of: self).getStorageWith(type: self.configuration.storageType)
|
|
177
|
+
storage = storageToUse
|
|
178
|
+
credentialManager = CredentialManager(storage: storageToUse)
|
|
179
|
+
eventStorage = PersistentEventStorage(storage: storageToUse)
|
|
180
|
+
|
|
181
|
+
api = VibesAPI(url: self.configuration.vibesAPIURL)
|
|
182
|
+
|
|
183
|
+
trackingApi = VibesAPI(url: self.configuration.vibesTrackingAPIURL)
|
|
184
|
+
|
|
185
|
+
let userDefaults = UserDefaults.standard
|
|
186
|
+
|
|
187
|
+
if userDefaults.bool(forKey: "hasRunBefore") == false {
|
|
188
|
+
// Remove Keychain item in case the application has been reinstalled.
|
|
189
|
+
// When an app is removed, its data in the keychain remains.
|
|
190
|
+
storage.set(nil, for: LocalStorageKeys.currentCredential)
|
|
191
|
+
userDefaults.set(true, forKey: "hasRunBefore")
|
|
192
|
+
userDefaults.synchronize()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let trackedEventTypes = self.configuration.trackedEventTypes as? [TrackedEventType] ?? []
|
|
196
|
+
appObserver = AppObserver(trackedEventTypes: trackedEventTypes)
|
|
197
|
+
super.init()
|
|
198
|
+
if !Vibes.isRunningThroughUnitTest {
|
|
199
|
+
appObserver.eventTracker = self
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
///
|
|
204
|
+
/// Enables register push
|
|
205
|
+
/// This will result in sending the APNS token to our servers whenever it changes
|
|
206
|
+
///
|
|
207
|
+
@objc public func enablePush() {
|
|
208
|
+
isPushEnabled = true
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
///
|
|
212
|
+
/// Disable register push
|
|
213
|
+
/// This will result in NOT sending the APNS token to our servers whenever it changes.
|
|
214
|
+
/// By calling this, it will be up to the app developer to call registerPush() when appropriate.
|
|
215
|
+
///
|
|
216
|
+
@objc public func disablePush() {
|
|
217
|
+
isPushEnabled = false
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
///
|
|
221
|
+
/// Get the status of the device registration
|
|
222
|
+
/// - returns: Boolean
|
|
223
|
+
///
|
|
224
|
+
@objc public func isDeviceRegistered() -> Bool {
|
|
225
|
+
return credentialManager.currentCredential != nil
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
///
|
|
229
|
+
/// Get the status of the device push registration
|
|
230
|
+
/// - returns: Boolean
|
|
231
|
+
///
|
|
232
|
+
@objc public func isDevicePushRegistered() -> Bool {
|
|
233
|
+
return UserDefaults.standard.bool(forKey: Vibes.REGISTER_PUSH_STATUS)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
///
|
|
237
|
+
/// Parses the push token from the Data that Apple sends, and stores it
|
|
238
|
+
/// locally by converting the data to a hex string. Once this has been called,
|
|
239
|
+
/// `registerPush` can be called at will to enable push notifications from
|
|
240
|
+
/// Vibes.
|
|
241
|
+
///
|
|
242
|
+
/// - parameters:
|
|
243
|
+
/// - fromData: the data send from Apple and received in your AppDelegate's
|
|
244
|
+
/// `application:didRegisterForRemoteNotificationsWithDeviceToken` function.
|
|
245
|
+
///
|
|
246
|
+
@objc public func setPushToken(fromData data: Data) {
|
|
247
|
+
setPushToken(token: data.reduce("", { $0 + String(format: "%02X", $1) }))
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
///
|
|
251
|
+
/// Stores Token in local storage and if Push is enabled it pushes to vibes
|
|
252
|
+
/// servers
|
|
253
|
+
/// - Parameters:
|
|
254
|
+
/// - Optional token that is set
|
|
255
|
+
///
|
|
256
|
+
private func setPushToken(token: String?) {
|
|
257
|
+
storage.set(token, for: LocalStorageKeys.pushToken)
|
|
258
|
+
if isPushEnabled {
|
|
259
|
+
registerPush()
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
///
|
|
264
|
+
/// Notify Vibes that a push message has been tapped on or for the case of silent push, has been received.
|
|
265
|
+
///
|
|
266
|
+
/// - parameters:
|
|
267
|
+
/// - userInfo: the details from the remote notification
|
|
268
|
+
/// - timestamp: an optional date for the receipt of the push notification
|
|
269
|
+
/// (default: now)
|
|
270
|
+
///
|
|
271
|
+
@objc public func receivedPush(with userInfo: [AnyHashable: Any], at timestamp: Date = Date()) {
|
|
272
|
+
appObserver.lastNotification = (userInfo, timestamp)
|
|
273
|
+
storeTrackingData(userInfo: userInfo)
|
|
274
|
+
handleSilentMigration(userInfo)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/// Handle Silent migration
|
|
278
|
+
///
|
|
279
|
+
/// - Parameter userInfo: The push payload
|
|
280
|
+
func handleSilentMigration(_ userInfo: [AnyHashable: Any]) {
|
|
281
|
+
if let customData = userInfo["client_custom_data"] as? [String: Any], // ensure client_custom_data is supplied
|
|
282
|
+
let pushToken = customData["vibes_auto_register_token"] as? String, // ensure push token is supplied in client_custom_data
|
|
283
|
+
let migrationItemId = customData["migration_item_id"] as? String, // ensure the migration item id
|
|
284
|
+
!pushToken.isEmpty { // ensure push token is not empty
|
|
285
|
+
Vibes.shared.configuration.logger?
|
|
286
|
+
.log(LogObject(.info, message: "Migration push received. Processing ..."))
|
|
287
|
+
let registerDeviceCallback: VibesCompletion<VibesCredential> = { [weak self] result in
|
|
288
|
+
switch result {
|
|
289
|
+
case let .success(credential):
|
|
290
|
+
Vibes.shared.configuration.logger?
|
|
291
|
+
.log(LogObject(.info, message: "Migration request completed successfully. Device Id: \(credential.deviceId)"))
|
|
292
|
+
self?.setPushToken(token: pushToken)
|
|
293
|
+
self?.registerPush()
|
|
294
|
+
// trigger migration callback
|
|
295
|
+
if let appId = self?.appId {
|
|
296
|
+
self?.migrationCallback(appId: appId, migrationItemId: migrationItemId, vibesDeviceId: credential.deviceId)
|
|
297
|
+
}
|
|
298
|
+
case let .failure(error):
|
|
299
|
+
Vibes.shared.configuration.logger?
|
|
300
|
+
.log(LogObject(.error, message: "Migration attempt failed in triggering registerDevice(): \(error)"))
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
submit(operation: RegisterDeviceOperation(credentials: credentialManager,
|
|
305
|
+
api: api,
|
|
306
|
+
advertisingId: advertisingId,
|
|
307
|
+
appId: appId,
|
|
308
|
+
delegate: delegate,
|
|
309
|
+
callback: registerDeviceCallback))
|
|
310
|
+
} else {
|
|
311
|
+
Vibes.shared.configuration.logger?
|
|
312
|
+
.log(LogObject(.info, message: "Silent Register Device Failed: Valid Push Token was not supplied"))
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/// Trigger Migration Callback Http Operation
|
|
317
|
+
///
|
|
318
|
+
/// - Parameters:
|
|
319
|
+
/// - appId: The app id
|
|
320
|
+
/// - migrationItemId: Migration item id
|
|
321
|
+
/// - vibesDeviceId: Vibes Device id
|
|
322
|
+
func migrationCallback(appId: String, migrationItemId: String, vibesDeviceId: String) {
|
|
323
|
+
let migrationCallback: VibesCompletion<Void> = { result in
|
|
324
|
+
switch result {
|
|
325
|
+
case .success:
|
|
326
|
+
Vibes.shared.configuration.logger?
|
|
327
|
+
.log(LogObject(.info, message: "Migration Callback Successfull."))
|
|
328
|
+
case let .failure(error):
|
|
329
|
+
Vibes.shared.configuration.logger?
|
|
330
|
+
.log(LogObject(.error, message: "Couldn't process migration callback: \(error)"))
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
submit(operation: MigrationCallbackOperation(credentials: credentialManager,
|
|
334
|
+
api: api,
|
|
335
|
+
advertisingId: advertisingId,
|
|
336
|
+
appId: appId,
|
|
337
|
+
migrationItemId: migrationItemId,
|
|
338
|
+
vibesDeviceId: vibesDeviceId,
|
|
339
|
+
callback: migrationCallback))
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
///
|
|
343
|
+
/// Submit a new operation (REG_DEVICE, UNREG_DEVICE ...)
|
|
344
|
+
/// If the current queue isn't empty, the operation will be executed
|
|
345
|
+
/// when the last operation of the queue is finished.
|
|
346
|
+
///
|
|
347
|
+
fileprivate func submit<A>(operation: BaseOperation<A>) {
|
|
348
|
+
if !queue.operations.isEmpty {
|
|
349
|
+
operation.addDependency(queue.operations.last!)
|
|
350
|
+
}
|
|
351
|
+
queue.addOperation(operation)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// MARK: OPERATIONS
|
|
355
|
+
|
|
356
|
+
///////////////////////////////////////////// OPERATIONS /////////////////////////////////////////////
|
|
357
|
+
|
|
358
|
+
///
|
|
359
|
+
/// Checks and compares the Vibes SDK version in use.
|
|
360
|
+
///
|
|
361
|
+
internal func checkLatestVersion(_ callback: GetGitTagsCallback? = nil) {
|
|
362
|
+
let gitTagsCallback: GetGitTagsCallback = { tags, error in
|
|
363
|
+
if let error = error {
|
|
364
|
+
self.configuration.logger?
|
|
365
|
+
.log(LogObject(.error, message: "Unable to fetch latest version of SDK --> \(error)"))
|
|
366
|
+
callback?(tags, error)
|
|
367
|
+
return
|
|
368
|
+
}
|
|
369
|
+
if let latest = tags.first {
|
|
370
|
+
if latest.name.compare(Vibes.SDK_VERSION) == .orderedDescending {
|
|
371
|
+
self.configuration.logger?
|
|
372
|
+
.log(LogObject(.info, message: "Latest version of the Vibes SDK is v\(latest.name). We recommend upgrading by following the instructions here: https://developer.vibes.com/display/APIs/Installing+the+iOS+Push+Notifications+SDK."))
|
|
373
|
+
} else {
|
|
374
|
+
self.configuration.logger?
|
|
375
|
+
.log(LogObject(.info, message: "Your Vibes SDK v\(Vibes.SDK_VERSION) is up to date."))
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
callback?(tags, error)
|
|
379
|
+
}
|
|
380
|
+
submit(operation: GetGitTagsOperation(credentials: credentialManager,
|
|
381
|
+
storage: storage,
|
|
382
|
+
api: api,
|
|
383
|
+
advertisingId: advertisingId,
|
|
384
|
+
appId: appId,
|
|
385
|
+
callback: gitTagsCallback))
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
private var advertisingId: String {
|
|
389
|
+
return configuration.advertisingId ?? ""
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
///
|
|
393
|
+
/// Registers this device with Vibes.
|
|
394
|
+
///
|
|
395
|
+
@objc public func registerDevice() {
|
|
396
|
+
let registerDeviceCallback: VibesCompletion<VibesCredential> = { [weak self] result in
|
|
397
|
+
switch result {
|
|
398
|
+
case .success:
|
|
399
|
+
self?.configuration.logger?.log(LogObject(.info, message: "Device registered successfully"))
|
|
400
|
+
case let .failure(error):
|
|
401
|
+
self?.configuration.logger?.log(LogObject(.error, message: "Device registered failed: \(error)"))
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
submit(operation: RegisterDeviceOperation(credentials: credentialManager,
|
|
406
|
+
api: api,
|
|
407
|
+
advertisingId: advertisingId,
|
|
408
|
+
appId: appId,
|
|
409
|
+
delegate: delegate,
|
|
410
|
+
callback: registerDeviceCallback))
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
///
|
|
414
|
+
/// Unregisters this device with Vibes.
|
|
415
|
+
///
|
|
416
|
+
@objc public func unregisterDevice() {
|
|
417
|
+
let unRegisterDeviceCallback: VibesCompletion<Void> = { [weak self] result in
|
|
418
|
+
switch result {
|
|
419
|
+
case .success:
|
|
420
|
+
self?.configuration.logger?.log(LogObject(.info, message: "Device unregistered successfully"))
|
|
421
|
+
case let .failure(error):
|
|
422
|
+
self?.configuration.logger?.log(LogObject(.error, message: "Device unregistered failed: \(error)"))
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
submit(operation: UnregisterDeviceOperation(credentials: credentialManager,
|
|
427
|
+
api: api,
|
|
428
|
+
advertisingId: advertisingId,
|
|
429
|
+
appId: appId,
|
|
430
|
+
delegate: delegate,
|
|
431
|
+
callback: unRegisterDeviceCallback))
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
///
|
|
435
|
+
/// Updates this device with Vibes.
|
|
436
|
+
///
|
|
437
|
+
/// - parameters:
|
|
438
|
+
/// - lat: latitude of the User location.
|
|
439
|
+
/// - long: longitude of the User location
|
|
440
|
+
/// - updateCredentials: a boolean indicating if it's a token update or device info update. Specify `false` if not certain
|
|
441
|
+
///
|
|
442
|
+
@objc public func updateDevice(lat: NSNumber? = nil, long: NSNumber? = nil, updateCredentials: Bool) {
|
|
443
|
+
let updateDeviceCallback: VibesCompletion<Void> = { [weak self] result in
|
|
444
|
+
switch result {
|
|
445
|
+
case .success:
|
|
446
|
+
self?.configuration.logger?.log(LogObject(.info, message: "Device updated successfully"))
|
|
447
|
+
case let .failure(error):
|
|
448
|
+
self?.configuration.logger?.log(LogObject(.error, message: "Update device failed: \(error)"))
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
var location: (Double, Double)?
|
|
453
|
+
if let lat = lat as? Double,
|
|
454
|
+
let long = long as? Double {
|
|
455
|
+
location = (lat: lat, long: long)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if updateCredentials {
|
|
459
|
+
submit(operation: UpdateDeviceOperationPut(credentials: credentialManager,
|
|
460
|
+
api: api,
|
|
461
|
+
advertisingId: advertisingId,
|
|
462
|
+
appId: appId,
|
|
463
|
+
delegate: delegate,
|
|
464
|
+
location: location,
|
|
465
|
+
callback: updateDeviceCallback))
|
|
466
|
+
} else {
|
|
467
|
+
submit(operation: UpdateDeviceOperationPatch(credentials: credentialManager,
|
|
468
|
+
api: api,
|
|
469
|
+
advertisingId: advertisingId,
|
|
470
|
+
appId: appId,
|
|
471
|
+
delegate: delegate,
|
|
472
|
+
location: location,
|
|
473
|
+
callback: updateDeviceCallback))
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
///
|
|
478
|
+
/// Associate this device with a person
|
|
479
|
+
///
|
|
480
|
+
/// - paramters:
|
|
481
|
+
/// - externalPersonId: the third party identifier for the person, stored
|
|
482
|
+
/// by vibes as external_person_id
|
|
483
|
+
@objc public func associatePerson(externalPersonId: String) {
|
|
484
|
+
let associatePersonCallback: VibesCompletion<Void> = { [weak self] result in
|
|
485
|
+
switch result {
|
|
486
|
+
case .success:
|
|
487
|
+
self?.configuration.logger?.log(LogObject(.info, message: "Associated person with device successfully"))
|
|
488
|
+
case let .failure(error):
|
|
489
|
+
self?.configuration.logger?.log(LogObject(.error, message: "Register Device failed: \(error)"))
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
submit(operation: AssociatePersonOperation(credentials: credentialManager,
|
|
494
|
+
api: api,
|
|
495
|
+
advertisingId: advertisingId,
|
|
496
|
+
appId: appId,
|
|
497
|
+
delegate: delegate,
|
|
498
|
+
callback: associatePersonCallback,
|
|
499
|
+
externalPersonId: externalPersonId))
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
///
|
|
503
|
+
/// Register push notifications for this device with Vibes.
|
|
504
|
+
///
|
|
505
|
+
@objc public func registerPush() {
|
|
506
|
+
let registerPushCallback: VibesCompletion<Void> = { [weak self] result in
|
|
507
|
+
switch result {
|
|
508
|
+
case .success:
|
|
509
|
+
self?.configuration.logger?.log(LogObject(.info, message: "Push token registered successfully"))
|
|
510
|
+
case let .failure(error):
|
|
511
|
+
self?.configuration.logger?.log(LogObject(.error, message: "Push token registration failed: \(error)"))
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
submit(operation: RegisterPushOperation(credentials: credentialManager,
|
|
516
|
+
storage: storage,
|
|
517
|
+
api: api,
|
|
518
|
+
advertisingId: advertisingId,
|
|
519
|
+
appId: appId,
|
|
520
|
+
delegate: delegate,
|
|
521
|
+
callback: registerPushCallback))
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
///
|
|
525
|
+
/// Unregister push notifications for this device with Vibes.
|
|
526
|
+
///
|
|
527
|
+
@objc public func unregisterPush() {
|
|
528
|
+
let unRegisterPushCallback: VibesCompletion<Void> = { [weak self] result in
|
|
529
|
+
switch result {
|
|
530
|
+
case .success:
|
|
531
|
+
self?.configuration.logger?.log(LogObject(.info, message: "Push token unregistered successfully"))
|
|
532
|
+
case let .failure(error):
|
|
533
|
+
self?.configuration.logger?.log(LogObject(.error, message: "Push token unregistered failed: \(error)"))
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
submit(operation: UnregisterPushOperation(credentials: credentialManager,
|
|
538
|
+
storage: storage,
|
|
539
|
+
api: api,
|
|
540
|
+
advertisingId: advertisingId,
|
|
541
|
+
appId: appId,
|
|
542
|
+
delegate: delegate,
|
|
543
|
+
callback: unRegisterPushCallback))
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
///
|
|
547
|
+
/// Get Vibes Person Information.
|
|
548
|
+
///
|
|
549
|
+
/// - Parameter callback: GetPersonCallback (Person?, Error?)
|
|
550
|
+
@objc public func getPerson(_ callback: GetPersonCallback?) {
|
|
551
|
+
submit(operation: GetPersonOperation(credentials: credentialManager,
|
|
552
|
+
storage: storage,
|
|
553
|
+
api: api,
|
|
554
|
+
advertisingId: advertisingId,
|
|
555
|
+
appId: appId,
|
|
556
|
+
callback: completionWithLogger(.getPerson, callback)))
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
///
|
|
560
|
+
/// Retrieve inbox messages status for an app
|
|
561
|
+
///
|
|
562
|
+
/// - Parameter callback: GetVibesAppInfoCallback (Status, Error?)
|
|
563
|
+
@objc public func getAppInfo(_ callback: GetVibesAppInfoCallback?) {
|
|
564
|
+
submit(operation: GetVibesAppInfoOperation(credentials: credentialManager,
|
|
565
|
+
storage: storage,
|
|
566
|
+
api: api,
|
|
567
|
+
advertisingId: advertisingId,
|
|
568
|
+
appId: appId,
|
|
569
|
+
callback: completionWithLogger(.getAppInfo, callback)))
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/// Retrieve inbox messages for a Person
|
|
573
|
+
///
|
|
574
|
+
/// - Parameters:
|
|
575
|
+
/// - callback: GetInboxMessagesCallback ([InboxMessage], Error?)
|
|
576
|
+
@objc public func fetchInboxMessages(_ callback: GetInboxMessagesCallback?) {
|
|
577
|
+
let getPersonCallback: GetPersonCallback = { person, error in
|
|
578
|
+
if let person = person, let personKey = person.personKey {
|
|
579
|
+
self.storage.set(person, for: LocalStorageKeys.currentPerson)
|
|
580
|
+
self.submit(operation: GetInboxMessagesOperation(credentials: self.credentialManager,
|
|
581
|
+
storage: self.storage,
|
|
582
|
+
api: self.api,
|
|
583
|
+
advertisingId: self.advertisingId,
|
|
584
|
+
appId: self.appId,
|
|
585
|
+
personKey: personKey,
|
|
586
|
+
callback: self.completionWithLogger(.fetchMessages, callback)))
|
|
587
|
+
} else {
|
|
588
|
+
callback?([], error) // error retrieving person info
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
submit(operation: GetPersonOperation(credentials: credentialManager,
|
|
592
|
+
storage: storage,
|
|
593
|
+
api: api,
|
|
594
|
+
advertisingId: advertisingId,
|
|
595
|
+
appId: appId,
|
|
596
|
+
callback: getPersonCallback))
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/// Retrieve a single inbox message for a Person
|
|
600
|
+
///
|
|
601
|
+
/// - Parameters:
|
|
602
|
+
/// - messageUID: unique UID for the message to be edited
|
|
603
|
+
/// - callback: GetInboxMessageCallback (InboxMessage, Error?)
|
|
604
|
+
@objc public func fetchInboxMessage(messageUID: String, _ callback: @escaping GetInboxMessageCallback) {
|
|
605
|
+
if let person = storage.get(LocalStorageKeys.currentPerson),
|
|
606
|
+
let personKey = person.personKey {
|
|
607
|
+
submit(operation: GetInboxMessageOperation(credentials: credentialManager,
|
|
608
|
+
storage: storage,
|
|
609
|
+
api: api,
|
|
610
|
+
advertisingId: advertisingId,
|
|
611
|
+
appId: appId,
|
|
612
|
+
personKey: personKey,
|
|
613
|
+
messageUID: messageUID,
|
|
614
|
+
callback: completionWithLogger(.fetchMessage, callback)))
|
|
615
|
+
} else { // we dint find personKey, lets retrieve it then retry
|
|
616
|
+
let getPersonCallback: GetPersonCallback = { person, error in
|
|
617
|
+
if let person = person, let personKey = person.personKey {
|
|
618
|
+
self.storage.set(person, for: LocalStorageKeys.currentPerson)
|
|
619
|
+
self.submit(operation: GetInboxMessageOperation(credentials: self.credentialManager,
|
|
620
|
+
storage: self.storage,
|
|
621
|
+
api: self.api,
|
|
622
|
+
advertisingId: self.advertisingId,
|
|
623
|
+
appId: self.appId,
|
|
624
|
+
personKey: personKey,
|
|
625
|
+
messageUID: messageUID,
|
|
626
|
+
callback: callback))
|
|
627
|
+
} else {
|
|
628
|
+
callback(nil, error) // error retrieving person info
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
submit(operation: GetPersonOperation(credentials: credentialManager,
|
|
632
|
+
storage: storage,
|
|
633
|
+
api: api,
|
|
634
|
+
advertisingId: advertisingId,
|
|
635
|
+
appId: appId,
|
|
636
|
+
callback: getPersonCallback))
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/// Mark inbox message as read
|
|
641
|
+
///
|
|
642
|
+
/// - Parameters:
|
|
643
|
+
/// - messageUID: unique UID for the message to be edited
|
|
644
|
+
/// - callback: UpdateMessageCallback (InboxMessage?, Error?)
|
|
645
|
+
@objc public func markInboxMessageAsRead(messageUID: String, _ callback: @escaping UpdateMessageCallback) {
|
|
646
|
+
updateInboxMessage(messageUID: messageUID,
|
|
647
|
+
payload: ["read": true as AnyObject],
|
|
648
|
+
callback: completionWithLogger(.markMessageRead, callback))
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/// Expire inbox message at specified date
|
|
652
|
+
///
|
|
653
|
+
/// - Parameters:
|
|
654
|
+
/// - messageUID: unique UID for the message to be edited
|
|
655
|
+
/// - date: The date to expire the inbox message. if not set, defaults to now.
|
|
656
|
+
/// - callback: UpdateMessageCallback (InboxMessage?, Error?)
|
|
657
|
+
@objc public func expireInboxMessage(messageUID: String, date: Date = Date(), _ callback: @escaping UpdateMessageCallback) {
|
|
658
|
+
updateInboxMessage(messageUID: messageUID,
|
|
659
|
+
payload: ["expires_at": date.iso8601WithUpdatingTimezone as AnyObject],
|
|
660
|
+
callback: completionWithLogger(.markMessageExpired, callback))
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/// Records the fact that an inbox message has been viewed in detail by the user.
|
|
664
|
+
///
|
|
665
|
+
/// - Parameter inboxMessage: The viewed inbox message
|
|
666
|
+
@objc public func onInboxMessageOpen(inboxMessage: InboxMessage) {
|
|
667
|
+
let event = Event(eventType: .inboxopen, properties: inboxMessage.eventMap())
|
|
668
|
+
track(event: event)
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/// Records the fact that an inbox messages have been fetched by the user.
|
|
672
|
+
///
|
|
673
|
+
@objc public func onInboxMessagesFetched() {
|
|
674
|
+
let event = Event(eventType: .inboxfetch, properties: [:])
|
|
675
|
+
track(event: event)
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/// Update inbox message
|
|
679
|
+
///
|
|
680
|
+
/// - Parameters:
|
|
681
|
+
/// - messageUID: unique UID for the message to be edited
|
|
682
|
+
/// - payload: payload to be updated in the backend
|
|
683
|
+
/// - callback: UpdateMessageCallback (InboxMessage?, Error?)
|
|
684
|
+
internal func updateInboxMessage(messageUID: String, payload: VibesJSONDictionary, callback: @escaping UpdateMessageCallback) {
|
|
685
|
+
// callback, here on success, we use person key to fetch inbox messages
|
|
686
|
+
if let person = storage.get(LocalStorageKeys.currentPerson),
|
|
687
|
+
let personKey = person.personKey {
|
|
688
|
+
submit(operation: UpdateMessageOperation(credentials: credentialManager,
|
|
689
|
+
storage: storage,
|
|
690
|
+
api: api,
|
|
691
|
+
advertisingId: advertisingId,
|
|
692
|
+
appId: appId,
|
|
693
|
+
personKey: personKey,
|
|
694
|
+
messageUID: messageUID,
|
|
695
|
+
payload: payload,
|
|
696
|
+
callback: callback))
|
|
697
|
+
} else { // we dint find personKey, lets retrieve it then retry
|
|
698
|
+
let getPersonCallback: GetPersonCallback = { person, error in
|
|
699
|
+
if let person = person, let personKey = person.personKey {
|
|
700
|
+
self.storage.set(person, for: LocalStorageKeys.currentPerson)
|
|
701
|
+
self.submit(operation: UpdateMessageOperation(credentials: self.credentialManager,
|
|
702
|
+
storage: self.storage,
|
|
703
|
+
api: self.api,
|
|
704
|
+
advertisingId: self.advertisingId,
|
|
705
|
+
appId: self.appId,
|
|
706
|
+
personKey: personKey,
|
|
707
|
+
messageUID: messageUID,
|
|
708
|
+
payload: payload,
|
|
709
|
+
callback: callback))
|
|
710
|
+
} else {
|
|
711
|
+
callback(nil, error) // error retrieving person info
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
submit(operation: GetPersonOperation(credentials: credentialManager,
|
|
715
|
+
storage: storage,
|
|
716
|
+
api: api,
|
|
717
|
+
advertisingId: advertisingId,
|
|
718
|
+
appId: appId,
|
|
719
|
+
callback: getPersonCallback))
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
///
|
|
724
|
+
/// Tracks events with Vibes.
|
|
725
|
+
///
|
|
726
|
+
/// - parameters:
|
|
727
|
+
/// - events: the Events to track, e.g. [.launch]
|
|
728
|
+
/// - completion: a handler for receiving the result of tracking the events
|
|
729
|
+
/// (it returns an Array of the events that have been tracked)
|
|
730
|
+
///
|
|
731
|
+
internal func track(events incomingEvents: [Event], completion: VibesCompletion<[Event]>?) {
|
|
732
|
+
let allowedEvents = handleDuplicateLaunchEventsBySecond(events: incomingEvents, existing: eventStorage.storedEvents)
|
|
733
|
+
eventStorage.tracking(events: allowedEvents) { outgoingEvents, trackingCompletion in
|
|
734
|
+
self.submit(operation: EventOperation(credentials: self.credentialManager,
|
|
735
|
+
api: self.api,
|
|
736
|
+
advertisingId: advertisingId,
|
|
737
|
+
appId: self.appId,
|
|
738
|
+
completion: completion,
|
|
739
|
+
outgoingEvents: outgoingEvents,
|
|
740
|
+
trackCompletion: trackingCompletion))
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/// Track CLICK Product Action
|
|
745
|
+
///
|
|
746
|
+
/// - Parameters:
|
|
747
|
+
/// - product: the product being tracked
|
|
748
|
+
/// - activityUid: optional activityUid
|
|
749
|
+
/// - completion: Completion Callback
|
|
750
|
+
@objc public func trackClick(product: Product, activityUid: String? = nil, completion: TrackProductActionCallback?) {
|
|
751
|
+
trackActionCall(action: ProductAction.click, product: product, activityUid: activityUid, completion: completionWithLogger(.trackProductClick, completion))
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/// Track ADD Product Action
|
|
755
|
+
///
|
|
756
|
+
/// - Parameters:
|
|
757
|
+
/// - product: the product being tracked
|
|
758
|
+
/// - activityUid: optional activityUid
|
|
759
|
+
/// - completion: Completion Callback
|
|
760
|
+
@objc public func trackAdd(product: Product, activityUid: String? = nil, completion: TrackProductActionCallback?) {
|
|
761
|
+
trackActionCall(action: ProductAction.add, product: product, activityUid: activityUid, completion: completionWithLogger(.trackProductAdd, completion))
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/// Track Product Action Call
|
|
765
|
+
///
|
|
766
|
+
/// - Parameters:
|
|
767
|
+
/// - action: the type of ProductAction
|
|
768
|
+
/// - product: the product being tracked
|
|
769
|
+
/// - activityUid: optional activityUid
|
|
770
|
+
/// - completion: Completion Callback
|
|
771
|
+
internal func trackActionCall(action: ProductAction, product: Product, activityUid: String?, completion: TrackProductActionCallback?) {
|
|
772
|
+
if let trackingData = getTrackingData() {
|
|
773
|
+
submit(operation: TrackProductActionOperation(credentials: credentialManager, storage: storage, api: trackingApi, advertisingId: advertisingId, appId: appId, action: action, product: product, data: trackingData, activityUid: activityUid, callback: completion))
|
|
774
|
+
} else {
|
|
775
|
+
completion?(VibesError.other("No Tracking Data"))
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/// Track Detail
|
|
780
|
+
///
|
|
781
|
+
/// - Parameters:
|
|
782
|
+
/// - product: the product being tracked
|
|
783
|
+
/// - activityUid: optional activityUid
|
|
784
|
+
/// - completion: Completion Callback
|
|
785
|
+
@objc public func trackDetail(product: Product, activityUid: String? = nil, completion: TrackProductActionCallback?) {
|
|
786
|
+
trackProductActionCall(action: .detail, product: product, activityUid: activityUid, completion: completionWithLogger(.trackDetail, completion))
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
internal enum SDKAction<T> {
|
|
790
|
+
case fetchMessage
|
|
791
|
+
case fetchMessages
|
|
792
|
+
case markMessageRead
|
|
793
|
+
case markMessageExpired
|
|
794
|
+
case getPerson
|
|
795
|
+
case getAppInfo
|
|
796
|
+
case trackProductClick
|
|
797
|
+
case trackProductAdd
|
|
798
|
+
case trackDetail
|
|
799
|
+
|
|
800
|
+
func successLogObject(_ data: T) -> LogObject {
|
|
801
|
+
switch self {
|
|
802
|
+
case .fetchMessage:
|
|
803
|
+
return LogObject(.info, message: "Inbox Message fetched successfully")
|
|
804
|
+
case .fetchMessages:
|
|
805
|
+
return LogObject(.info, message: "Inbox messages fetched successfully")
|
|
806
|
+
case .markMessageRead:
|
|
807
|
+
return LogObject(.info, message: "Inbox message marked as read successfully")
|
|
808
|
+
case .markMessageExpired:
|
|
809
|
+
return LogObject(.info, message: "Inbox message marked as expired successfully")
|
|
810
|
+
case .getPerson:
|
|
811
|
+
return LogObject(.info, message: "Successfully retrieved person details")
|
|
812
|
+
case .getAppInfo:
|
|
813
|
+
if let info = data as? VibesAppInfo {
|
|
814
|
+
let state = info.inboxEnabled ? "enabled" : "disabled"
|
|
815
|
+
return LogObject(.info, message: "App Inbox is \(state).")
|
|
816
|
+
}
|
|
817
|
+
return LogObject(.info, message: "App Inbox status unknown.")
|
|
818
|
+
case .trackProductClick:
|
|
819
|
+
return LogObject(.info, message: "Product CLICK tracked successfully")
|
|
820
|
+
case .trackProductAdd:
|
|
821
|
+
return LogObject(.info, message: "Product ADD tracked successfully")
|
|
822
|
+
case .trackDetail:
|
|
823
|
+
return LogObject(.info, message: "Product DETAIL tracked successfully")
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
func errorLogObject(_ error: Error) -> LogObject {
|
|
828
|
+
switch self {
|
|
829
|
+
case .fetchMessage:
|
|
830
|
+
return LogObject(.error, message: "Fetch Messsage Failed: \(error.localizedDescription)")
|
|
831
|
+
case .fetchMessages:
|
|
832
|
+
return LogObject(.error, message: "Fetch Messsages Failed: \(error)")
|
|
833
|
+
case .markMessageRead:
|
|
834
|
+
return LogObject(.error, message: "Inbox message marked as read failed: \(error)")
|
|
835
|
+
case .markMessageExpired:
|
|
836
|
+
return LogObject(.error, message: "Inbox message marked as expired failed: \(error)")
|
|
837
|
+
case .getPerson:
|
|
838
|
+
return LogObject(.error, message: "Retrieve person details failed: \(error)")
|
|
839
|
+
case .getAppInfo:
|
|
840
|
+
return LogObject(.error, message: "Retrieve Vibes App Info failed: \(error)")
|
|
841
|
+
case .trackProductClick:
|
|
842
|
+
return LogObject(.error, message: "Product CLICK tracking failed: \(error)")
|
|
843
|
+
case .trackProductAdd:
|
|
844
|
+
return LogObject(.error, message: "Product ADD tracking failed: \(error)")
|
|
845
|
+
case .trackDetail:
|
|
846
|
+
return LogObject(.error, message: "Product DETAIL tracking failed: \(error)")
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// MARK: Tracking
|
|
852
|
+
|
|
853
|
+
/// Wraps a callback to handle logging
|
|
854
|
+
///
|
|
855
|
+
/// - Parameters:
|
|
856
|
+
/// - action: SDK Action to log
|
|
857
|
+
/// - callback: The callback to wrap
|
|
858
|
+
/// - Returns: The callback with logging capabilities
|
|
859
|
+
internal func completionWithLogger<T>(_ action: SDKAction<T>, _ callback: ((T, Error?) -> Void)?) -> (T, Error?) -> Void {
|
|
860
|
+
let completion: (T, Error?) -> Void = { (result: T, error: Error?) in
|
|
861
|
+
if let error = error {
|
|
862
|
+
self.configuration.logger?.log(action.errorLogObject(error))
|
|
863
|
+
callback?(result, error)
|
|
864
|
+
return
|
|
865
|
+
}
|
|
866
|
+
self.configuration.logger?.log(action.successLogObject(result))
|
|
867
|
+
callback?(result, error)
|
|
868
|
+
}
|
|
869
|
+
return completion
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/// Wraps a void callback to handle logging
|
|
873
|
+
///
|
|
874
|
+
/// - Parameters:
|
|
875
|
+
/// - action: SDK Action to log
|
|
876
|
+
/// - callback: The callback to wrap
|
|
877
|
+
/// - Returns: The callback with logging capabilities
|
|
878
|
+
internal func completionWithLogger(_ action: SDKAction<Void>, _ callback: ((Error?) -> Void)?) -> (Error?) -> Void {
|
|
879
|
+
let completion: (Error?) -> Void = { (error: Error?) in
|
|
880
|
+
if let error = error {
|
|
881
|
+
self.configuration.logger?.log(action.errorLogObject(error))
|
|
882
|
+
callback?(error)
|
|
883
|
+
return
|
|
884
|
+
}
|
|
885
|
+
self.configuration.logger?.log(action.successLogObject(()))
|
|
886
|
+
callback?(error)
|
|
887
|
+
}
|
|
888
|
+
return completion
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/// Store Tracking Data
|
|
892
|
+
///
|
|
893
|
+
/// - Parameters:
|
|
894
|
+
/// - userInfo: Client_app_data
|
|
895
|
+
@objc fileprivate func storeTrackingData(userInfo: [AnyHashable: Any]) {
|
|
896
|
+
let getPersonCallback: GetPersonCallback = { person, _ in
|
|
897
|
+
if let person = person, let personKey = person.personKey {
|
|
898
|
+
if var clientAppData = userInfo["client_app_data"] as? VibesTrackingData {
|
|
899
|
+
clientAppData["person_key"] = personKey
|
|
900
|
+
let userDefaults = UserDefaults.standard
|
|
901
|
+
userDefaults.set(clientAppData, forKey: "vibesTagData")
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
getPerson(getPersonCallback)
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/// Get Tracking Data
|
|
909
|
+
///
|
|
910
|
+
/// - Returns: VibesTrackingData
|
|
911
|
+
@objc public func getTrackingData() -> VibesTrackingData? {
|
|
912
|
+
let userDefaults = UserDefaults.standard
|
|
913
|
+
return userDefaults.object(forKey: "vibesTagData") as? VibesTrackingData
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/// Track PURCHASE Product Action
|
|
917
|
+
///
|
|
918
|
+
/// - Parameters:
|
|
919
|
+
/// - activityUid: optional activityUid
|
|
920
|
+
/// - completion: Completion Callback
|
|
921
|
+
@objc public func trackPurchase(purchase: Purchase, activityUid: String?, completion: TrackProductActionCallback?) {
|
|
922
|
+
trackPurchaseActionCall(action: PurchaseAction.purchase,
|
|
923
|
+
purchase: purchase,
|
|
924
|
+
activityUid: activityUid,
|
|
925
|
+
completion: completion)
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/// Track Product Action Call
|
|
929
|
+
///
|
|
930
|
+
/// - Parameters:
|
|
931
|
+
/// - action: the type of Product Action
|
|
932
|
+
/// - product: the product being tracked
|
|
933
|
+
/// - activityUid: optional activityUid
|
|
934
|
+
/// - completion: Completion Callback
|
|
935
|
+
internal func trackProductActionCall(action: ProductAction, product: Product, activityUid: String?, completion: TrackProductActionCallback?) {
|
|
936
|
+
if let trackingData = getTrackingData() {
|
|
937
|
+
submit(operation: TrackProductActionOperation(credentials: credentialManager,
|
|
938
|
+
storage: storage,
|
|
939
|
+
api: trackingApi,
|
|
940
|
+
advertisingId: advertisingId,
|
|
941
|
+
appId: appId,
|
|
942
|
+
action: action,
|
|
943
|
+
product: product,
|
|
944
|
+
data: trackingData,
|
|
945
|
+
activityUid: activityUid,
|
|
946
|
+
callback: completion))
|
|
947
|
+
} else {
|
|
948
|
+
completion?(VibesError.other("No Tracking Data"))
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/// Track Purchase Action Call
|
|
953
|
+
///
|
|
954
|
+
/// - Parameters:
|
|
955
|
+
/// - action: the type of purchase Action
|
|
956
|
+
/// - activityUid: optional activityUid
|
|
957
|
+
/// - completion: Completion Callback
|
|
958
|
+
internal func trackPurchaseActionCall(action: PurchaseAction, purchase: Purchase, activityUid: String?, completion: TrackPurchaseActionCallback?) {
|
|
959
|
+
if let trackingData = getTrackingData() {
|
|
960
|
+
submit(operation: TrackPurchaseActionOperation(credentials: credentialManager,
|
|
961
|
+
storage: storage,
|
|
962
|
+
api: trackingApi,
|
|
963
|
+
advertisingId: advertisingId,
|
|
964
|
+
appId: appId,
|
|
965
|
+
action: action,
|
|
966
|
+
purchase: purchase,
|
|
967
|
+
data: trackingData,
|
|
968
|
+
activityUid: activityUid,
|
|
969
|
+
callback: completion))
|
|
970
|
+
} else {
|
|
971
|
+
completion?(VibesError.other("No Tracking Data"))
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
extension Vibes {
|
|
977
|
+
///
|
|
978
|
+
/// Configure the shared Vibes instance. This must be called before using any
|
|
979
|
+
/// of the functionality of Vibes, like registering your device.
|
|
980
|
+
///
|
|
981
|
+
/// - Parameters:
|
|
982
|
+
/// - appId: The Vibes App ID
|
|
983
|
+
/// - configuration: Contains all the properties to configure Vibes
|
|
984
|
+
///
|
|
985
|
+
@objc public static func configure(appId: String, configuration: VibesConfiguration? = nil) {
|
|
986
|
+
guard !appId.isEmpty else {
|
|
987
|
+
fatalError("App ID must not be null or empty")
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
shared = Vibes(appId: appId, configuration: configuration)
|
|
991
|
+
Vibes.shared.configuration.logger?.log(LogObject(.info, message: "\(ConsoleLogger.versionInfoLogPrefix)\(Vibes.SDK_VERSION)"))
|
|
992
|
+
if _isDebugAssertConfiguration() {
|
|
993
|
+
Vibes.shared.checkLatestVersion()
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
let userDefaults = UserDefaults.standard
|
|
997
|
+
let currentAppId = userDefaults.string(forKey: CURRENT_APP_ID) ?? ""
|
|
998
|
+
|
|
999
|
+
if currentAppId != appId {
|
|
1000
|
+
// Save the new appId and log the new appId
|
|
1001
|
+
userDefaults.set(appId, forKey: CURRENT_APP_ID)
|
|
1002
|
+
userDefaults.synchronize()
|
|
1003
|
+
Vibes.shared.configuration.logger?.log(LogObject(.info, message: "AppId has changed to: \(appId)"))
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/// Will first ensure the events are unique, stripped of any dupicates based on equality test.
|
|
1008
|
+
/// Will then go through events and compare with any existing stored events,
|
|
1009
|
+
/// and handle any duplicate based on seconds difference in timestamp
|
|
1010
|
+
///
|
|
1011
|
+
/// - Parameters:
|
|
1012
|
+
/// - events: The events to check
|
|
1013
|
+
/// - existing: The existing events list to check against
|
|
1014
|
+
/// - Returns: Events with any duplicates removed
|
|
1015
|
+
internal func handleDuplicateLaunchEventsBySecond(events: [Event], existing: [Event]) -> [Event] {
|
|
1016
|
+
let uniqueEvents = events.firstUniqueElements
|
|
1017
|
+
// filter all stored launch events
|
|
1018
|
+
let storedLaunchSorted = existing.filter({ event -> Bool in
|
|
1019
|
+
event.isLaunchEvent
|
|
1020
|
+
}).sorted { $0.timestamp > $1.timestamp }
|
|
1021
|
+
// get first from sorted list, which should be the latest one
|
|
1022
|
+
if let latestStoredLaunchEvent = storedLaunchSorted.first {
|
|
1023
|
+
let launchEvents = uniqueEvents.filter { event -> Bool in
|
|
1024
|
+
event.isLaunchEvent
|
|
1025
|
+
}
|
|
1026
|
+
if launchEvents.isEmpty {
|
|
1027
|
+
return uniqueEvents
|
|
1028
|
+
}
|
|
1029
|
+
let otherEvents = uniqueEvents.filter { event -> Bool in
|
|
1030
|
+
!event.isLaunchEvent
|
|
1031
|
+
}
|
|
1032
|
+
// find the accepted launch events to keep
|
|
1033
|
+
let acceptedLaunch = launchEvents.filter { event -> Bool in
|
|
1034
|
+
if let seconds = latestStoredLaunchEvent.timestamp.secondsDifference(with: event.timestamp),
|
|
1035
|
+
seconds > 1 { // add it to accepted list
|
|
1036
|
+
return true
|
|
1037
|
+
}
|
|
1038
|
+
print("Filtered out a launch event, similar exists within the second: \(event.encodeJSON())")
|
|
1039
|
+
return false // else dont add to accepted list
|
|
1040
|
+
}
|
|
1041
|
+
let result = otherEvents + acceptedLaunch
|
|
1042
|
+
return result
|
|
1043
|
+
}
|
|
1044
|
+
return uniqueEvents
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
public extension Sequence where Element: Equatable {
|
|
1049
|
+
/// Get the unique elements off this array, based on Eqality test
|
|
1050
|
+
var firstUniqueElements: [Element] {
|
|
1051
|
+
reduce(into: []) { uniqueElements, element in
|
|
1052
|
+
if !uniqueElements.contains(element) {
|
|
1053
|
+
uniqueElements.append(element)
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
extension Date {
|
|
1060
|
+
/// Get seconds difference between two dates
|
|
1061
|
+
/// - Parameter date: The other date
|
|
1062
|
+
/// - Returns: Seconds difference
|
|
1063
|
+
func secondsDifference(with date: Date) -> Int? {
|
|
1064
|
+
let calendar = Calendar.current
|
|
1065
|
+
let components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: self, to: date)
|
|
1066
|
+
let seconds = components.second
|
|
1067
|
+
return seconds
|
|
1068
|
+
}
|
|
1069
|
+
}
|