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,36 @@
|
|
|
1
|
+
/// A simple object to encapsulate the notion of an array of Events.
|
|
2
|
+
final class EventCollection {
|
|
3
|
+
/// The events in the collection
|
|
4
|
+
var events: [Event] = []
|
|
5
|
+
|
|
6
|
+
/// Initialize this object.
|
|
7
|
+
///
|
|
8
|
+
/// - parameters:
|
|
9
|
+
/// - events: the initial events to contain
|
|
10
|
+
init(events: [Event] = []) {
|
|
11
|
+
self.events = events
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Ideally, instead of `EventCollection` we would use conditional conformance
|
|
16
|
+
// to do:
|
|
17
|
+
//
|
|
18
|
+
// extension Array: LocalObjectType where Element: LocalObjectType {
|
|
19
|
+
// ...
|
|
20
|
+
// }
|
|
21
|
+
//
|
|
22
|
+
// but this sort of functionality isn't available yet in Swift.
|
|
23
|
+
// See: https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md
|
|
24
|
+
|
|
25
|
+
extension EventCollection: LocalObjectType {
|
|
26
|
+
var attributes: [String: Any] {
|
|
27
|
+
return ["object": events.map { $0.attributes }]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
convenience init?(attributes: [String: Any]) {
|
|
31
|
+
self.init()
|
|
32
|
+
|
|
33
|
+
guard let eventAttributes = attributes["object"] as? [[String: Any]] else { return nil }
|
|
34
|
+
self.events = eventAttributes.compactMap { Event(attributes: $0) }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/// A protocol for Types that wish to track events.
|
|
2
|
+
protocol EventTracker: class {
|
|
3
|
+
/// Tracks events.
|
|
4
|
+
///
|
|
5
|
+
/// - parameters:
|
|
6
|
+
/// - events: the Events to track, e.g. [.launch]
|
|
7
|
+
/// - completion: a handler for receiving the result of tracking the events
|
|
8
|
+
/// (it returns an Array of the events that have been tracked)
|
|
9
|
+
func track(events incomingEvents: [Event], completion: VibesCompletion<[Event]>?)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
extension EventTracker {
|
|
13
|
+
/// A convenience method for tracking a single event.
|
|
14
|
+
///
|
|
15
|
+
/// - parameters:
|
|
16
|
+
/// - event: the Event to track, e.g. .launch
|
|
17
|
+
/// - completion: a handler for receiving the result of tracking the events
|
|
18
|
+
/// (it returns an Array of the events that have been tracked)
|
|
19
|
+
func track(event incomingEvent: Event, completion: VibesCompletion<[Event]>?) {
|
|
20
|
+
track(events: [incomingEvent], completion: completion)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// A convenience method for tracking events without caring about completion.
|
|
24
|
+
///
|
|
25
|
+
/// - parameters:
|
|
26
|
+
/// - events: the Events to track, e.g. [.launch]
|
|
27
|
+
func track(events incomingEvents: [Event]) {
|
|
28
|
+
track(events: incomingEvents, completion: nil)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// A convenience method for tracking a single event without caring about
|
|
32
|
+
/// completion.
|
|
33
|
+
///
|
|
34
|
+
/// - parameters:
|
|
35
|
+
/// - event: the Event to track, e.g. .launch
|
|
36
|
+
func track(event incomingEvent: Event) {
|
|
37
|
+
track(events: [incomingEvent], completion: nil)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FoundationExtensions.swift
|
|
3
|
+
// VibesPush
|
|
4
|
+
//
|
|
5
|
+
// Created by Moin' Victor on 10/08/2021.
|
|
6
|
+
// Copyright © 2021 Vibes. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
|
|
11
|
+
extension String {
|
|
12
|
+
func toJSON() -> Any? {
|
|
13
|
+
guard let data = self.data(using: .utf8, allowLossyConversion: false) else { return nil }
|
|
14
|
+
return try? JSONSerialization.jsonObject(with: data, options: .mutableContainers)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// GitTag.swift
|
|
3
|
+
// VibesPush
|
|
4
|
+
//
|
|
5
|
+
// Created by Moin' Victor on 15/09/2020.
|
|
6
|
+
// Copyright © 2020 Vibes. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
|
|
11
|
+
/// A small data object to hold a Git Tag
|
|
12
|
+
public struct GitTag {
|
|
13
|
+
let name: String
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
extension GitTag: JSONEncodable, JSONDecodable {
|
|
17
|
+
init?(json: VibesJSONDictionary) {
|
|
18
|
+
guard let name = json["name"] as? String else { return nil }
|
|
19
|
+
self.name = name
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func encodeJSON() -> VibesJSONDictionary {
|
|
23
|
+
return [
|
|
24
|
+
"name": name as AnyObject,
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
/// A protocol to allow a network client to add behavior around requesting an
|
|
3
|
+
/// HTTP resource.
|
|
4
|
+
///
|
|
5
|
+
/// This protocol may house more methods in the future, to allow for further
|
|
6
|
+
/// customization of request-handling in a network client (e.g. for before/after
|
|
7
|
+
/// callbacks to support an observer pattern).
|
|
8
|
+
protocol HTTPBehavior {
|
|
9
|
+
/// Allows for the modification of a request object prior to it being sent.
|
|
10
|
+
///
|
|
11
|
+
/// - parameters:
|
|
12
|
+
/// - request: the request you would like to modify
|
|
13
|
+
func modifyRequest(request: inout URLRequest)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// We extend HTTPBehavior with a default no-op for its protocol methods.
|
|
17
|
+
extension HTTPBehavior {
|
|
18
|
+
func modifyRequest(request: inout URLRequest) {}
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// A simple enum to provide type safety around HTTP methods.
|
|
2
|
+
enum HTTPMethod: String {
|
|
3
|
+
/// A HEAD method for an HTTP Request.
|
|
4
|
+
case HEAD
|
|
5
|
+
|
|
6
|
+
/// A GET method for an HTTP Request.
|
|
7
|
+
case GET
|
|
8
|
+
|
|
9
|
+
/// A POST method for an HTTP Request.
|
|
10
|
+
case POST
|
|
11
|
+
|
|
12
|
+
/// A PUT method for an HTTP Request.
|
|
13
|
+
case PUT
|
|
14
|
+
|
|
15
|
+
/// A DELETE method for an HTTP Request.
|
|
16
|
+
case DELETE
|
|
17
|
+
|
|
18
|
+
/// A PATCH method for an HTTP Request.
|
|
19
|
+
case PATCH
|
|
20
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// A generic value object representing an HTTP resource that can be requested
|
|
4
|
+
/// and parsed.
|
|
5
|
+
///
|
|
6
|
+
/// T represents the Type that will be returned for this resource, e.g.
|
|
7
|
+
/// `Credential` or `String`.
|
|
8
|
+
struct HTTPResource<T> {
|
|
9
|
+
// The base URL to be used for this resource
|
|
10
|
+
var baseURL: URL?
|
|
11
|
+
/// The path of the resource relative to the base url, e.g. "/devices"
|
|
12
|
+
let path: String
|
|
13
|
+
|
|
14
|
+
/// The HTTP method to use to fetch this resource, e.g. .POST
|
|
15
|
+
let method: HTTPMethod
|
|
16
|
+
|
|
17
|
+
/// The body to send when requesting this resource.
|
|
18
|
+
let requestBody: Data?
|
|
19
|
+
|
|
20
|
+
/// A set of headers to send when requesting this resource.
|
|
21
|
+
let headers: [String: String]
|
|
22
|
+
|
|
23
|
+
/// A closure to use to transform the returned Data for the resource into
|
|
24
|
+
/// the generic type, T.
|
|
25
|
+
let parse: (Data) -> T?
|
|
26
|
+
|
|
27
|
+
/// Initialize this object.
|
|
28
|
+
///
|
|
29
|
+
/// - parameters:
|
|
30
|
+
/// - baseURL: The Base URL, if this is specified, the default base URL will not be used.
|
|
31
|
+
/// - path: The path of the resource relative to the base url, e.g.
|
|
32
|
+
/// "/devices"
|
|
33
|
+
/// - method: The HTTP method to use to fetch this resource, e.g. .POST
|
|
34
|
+
/// - requestBody: The body to send when requesting this resource.
|
|
35
|
+
/// - headers: A set of headers to send when requesting this resource.
|
|
36
|
+
/// - parse: A closure to use to transform the returned Data for the
|
|
37
|
+
/// resource into the generic type, T.
|
|
38
|
+
// swiftlint:disable:next line_length
|
|
39
|
+
init(baseURL: URL? = nil, path: String, method: HTTPMethod = .GET, requestBody: Data? = nil, headers: [String: String] = [:], parse: @escaping (Data) -> T?) {
|
|
40
|
+
self.baseURL = baseURL
|
|
41
|
+
self.path = path
|
|
42
|
+
self.method = method
|
|
43
|
+
self.requestBody = requestBody
|
|
44
|
+
self.headers = headers
|
|
45
|
+
self.parse = parse
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Creates a new URLRequest for this resource. This can be passed to
|
|
49
|
+
/// URLSession to make a network request.
|
|
50
|
+
///
|
|
51
|
+
/// - parameters:
|
|
52
|
+
/// - baseURL: The base URL to use for a request for this resource, e.g.
|
|
53
|
+
/// "http://example.com". The path will be appended to this.
|
|
54
|
+
func toRequest(baseURL: URL) -> URLRequest {
|
|
55
|
+
// use the base URL provided in the resource or use the default base URL
|
|
56
|
+
let url = (self.baseURL ?? baseURL).appendingPathComponent(path)
|
|
57
|
+
var request = URLRequest(url: url)
|
|
58
|
+
|
|
59
|
+
for (key, value) in headers {
|
|
60
|
+
request.setValue(value, forHTTPHeaderField: key)
|
|
61
|
+
}
|
|
62
|
+
request.httpMethod = method.rawValue
|
|
63
|
+
if method != .GET {
|
|
64
|
+
request.httpBody = requestBody
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return request
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
/// A closure used as a completion handler for HTTP requests.
|
|
3
|
+
typealias HTTPCompletion<T> = (ResourceResult<T>) -> Void
|
|
4
|
+
|
|
5
|
+
/// A protocol that indicates the required interface for being considered an
|
|
6
|
+
/// `HTTPResourceClient`
|
|
7
|
+
protocol HTTPResourceClientType {
|
|
8
|
+
func request<A>(resource: HTTPResource<A>,
|
|
9
|
+
behavior: HTTPBehavior?,
|
|
10
|
+
completion: HTTPCompletion<A>?)
|
|
11
|
+
func request<A>(resource: HTTPResource<A>,
|
|
12
|
+
completion: HTTPCompletion<A>?)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/// A closure used as a completion handler for URLSessionDataTasks.
|
|
16
|
+
typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void
|
|
17
|
+
|
|
18
|
+
/// A protocol that indicates the required interface for being considered a
|
|
19
|
+
/// `URLSession` for use with an `HTTPResourceClient`.
|
|
20
|
+
protocol URLSessionType {
|
|
21
|
+
func dataTask(with request: URLRequest,
|
|
22
|
+
completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskType
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
extension URLSession: URLSessionType {
|
|
26
|
+
func dataTask(with request: URLRequest,
|
|
27
|
+
completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskType {
|
|
28
|
+
let task = dataTask(with: request,
|
|
29
|
+
completionHandler: completionHandler) as URLSessionDataTask
|
|
30
|
+
return task as URLSessionDataTaskType
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// A protocol that indicates the required interface for being considered a
|
|
35
|
+
/// `URLSessionDataTask` for use with an `HTTPResourceClient`.
|
|
36
|
+
protocol URLSessionDataTaskType {
|
|
37
|
+
func resume()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
extension URLSessionDataTask: URLSessionDataTaskType {}
|
|
41
|
+
|
|
42
|
+
extension HTTPResourceClientType {
|
|
43
|
+
/// Make an HTTP request for an `HTTPResource`.
|
|
44
|
+
///
|
|
45
|
+
/// - parameters:
|
|
46
|
+
/// - resource: The resource to request
|
|
47
|
+
/// - completion: A handler for when the request fails or succeeds.
|
|
48
|
+
func request<A>(resource: HTTPResource<A>,
|
|
49
|
+
completion: HTTPCompletion<A>?) {
|
|
50
|
+
request(resource: resource, behavior: nil, completion: completion)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// A network client that knows how to take `HTTPResource` objects, request them
|
|
55
|
+
/// via URLSession, and parse them.
|
|
56
|
+
class HTTPResourceClient: HTTPResourceClientType {
|
|
57
|
+
/// The base URL to use for this network client, e.g. "http://example.com"
|
|
58
|
+
private let baseURL: URL
|
|
59
|
+
|
|
60
|
+
/// A session to use, usually URLSession.shared.
|
|
61
|
+
private let session: URLSessionType
|
|
62
|
+
|
|
63
|
+
/// Optional provider for request/response logging. Evaluated per request so
|
|
64
|
+
/// it is safe before `Vibes.configure` has finished.
|
|
65
|
+
private let loggerProvider: () -> VibesLogger?
|
|
66
|
+
|
|
67
|
+
/// Initialize this object.
|
|
68
|
+
///
|
|
69
|
+
/// - parameters:
|
|
70
|
+
/// - baseURL: the base URL to use for this network client, e.g.
|
|
71
|
+
/// "http://example.com"
|
|
72
|
+
/// - session: the `URLSessionType` to use for making HTTP requests
|
|
73
|
+
/// - loggerProvider: returns the logger to use for this request, if any
|
|
74
|
+
init(baseURL: URL,
|
|
75
|
+
session: URLSessionType = URLSession.shared,
|
|
76
|
+
loggerProvider: @escaping () -> VibesLogger? = { nil }) {
|
|
77
|
+
self.baseURL = baseURL
|
|
78
|
+
self.session = session
|
|
79
|
+
self.loggerProvider = loggerProvider
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// Make an HTTP request for an `HTTPResource`.
|
|
83
|
+
///
|
|
84
|
+
/// - parameters:
|
|
85
|
+
/// - resource: The resource to request
|
|
86
|
+
/// - behavior: An optional HTTPBehavior to apply to the request
|
|
87
|
+
/// - completion: A handler for when the request fails or succeeds.
|
|
88
|
+
func request<A>(resource: HTTPResource<A>,
|
|
89
|
+
behavior: HTTPBehavior?,
|
|
90
|
+
completion: HTTPCompletion<A>?) {
|
|
91
|
+
var request = resource.toRequest(baseURL: baseURL)
|
|
92
|
+
behavior?.modifyRequest(request: &request)
|
|
93
|
+
|
|
94
|
+
loggerProvider()?.log(request: request)
|
|
95
|
+
|
|
96
|
+
let task = session.dataTask(with: request) { data, response, error in
|
|
97
|
+
if let error = error {
|
|
98
|
+
self.loggerProvider()?.log(error: error)
|
|
99
|
+
self.handleError(error, completion: completion)
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if let response = response {
|
|
104
|
+
self.loggerProvider()?.log(response: response, data: data)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
guard let response = response as? HTTPURLResponse else {
|
|
108
|
+
completion?(.failure(.other(error)))
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
guard 200 ... 299 ~= response.statusCode else {
|
|
113
|
+
self.handleResponseWithStatusCode(of: response.statusCode,
|
|
114
|
+
data: data,
|
|
115
|
+
completion: completion)
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
guard let data = data else {
|
|
120
|
+
completion?(.failure(.noData))
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if let value = resource.parse(data) {
|
|
125
|
+
completion?(.success(value))
|
|
126
|
+
} else {
|
|
127
|
+
completion?(.failure(.couldNotParse(data: data)))
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
task.resume()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// Handle an error in even being able to make a request.
|
|
135
|
+
///
|
|
136
|
+
/// - parameters:
|
|
137
|
+
/// - error: The error
|
|
138
|
+
/// - completion: A handler for when the request fails or succeeds.
|
|
139
|
+
fileprivate func handleError<A>(_ error: Error?, completion: HTTPCompletion<A>?) {
|
|
140
|
+
if let nserror = error as NSError? {
|
|
141
|
+
switch nserror.code {
|
|
142
|
+
case NSURLErrorTimedOut, NSURLErrorNotConnectedToInternet: completion?(.failure(.unreachable))
|
|
143
|
+
default: completion?(.failure(.other(error)))
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// Handle an HTTP response status (which may indicate an error or a success).
|
|
149
|
+
///
|
|
150
|
+
/// - parameters:
|
|
151
|
+
/// - statusCode: The HTTP response code, e.g. 200 or 502
|
|
152
|
+
/// - data: The data returned with the response
|
|
153
|
+
/// - completion: A handler for when the request fails or succeeds.
|
|
154
|
+
fileprivate func handleResponseWithStatusCode<A>(of statusCode: Int,
|
|
155
|
+
data: Data?,
|
|
156
|
+
completion: HTTPCompletion<A>?) {
|
|
157
|
+
let responseText = String(data: data ?? Data(), encoding: .utf8)
|
|
158
|
+
|
|
159
|
+
switch statusCode {
|
|
160
|
+
case 401: completion?(.failure(.unauthorized))
|
|
161
|
+
default:
|
|
162
|
+
let error = ResourceError.invalidResponse(statusCode: statusCode, responseText: responseText ?? "")
|
|
163
|
+
completion?(.failure(error))
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
internal extension URLRequest {
|
|
169
|
+
|
|
170
|
+
func toString() -> String {
|
|
171
|
+
return """
|
|
172
|
+
\(httpMethod!) \((url?.absoluteString)!),
|
|
173
|
+
\nBody: \(String(describing: httpBody != nil ? String(data: httpBody!,
|
|
174
|
+
encoding: String.Encoding.utf8)! : "")) \nHeaders: \(allHTTPHeaderFields!)
|
|
175
|
+
"""
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
internal extension Int {
|
|
180
|
+
|
|
181
|
+
/// Returns `true` if this Int is a status code value to be retried
|
|
182
|
+
func isStatusCodeForRetryableServerError() -> Bool {
|
|
183
|
+
switch self {
|
|
184
|
+
case 408, 429, 500, 502, 503, 504:
|
|
185
|
+
return true
|
|
186
|
+
default:
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// A Type for a JSON dictionary (object)
|
|
4
|
+
public typealias VibesJSONDictionary = [String: AnyObject]
|
|
5
|
+
/// A Type for Vibes Tracking JSON dictionary (object)
|
|
6
|
+
public typealias VibesTrackingData = [String: Any]
|
|
7
|
+
/// A Type for a JSON array
|
|
8
|
+
typealias VibesJSONArray = [VibesJSONDictionary]
|
|
9
|
+
|
|
10
|
+
/// A protocol that can be adopted to say that a Type can be encoded into JSON.
|
|
11
|
+
protocol JSONEncodable {
|
|
12
|
+
/// Encodes this Type into a JSONDictionary.
|
|
13
|
+
func encodeJSON() -> VibesJSONDictionary
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// A protocol that can be adopted to say that a Type can be decoded from JSON.
|
|
17
|
+
protocol JSONDecodable {
|
|
18
|
+
/// A convenience initializer for decoding a JSONDictionary into this Type.
|
|
19
|
+
///
|
|
20
|
+
/// - parameters:
|
|
21
|
+
/// - json: the JSONDictionary to encode into data
|
|
22
|
+
init?(json: VibesJSONDictionary)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Allows the ability to decode/encode `Data` to/from a `JSONDictionary`.
|
|
26
|
+
struct JSONParser {
|
|
27
|
+
/// Decodes data into a JSONDictionary.
|
|
28
|
+
///
|
|
29
|
+
/// - parameters:
|
|
30
|
+
/// - data: the data to decode
|
|
31
|
+
func decode(data: Data) -> VibesJSONDictionary? {
|
|
32
|
+
// Treat an existing but blank data as an empty JSON object
|
|
33
|
+
guard !data.isEmpty else { return [:] }
|
|
34
|
+
|
|
35
|
+
// swiftlint:disable:next force_cast
|
|
36
|
+
return try? JSONSerialization.jsonObject(with: data) as? VibesJSONDictionary
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Decodes data into a JSONArray.
|
|
40
|
+
///
|
|
41
|
+
/// - parameters:
|
|
42
|
+
/// - data: the data to decode
|
|
43
|
+
func decodeJSONArray(data: Data) -> VibesJSONArray? {
|
|
44
|
+
// Treat an existing but blank data as an empty JSON object
|
|
45
|
+
guard !data.isEmpty else { return [] }
|
|
46
|
+
|
|
47
|
+
guard let arrayJson = ((try? JSONSerialization.jsonObject(with: data) as? VibesJSONArray) as VibesJSONArray??)
|
|
48
|
+
else {
|
|
49
|
+
return nil
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return arrayJson
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Encodes a JSONEncodable to data.
|
|
56
|
+
///
|
|
57
|
+
/// - parameters:
|
|
58
|
+
/// - object: a JSONEncodable object to encode into data
|
|
59
|
+
func encode(_ object: JSONEncodable) -> Data? {
|
|
60
|
+
let dict = object.encodeJSON()
|
|
61
|
+
return encode(dict)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Encodes an array of JSONEncodable objects to data.
|
|
65
|
+
///
|
|
66
|
+
/// - parameters:
|
|
67
|
+
/// - objects: an array of JSONEncodable object to encode into data
|
|
68
|
+
func encode(_ objects: [JSONEncodable]) -> Data? {
|
|
69
|
+
let array = objects.map { $0.encodeJSON() }
|
|
70
|
+
return encode(array)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Encodes a JSONDictionary to data.
|
|
74
|
+
///
|
|
75
|
+
/// - parameters:
|
|
76
|
+
/// - dict: the JSONDictionary to encode into data
|
|
77
|
+
func encode(_ dict: VibesJSONDictionary) -> Data? {
|
|
78
|
+
guard !dict.isEmpty else { return nil }
|
|
79
|
+
guard JSONSerialization.isValidJSONObject(dict) else { return nil }
|
|
80
|
+
return try? JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Encodes a JSONArray to data.
|
|
84
|
+
///
|
|
85
|
+
/// - parameters:
|
|
86
|
+
/// - array: the JSONArray to encode into data
|
|
87
|
+
func encode(_ array: VibesJSONArray) -> Data? {
|
|
88
|
+
guard JSONSerialization.isValidJSONObject(array) else { return nil }
|
|
89
|
+
return try? JSONSerialization.data(withJSONObject: array, options: .prettyPrinted)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//
|
|
2
|
+
// AssociatePersonOperation.swift
|
|
3
|
+
// VibesPush-iOS
|
|
4
|
+
//
|
|
5
|
+
// Created by Peter Compernolle on 5/30/2018.
|
|
6
|
+
// Copyright © 2018 Vibes. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
internal class AssociatePersonOperation: BaseOperation<Void> {
|
|
10
|
+
fileprivate let resource: () -> HTTPResource<Void>
|
|
11
|
+
fileprivate weak var delegate: VibesAPIDelegate?
|
|
12
|
+
fileprivate var callback: VibesCompletion<Void>?
|
|
13
|
+
|
|
14
|
+
/// Completion handler responsible for notifying sdk users
|
|
15
|
+
fileprivate lazy var completionHandler: VibesCompletion<Void> = {[weak self] (result) in
|
|
16
|
+
switch result {
|
|
17
|
+
case .success:
|
|
18
|
+
self?.callback?(.success(()))
|
|
19
|
+
self?.delegate?.didAssociatePerson?(error: nil)
|
|
20
|
+
case .failure(let error):
|
|
21
|
+
self?.callback?(.failure(error))
|
|
22
|
+
self?.delegate?.didAssociatePerson?(error: error)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Initialize this object
|
|
27
|
+
///
|
|
28
|
+
/// - parameters:
|
|
29
|
+
/// - credentialManager: an object to store credentials, keeping track of the current Credential..
|
|
30
|
+
/// - api: an object that allows talking to the Vibes API
|
|
31
|
+
/// - advertisingId: AdSupport.advertisingId
|
|
32
|
+
/// - appId: the application ID provided by Vibes to identify this application
|
|
33
|
+
/// - delegate: VibesAPIDelegate.
|
|
34
|
+
/// - externalPersonId: a String representation of the third party person identifier
|
|
35
|
+
public init(credentials: CredentialManager,
|
|
36
|
+
api: VibesAPIType,
|
|
37
|
+
advertisingId: String,
|
|
38
|
+
appId: String,
|
|
39
|
+
delegate: VibesAPIDelegate?,
|
|
40
|
+
callback: VibesCompletion<Void>? = nil,
|
|
41
|
+
externalPersonId: String) {
|
|
42
|
+
self.delegate = delegate
|
|
43
|
+
self.callback = callback
|
|
44
|
+
resource = {
|
|
45
|
+
let deviceId = credentials.currentCredential?.deviceId ?? VibesQueueConstant.NONE
|
|
46
|
+
return APIDefinition.associatePerson(appId: appId, deviceId: deviceId, externalPersonId: externalPersonId)
|
|
47
|
+
}
|
|
48
|
+
super.init(credentials: credentials, api: api, advertisingId: advertisingId, appId: appId)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Execute the operation
|
|
52
|
+
override func start() {
|
|
53
|
+
super.start()
|
|
54
|
+
super.execute(retry: 0, resource: resource, completion: completionHandler)
|
|
55
|
+
}
|
|
56
|
+
}
|