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,137 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
extension String {
|
|
4
|
+
func toDictionary() -> [String: Any]? {
|
|
5
|
+
if let data = data(using: .utf8) {
|
|
6
|
+
do {
|
|
7
|
+
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
|
|
8
|
+
} catch {
|
|
9
|
+
Vibes.shared.configuration.logger?.log(error: error)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return nil
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// Observes app lifecycle events and tracks events.
|
|
17
|
+
class AppObserver: NSObject {
|
|
18
|
+
/// A list of the events that should be tracked
|
|
19
|
+
private let trackedEventTypes: [TrackedEventType]
|
|
20
|
+
|
|
21
|
+
/// The event tracker that should be used to track events (usally a Vibes
|
|
22
|
+
/// instance)
|
|
23
|
+
weak var eventTracker: EventTracker?
|
|
24
|
+
|
|
25
|
+
/// The user info and date of the last notification that was received.[
|
|
26
|
+
var lastNotification: (userInfo: [AnyHashable: Any], timestamp: Date)? {
|
|
27
|
+
didSet {
|
|
28
|
+
if let userInfo = lastNotification?.userInfo, shouldTrackPushReceive() {
|
|
29
|
+
trackPushReceive(with: userInfo)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// The threshold of time between receiving a notification and the app
|
|
35
|
+
/// becoming active that we should consider a "clickthru"
|
|
36
|
+
private let clickthruThreshold: TimeInterval // seconds
|
|
37
|
+
|
|
38
|
+
/// Initialize this object.
|
|
39
|
+
///
|
|
40
|
+
/// - parameters:
|
|
41
|
+
/// - trackedEventTypes: the type of events that should be tracked
|
|
42
|
+
/// (default: all)
|
|
43
|
+
/// - clickthruThreshold - the threshold of time between receiving a
|
|
44
|
+
/// notification and the app becoming active that we should consider
|
|
45
|
+
/// a "clickthru" (default: 1 second)
|
|
46
|
+
init(trackedEventTypes: [TrackedEventType] = TrackedEventType.all, clickthruThreshold: TimeInterval = 1) {
|
|
47
|
+
self.clickthruThreshold = clickthruThreshold
|
|
48
|
+
self.trackedEventTypes = trackedEventTypes
|
|
49
|
+
|
|
50
|
+
super.init()
|
|
51
|
+
|
|
52
|
+
NotificationCenter.default.addObserver(
|
|
53
|
+
self,
|
|
54
|
+
selector: #selector(applicationDidBecomeActive(_:)),
|
|
55
|
+
name: UIApplication.didBecomeActiveNotification,
|
|
56
|
+
object: nil
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Run on deallocation.
|
|
61
|
+
deinit {
|
|
62
|
+
NotificationCenter.default.removeObserver(self)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Tracks when the application became active. If there's a push
|
|
66
|
+
/// notification, this fires after it is received by the AppDelegate.
|
|
67
|
+
///
|
|
68
|
+
/// - parameters:
|
|
69
|
+
/// - the notification fired by iOS
|
|
70
|
+
@objc func applicationDidBecomeActive(_ notification: Notification) {
|
|
71
|
+
if trackedEventTypes.contains(.launch) {
|
|
72
|
+
trackLaunch()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if let (userInfo, _) = lastNotification, shouldTrackClickThru() {
|
|
76
|
+
trackClickthru(with: userInfo)
|
|
77
|
+
}
|
|
78
|
+
lastNotification = nil
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// Determines if a clickthru should be tracked, based on the user vibes configuration
|
|
82
|
+
private func shouldTrackClickThru() -> Bool {
|
|
83
|
+
return trackedEventTypes.contains(.clickthru)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Determines if a .pushReceive should be tracked, based on the user vibes configuration
|
|
87
|
+
private func shouldTrackPushReceive() -> Bool {
|
|
88
|
+
return trackedEventTypes.contains(.pushReceive)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// Tracks a launch event.
|
|
92
|
+
private func trackLaunch() {
|
|
93
|
+
let event = Event(eventType: .launch, properties: [:])
|
|
94
|
+
eventTracker?.track(event: event)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
func eventProps(with userInfo: [AnyHashable: Any]) -> [AnyHashable: Any] {
|
|
98
|
+
var properties: [AnyHashable: Any] = [:]
|
|
99
|
+
|
|
100
|
+
if let messageUniqueId = userInfo["message_uid"] as? String {
|
|
101
|
+
properties["message_uid"] = messageUniqueId
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if let clientAppData = userInfo["client_app_data"] as? [AnyHashable: Any] {
|
|
105
|
+
if let activityType = clientAppData["activity_type"] as? String {
|
|
106
|
+
properties["activity_type"] = activityType
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if let activityUid = clientAppData["activity_uid"] as? String {
|
|
110
|
+
properties["activity_uid"] = activityUid
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return properties
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// Tracks a clickthru event.
|
|
119
|
+
///
|
|
120
|
+
/// - parameters:
|
|
121
|
+
/// - userInfo: the details from the push notification to track
|
|
122
|
+
private func trackClickthru(with userInfo: [AnyHashable: Any]) {
|
|
123
|
+
var properties: [AnyHashable: Any] = eventProps(with: userInfo)
|
|
124
|
+
let event = Event(eventType: .clickthru, properties: properties)
|
|
125
|
+
eventTracker?.track(event: event)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// Tracks a push_receive event.
|
|
129
|
+
///
|
|
130
|
+
/// - parameters:
|
|
131
|
+
/// - userInfo: the details from the push notification to track
|
|
132
|
+
private func trackPushReceive(with userInfo: [AnyHashable: Any]) {
|
|
133
|
+
var properties: [AnyHashable: Any] = eventProps(with: userInfo)
|
|
134
|
+
let event = Event(eventType: .pushReceive, properties: properties)
|
|
135
|
+
eventTracker?.track(event: event)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
/// A behavior for adding the necessary authentication token for requests to the
|
|
3
|
+
/// Vibes API.
|
|
4
|
+
struct AuthTokenBehavior: HTTPBehavior {
|
|
5
|
+
/// An authentication token to send in an request header.
|
|
6
|
+
let token: String?
|
|
7
|
+
|
|
8
|
+
/// Modifies the request by adding an `Authorization` header.
|
|
9
|
+
///
|
|
10
|
+
/// - parameters:
|
|
11
|
+
/// - request: the request to modify
|
|
12
|
+
func modifyRequest(request: inout URLRequest) {
|
|
13
|
+
if let token = self.token {
|
|
14
|
+
request.setValue("MobileAppToken \(token)", forHTTPHeaderField: "Authorization")
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//
|
|
2
|
+
// CombinedLogger.swift
|
|
3
|
+
// VibesPush-iOS
|
|
4
|
+
//
|
|
5
|
+
// Created by Moin' Victor on 20/08/2020.
|
|
6
|
+
// Copyright © 2020 Vibes. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
|
|
11
|
+
/// A Logger that writes to the `ConsoleLogger` if enabled, as well as to any custom Logger provider to `VibesConfigration` during init.
|
|
12
|
+
@objc public class CombinedLogger: NSObject, VibesLogger {
|
|
13
|
+
/// The custom logger provided in `VibesConfigration` during init.
|
|
14
|
+
public var customLogger: VibesLogger?
|
|
15
|
+
|
|
16
|
+
/// The console logger to use
|
|
17
|
+
public var consoleLogger: ConsoleLogger? {
|
|
18
|
+
return VibesConfiguration.consoleLogger
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Initialize this object.
|
|
22
|
+
///
|
|
23
|
+
/// - Parameters:
|
|
24
|
+
/// - customLogger: The custom logger provided in `VibesConfigration` during init.
|
|
25
|
+
init(customLogger: VibesLogger?) {
|
|
26
|
+
super.init()
|
|
27
|
+
self.customLogger = customLogger
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// Logs a message to the console if the `logObject.level` matches or is above the configured log `level`
|
|
31
|
+
///
|
|
32
|
+
/// - Parameters:
|
|
33
|
+
/// - logObject: The Log object with log `level` and specified log `message`.
|
|
34
|
+
public func log(_ logObject: LogObject) {
|
|
35
|
+
consoleLogger?.log(logObject)
|
|
36
|
+
customLogger?.log(logObject)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public func log(request: URLRequest) {
|
|
40
|
+
consoleLogger?.log(request: request)
|
|
41
|
+
customLogger?.log(request: request)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public func log(response: URLResponse, data: Data?) {
|
|
45
|
+
consoleLogger?.log(response: response, data: data)
|
|
46
|
+
customLogger?.log(response: response, data: data)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public func log(error: Error) {
|
|
50
|
+
consoleLogger?.log(error: error)
|
|
51
|
+
customLogger?.log(error: error)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// A simple Logger that writes to the Xcode console.
|
|
4
|
+
@objc public class ConsoleLogger: NSObject, VibesLogger {
|
|
5
|
+
/// Prefix for logging SDK initialization
|
|
6
|
+
static let versionInfoLogPrefix = "Initializing Vibes SDK v"
|
|
7
|
+
|
|
8
|
+
/// The level for this logger
|
|
9
|
+
private var level: LogLevel
|
|
10
|
+
|
|
11
|
+
/// Initialize this object.
|
|
12
|
+
///
|
|
13
|
+
/// - Parameters:
|
|
14
|
+
/// - level: the level for this logger
|
|
15
|
+
init(level: LogLevel = .verbose) {
|
|
16
|
+
self.level = level
|
|
17
|
+
super.init()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// Logs a message to the console if the `logObject.level` matches or is above the configured log `level`
|
|
21
|
+
///
|
|
22
|
+
/// - Parameters:
|
|
23
|
+
/// - logObject: The Log object with log `level` and specified log `message`.
|
|
24
|
+
public func log(_ logObject: LogObject) {
|
|
25
|
+
if logObject.level.rawValue >= level.rawValue {
|
|
26
|
+
printLogMessage(logObject.level, message: logObject.message)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// Logs an HTTP request to the console.
|
|
31
|
+
///
|
|
32
|
+
/// - Parameters:
|
|
33
|
+
/// - request: the HTTP request to log
|
|
34
|
+
public func log(request: URLRequest) {
|
|
35
|
+
switch level {
|
|
36
|
+
case .verbose:
|
|
37
|
+
log(LogObject(.verbose, message: "Request: \(request.toString())"))
|
|
38
|
+
default:
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Logs an HTTP response with its accompanying data to the console.
|
|
44
|
+
///
|
|
45
|
+
/// - Parameters:
|
|
46
|
+
/// - response: the HTTP response to log
|
|
47
|
+
/// - data: the data received, if any
|
|
48
|
+
public func log(response: URLResponse, data: Data? = nil) {
|
|
49
|
+
guard let response = response as? HTTPURLResponse else { return }
|
|
50
|
+
|
|
51
|
+
switch level {
|
|
52
|
+
case .verbose:
|
|
53
|
+
log(LogObject(.verbose, message: "Response: \(response.responseCodeDescription)"))
|
|
54
|
+
log(data: data)
|
|
55
|
+
default:
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Logs an error the console.
|
|
61
|
+
///
|
|
62
|
+
/// - Parameters:
|
|
63
|
+
/// - error: the error that occurred
|
|
64
|
+
public func log(error: Error) {
|
|
65
|
+
log(LogObject(.error, message: "Error: \(error.localizedDescription)"))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/// Logs data to the console, if it exists.
|
|
69
|
+
///
|
|
70
|
+
/// - parameters:
|
|
71
|
+
/// - data: the Data of the HTTP response
|
|
72
|
+
private func log(data: Data?) {
|
|
73
|
+
guard let data = data else { return }
|
|
74
|
+
|
|
75
|
+
do {
|
|
76
|
+
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
|
|
77
|
+
let pretty = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
|
|
78
|
+
|
|
79
|
+
if let string = NSString(data: pretty, encoding: String.Encoding.utf8.rawValue) {
|
|
80
|
+
log(LogObject(.verbose, message: "JSON: \(string)"))
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
if let string = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
|
|
84
|
+
log(LogObject(.verbose, message: "Data: \(string)"))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Prints out the log message with template `[TIMESTAMP] VibesPush/[logLevel]: <MSG>`
|
|
90
|
+
///
|
|
91
|
+
/// - Parameters:
|
|
92
|
+
/// - msgLogLevel: The log level for the message
|
|
93
|
+
/// - message: The message to print on the log
|
|
94
|
+
func printLogMessage(_ msgLogLevel: LogLevel = .verbose, message: String) {
|
|
95
|
+
print("\(Date.logPrefix) VibesPush/\(msgLogLevel.logPrefix): \(message)")
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/// A value object to represent the credentials needed to interact with the
|
|
2
|
+
/// Vibes API.
|
|
3
|
+
public struct VibesCredential {
|
|
4
|
+
/// A unique identifier for the device, provided by Vibes when registering.
|
|
5
|
+
public let deviceId: String
|
|
6
|
+
|
|
7
|
+
/// A unique, transient token for the device. The combination of `deviceId`
|
|
8
|
+
/// and `authToken` is used to authenticate most calls in the `VibesAPI`.
|
|
9
|
+
public let authToken: String
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
extension VibesCredential: JSONDecodable {
|
|
13
|
+
init?(json: VibesJSONDictionary) {
|
|
14
|
+
guard let device = json["device"] as? VibesJSONDictionary,
|
|
15
|
+
let deviceId = device["vibes_device_id"] as? String,
|
|
16
|
+
let authToken = json["auth_token"] as? String else { return nil }
|
|
17
|
+
|
|
18
|
+
self.deviceId = deviceId
|
|
19
|
+
self.authToken = authToken
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
extension VibesCredential: Equatable {
|
|
24
|
+
public static func == (lhs: VibesCredential, rhs: VibesCredential) -> Bool {
|
|
25
|
+
return lhs.deviceId == rhs.deviceId && lhs.authToken == rhs.authToken
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
extension VibesCredential: LocalObjectType {
|
|
30
|
+
public var attributes: [String: Any] {
|
|
31
|
+
return ["id": deviceId, "token": authToken]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public init?(attributes: [String: Any]) {
|
|
35
|
+
guard let id = attributes["id"] as? String,
|
|
36
|
+
let token = attributes["token"] as? String else { return nil }
|
|
37
|
+
|
|
38
|
+
self.deviceId = id
|
|
39
|
+
self.authToken = token
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/// Handles storing credentials, keeping track of the current Credential, etc.
|
|
2
|
+
class CredentialManager {
|
|
3
|
+
/// An object for storing data in some kind of local storage.
|
|
4
|
+
private let storage: LocalStorageType
|
|
5
|
+
|
|
6
|
+
/// The currently-active Credential
|
|
7
|
+
var currentCredential: VibesCredential? {
|
|
8
|
+
get {
|
|
9
|
+
return storage.get(LocalStorageKeys.currentCredential)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
set {
|
|
13
|
+
storage.set(newValue, for: LocalStorageKeys.currentCredential)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// Initialize this object.
|
|
18
|
+
///
|
|
19
|
+
/// - parameters:
|
|
20
|
+
/// - storage: an object that allows storing data locally
|
|
21
|
+
init(storage: LocalStorageType) {
|
|
22
|
+
self.storage = storage
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public extension Formatter {
|
|
4
|
+
/// A date formatter for generating ISO8601-compatible timestamp strings.
|
|
5
|
+
static let iso8601: DateFormatter = {
|
|
6
|
+
let formatter = DateFormatter()
|
|
7
|
+
|
|
8
|
+
formatter.calendar = Calendar(identifier: .iso8601)
|
|
9
|
+
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
10
|
+
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
|
11
|
+
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
|
|
12
|
+
|
|
13
|
+
return formatter
|
|
14
|
+
}()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public extension Date {
|
|
18
|
+
/// An ISO8601-compatible timestamp string for this Date object.
|
|
19
|
+
var iso8601: String {
|
|
20
|
+
return Formatter.iso8601.string(from: self)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var iso8601WithUpdatingTimezone: String {
|
|
24
|
+
let formatter = Formatter.iso8601
|
|
25
|
+
formatter.timeZone = TimeZone.autoupdatingCurrent
|
|
26
|
+
return formatter.string(from: self)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public extension String {
|
|
31
|
+
var iso8601: Date? {
|
|
32
|
+
return Formatter.iso8601.date(from: self)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Date+logPrefix.swift
|
|
3
|
+
// VibesPush
|
|
4
|
+
//
|
|
5
|
+
// Created by Moin' Victor on 25/08/2020.
|
|
6
|
+
// Copyright © 2020 Vibes. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
|
|
11
|
+
// MARK: - Date + log prefix
|
|
12
|
+
|
|
13
|
+
extension Date {
|
|
14
|
+
public static var logPrefix: String {
|
|
15
|
+
let dateFormatter = DateFormatter()
|
|
16
|
+
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
|
|
17
|
+
let nowString = dateFormatter.string(from: Date())
|
|
18
|
+
return "[\(nowString)]"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// A small data object to hold a push token received from Apple. This provides
|
|
2
|
+
/// a convenient place to add our JSON encoding code.
|
|
3
|
+
struct DeviceToken {
|
|
4
|
+
let token: String
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
extension DeviceToken: JSONEncodable {
|
|
8
|
+
func encodeJSON() -> VibesJSONDictionary {
|
|
9
|
+
return [
|
|
10
|
+
"device": [
|
|
11
|
+
"push_token": token,
|
|
12
|
+
] as AnyObject,
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// A protocol that indicates the required interface for being considered a
|
|
4
|
+
/// `Device` in the Vibes system.
|
|
5
|
+
protocol DeviceType: JSONEncodable {
|
|
6
|
+
/// The operating system of this device, e.g. iOS
|
|
7
|
+
var systemName: String { get }
|
|
8
|
+
|
|
9
|
+
/// The operating system version of this device, e.g. "10.3.1"
|
|
10
|
+
var systemVersion: String { get }
|
|
11
|
+
|
|
12
|
+
/// The hardware make of this device, e.g. "Apple"
|
|
13
|
+
var hardwareMake: String { get }
|
|
14
|
+
|
|
15
|
+
/// The hardware model of this device, e.g. "iPhone9,1"
|
|
16
|
+
var hardwareModel: String { get }
|
|
17
|
+
|
|
18
|
+
/// The locale for this device, e.g. "en_US"
|
|
19
|
+
var localeIdentifier: String { get }
|
|
20
|
+
|
|
21
|
+
/// The timezone for this device, e.g. "America/Chicago"
|
|
22
|
+
var timeZoneIdentifier: String { get }
|
|
23
|
+
|
|
24
|
+
/// The version of the Vibes SDK installed on this device, e.g. "1.0.1"
|
|
25
|
+
var sdkVersion: String { get }
|
|
26
|
+
|
|
27
|
+
/// The version of the app installed on this device, e.g. "2.0.0"
|
|
28
|
+
var appVersion: String { get }
|
|
29
|
+
|
|
30
|
+
/// A unique advertising identifier for this device, e.g.
|
|
31
|
+
/// "E621E1F8-C36C-495A-93FC-0C247A3E6E5F"
|
|
32
|
+
var advertisingIdentifier: String? { get }
|
|
33
|
+
|
|
34
|
+
/// User location if provided
|
|
35
|
+
var location: (lat: Double, long: Double)? { get }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// A simple data object to allow us to send serialized Device information to
|
|
39
|
+
/// Vibes.
|
|
40
|
+
struct Device {
|
|
41
|
+
|
|
42
|
+
/// A unique advertising identifier for this device, e.g.
|
|
43
|
+
/// "E621E1F8-C36C-495A-93FC-0C247A3E6E5F"
|
|
44
|
+
let advertisingIdentifier: String?
|
|
45
|
+
|
|
46
|
+
/// User location :-(
|
|
47
|
+
var location: (lat: Double, long: Double)?
|
|
48
|
+
|
|
49
|
+
/// Initialize this object.
|
|
50
|
+
///
|
|
51
|
+
/// - parameters:
|
|
52
|
+
/// - advertisingIdentifier: AdSupport.AdvertisingIdentifier, A unique advertising
|
|
53
|
+
/// identifier for this device
|
|
54
|
+
init(advertisingIdentifier: String?, location: (lat: Double, long: Double)? = nil) {
|
|
55
|
+
// check if Ad tracking has been limited by the app's user.
|
|
56
|
+
// https://developer.apple.com/documentation/adsupport/asidentifiermanager
|
|
57
|
+
// Sad day: elvis operator '?:' doesn't work in swift.
|
|
58
|
+
if advertisingIdentifier == "00000000-0000-0000-0000-000000000000" || advertisingIdentifier == "" {
|
|
59
|
+
self.advertisingIdentifier = nil
|
|
60
|
+
} else {
|
|
61
|
+
self.advertisingIdentifier = advertisingIdentifier
|
|
62
|
+
}
|
|
63
|
+
if let location = location {
|
|
64
|
+
self.location = location
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
extension Device: DeviceType {
|
|
70
|
+
|
|
71
|
+
var systemName: String {
|
|
72
|
+
return "iOS"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
var systemVersion: String {
|
|
76
|
+
return UIDevice.current.systemVersion
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
var identifierForVendor: UUID? {
|
|
80
|
+
return UIDevice.current.identifierForVendor
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
var localeIdentifier: String {
|
|
84
|
+
return Locale.current.identifier
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
var timeZoneIdentifier: String {
|
|
88
|
+
return NSTimeZone.local.identifier
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
var hardwareMake: String {
|
|
92
|
+
return "Apple"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
var hardwareModel: String {
|
|
96
|
+
var systemInfo = utsname()
|
|
97
|
+
uname(&systemInfo)
|
|
98
|
+
|
|
99
|
+
let modelCode = withUnsafePointer(to: &systemInfo.machine) {
|
|
100
|
+
$0.withMemoryRebound(to: CChar.self, capacity: 1) { ptr in
|
|
101
|
+
String.init(validatingUTF8: ptr)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return modelCode ?? "Unknown"
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
var sdkVersion: String {
|
|
109
|
+
return Vibes.SDK_VERSION
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
var appVersion: String {
|
|
113
|
+
return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
extension DeviceType {
|
|
118
|
+
func encodeJSON() -> VibesJSONDictionary {
|
|
119
|
+
var locationDictionary: VibesJSONDictionary?
|
|
120
|
+
if let loc = location {
|
|
121
|
+
locationDictionary = [
|
|
122
|
+
"latitude": loc.lat as AnyObject,
|
|
123
|
+
"longitude": loc.long as AnyObject,
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
let dictionary: VibesJSONDictionary = [
|
|
127
|
+
"device": [
|
|
128
|
+
"os": systemName,
|
|
129
|
+
"os_version": systemVersion,
|
|
130
|
+
"sdk_version": sdkVersion,
|
|
131
|
+
"app_version": appVersion,
|
|
132
|
+
"hardware_make": hardwareMake,
|
|
133
|
+
"hardware_model": hardwareModel,
|
|
134
|
+
"advertising_id": advertisingIdentifier as Any,
|
|
135
|
+
"locale": localeIdentifier,
|
|
136
|
+
"timezone": timeZoneIdentifier,
|
|
137
|
+
"location": locationDictionary as AnyObject,
|
|
138
|
+
] as AnyObject,
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
return dictionary
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
extension Dictionary {
|
|
2
|
+
/// Merges a dictionary into this one.
|
|
3
|
+
///
|
|
4
|
+
/// - parameters:
|
|
5
|
+
/// - dictionary: the Dictionary to merge in
|
|
6
|
+
func merged(with dictionary: [Key: Value]) -> [Key: Value] {
|
|
7
|
+
var copy = self
|
|
8
|
+
dictionary.forEach { copy.updateValue($1, forKey: $0) }
|
|
9
|
+
return copy
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// A data object to represent an Event to send to Vibes.
|
|
4
|
+
struct Event {
|
|
5
|
+
/// The type of the event
|
|
6
|
+
fileprivate let eventType: TrackedEventType
|
|
7
|
+
|
|
8
|
+
/// Event unique id (string representation)
|
|
9
|
+
let uuid: String
|
|
10
|
+
|
|
11
|
+
/// The time the Event occurred
|
|
12
|
+
let timestamp: Date
|
|
13
|
+
|
|
14
|
+
/// A set of arbitrary key/values to send along with the event
|
|
15
|
+
let properties: [AnyHashable: Any]
|
|
16
|
+
|
|
17
|
+
/// A convenience property for retrieving the type of the event as a String.
|
|
18
|
+
var type: String {
|
|
19
|
+
return eventType.name()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Checks if this event of of type `launch`
|
|
23
|
+
var isLaunchEvent: Bool {
|
|
24
|
+
return self.type == TrackedEventType.launch.name()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Initialize this object.
|
|
28
|
+
///
|
|
29
|
+
/// - parameters:
|
|
30
|
+
/// - eventType: the type of the event
|
|
31
|
+
/// - properties: a set of arbitrary key/values to send along with the event
|
|
32
|
+
/// - timestamp: the time the Event occurred
|
|
33
|
+
init(eventType: TrackedEventType, properties: [AnyHashable: Any], timestamp: Date = Date()) {
|
|
34
|
+
uuid = UUID().uuidString
|
|
35
|
+
self.eventType = eventType
|
|
36
|
+
self.properties = properties
|
|
37
|
+
self.timestamp = timestamp
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
extension Event: Equatable {
|
|
42
|
+
static func == (lhs: Event, rhs: Event) -> Bool {
|
|
43
|
+
return lhs.uuid == rhs.uuid &&
|
|
44
|
+
lhs.type == rhs.type &&
|
|
45
|
+
lhs.timestamp == rhs.timestamp
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
extension Event: JSONEncodable {
|
|
50
|
+
func encodeJSON() -> VibesJSONDictionary {
|
|
51
|
+
return [
|
|
52
|
+
"uuid": uuid,
|
|
53
|
+
"type": type,
|
|
54
|
+
"timestamp": timestamp.iso8601,
|
|
55
|
+
"attributes": properties,
|
|
56
|
+
] as VibesJSONDictionary
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
extension Event: LocalObjectType {
|
|
61
|
+
var attributes: [String: Any] {
|
|
62
|
+
return [
|
|
63
|
+
"uuid": uuid,
|
|
64
|
+
"type": type,
|
|
65
|
+
"timestamp": timestamp,
|
|
66
|
+
"properties": properties,
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
init?(attributes: [String: Any]) {
|
|
71
|
+
guard let uuid = attributes["uuid"] as? String,
|
|
72
|
+
let timestamp = attributes["timestamp"] as? Date,
|
|
73
|
+
let properties = attributes["properties"] as? [AnyHashable: Any],
|
|
74
|
+
let type = attributes["type"] as? String else {
|
|
75
|
+
return nil
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
self.uuid = uuid
|
|
79
|
+
self.properties = properties
|
|
80
|
+
self.timestamp = timestamp
|
|
81
|
+
eventType = TrackedEventType.convert(rawValue: type)
|
|
82
|
+
}
|
|
83
|
+
}
|