-
Notifications
You must be signed in to change notification settings - Fork 96
/
Copy pathConfiguration.swift
292 lines (260 loc) · 10.7 KB
/
Configuration.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
//
// Configuration.swift
// analytics-swift
//
// Created by Brandon Sneed on 11/17/20.
//
import Foundation
import JSONSafeEncoding
#if os(Linux) || os(Windows)
import FoundationNetworking
#endif
// MARK: - Custom AnonymousId generator
/// Conform to this protocol to generate your own AnonymousID
public protocol AnonymousIdGenerator: AnyObject, Codable {
/// Returns a new anonymousId. Segment still manages storage and retrieval of the
/// current anonymousId and will call this method when new id's are needed.
///
/// - Returns: A new anonymousId.
func newAnonymousId() -> String
}
// MARK: - Operating Mode
/// Specifies the operating mode/context
public enum OperatingMode {
/// The operation of the Analytics client are synchronous.
case synchronous
/// The operation of the Analytics client are asynchronous.
case asynchronous
static internal let defaultQueue = DispatchQueue(label: "com.segment.operatingModeQueue", qos: .utility)
}
// MARK: - Storage Mode
/// Specifies the storage mode to be used for events
public enum StorageMode {
/// Store events to disk (default).
case disk
/// Store events to disk in the given a directory URL.
case diskAtURL(URL)
/// Store events to memory and specify a max count before they roll off.
case memory(Int)
/// Some custom, user-defined storage mechanism conforming to `DataStore`.
case custom(any DataStore)
}
// MARK: - Internal Configuration
public class Configuration {
internal struct Values {
var writeKey: String
var application: Any? = nil
var trackApplicationLifecycleEvents: Bool = true
var flushAt: Int = 20
var flushInterval: TimeInterval = 30
var defaultSettings: Settings? = nil
var autoAddSegmentDestination: Bool = true
var apiHost: String = HTTPClient.getDefaultAPIHost()
var cdnHost: String = HTTPClient.getDefaultCDNHost()
var requestFactory: ((URLRequest) -> URLRequest)? = nil
var errorHandler: ((Error) -> Void)? = nil
var flushPolicies: [FlushPolicy] = [CountBasedFlushPolicy(), IntervalBasedFlushPolicy()]
var operatingMode: OperatingMode = .asynchronous
var flushQueue: DispatchQueue = OperatingMode.defaultQueue
var userAgent: String? = nil
var jsonNonConformingNumberStrategy: JSONSafeEncoder.NonConformingFloatEncodingStrategy = .zero
var storageMode: StorageMode = .disk
var anonymousIdGenerator: AnonymousIdGenerator = SegmentAnonymousId()
var httpSession: (() -> any HTTPSession) = HTTPSessions.urlSession
}
internal var values: Values
/// Initialize a configuration object to pass along to an Analytics instance.
///
/// - Parameter writeKey: Your Segment write key value
public init(writeKey: String) {
self.values = Values(writeKey: writeKey)
JSON.jsonNonConformingNumberStrategy = self.values.jsonNonConformingNumberStrategy
// enable segment destination by default
var settings = Settings(writeKey: writeKey)
settings.integrations = try? JSON([
"Segment.io": true
])
self.defaultSettings(settings)
}
}
// MARK: - Analytics Configuration
public extension Configuration {
/// Sets a reference to your application. This can be useful in instances
/// where referring back to your application is necessary, such as within plugins
/// or async code. The default value is `nil`.
///
/// - Parameter value: A reference to your application.
/// - Returns: The current Configuration.
@discardableResult
func application(_ value: Any?) -> Configuration {
values.application = value
return self
}
/// Opt-in/out of tracking lifecycle events. The default value is `false`.
///
/// - Parameter enabled: A bool value
/// - Returns: The current Configuration.
@discardableResult
func trackApplicationLifecycleEvents(_ enabled: Bool) -> Configuration {
values.trackApplicationLifecycleEvents = enabled
return self
}
/// Set the number of events necessary to automatically flush. The default
/// value is `20`.
///
/// - Parameter count: Event count to trigger a flush.
/// - Returns: The current Configuration.
@discardableResult
func flushAt(_ count: Int) -> Configuration {
values.flushAt = count
return self
}
/// Set a time interval (in seconds) by which to trigger an automatic flush.
/// The default value is `30`.
///
/// - Parameter interval: A time interval
/// - Returns: The current Configuration.
@discardableResult
func flushInterval(_ interval: TimeInterval) -> Configuration {
values.flushInterval = interval
return self
}
/// Sets a default set of Settings. Normally these will come from Segment's
/// api.segment.com/v1/projects/<writekey>/settings, however in instances such
/// as first app launch, it can be useful to have a pre-set batch of settings to
/// ensure that the proper destinations and other settings are enabled prior
/// to receiving them from the Settings endpoint. The default is `nil`.
///
/// You can retrieve a copy of your settings from the following URL:
///
/// https://cdn-settings.segment.com/v1/projects/<writekey>/settings
///
/// Example:
/// ```
/// let defaults = Settings.load(resource: "mySegmentSettings.json")
/// let config = Configuration(writeKey: "1234").defaultSettings(defaults)
/// ```
///
/// - Parameter settings:
/// - Returns: The current Configuration.
@discardableResult
func defaultSettings(_ settings: Settings?) -> Configuration {
values.defaultSettings = settings
return self
}
/// Enable/Disable the automatic adding of Segment as a destination.
/// This can be useful in instances such as Consent Management, or in device
/// mode only setups. The default value is `true`.
///
/// - Parameter value: true/false
/// - Returns: The current Configuration.
@discardableResult
func autoAddSegmentDestination(_ value: Bool) -> Configuration {
values.autoAddSegmentDestination = value
return self
}
/// Sets an alternative API host. This is useful when a proxy is in use, or
/// events need to be routed to certain locales at all times (such as the EU).
/// The default value is `api.segment.io/v1`.
///
/// - Parameter value: A string representing the desired API host.
/// - Returns: The current Configuration.
@discardableResult
func apiHost(_ value: String) -> Configuration {
values.apiHost = value
return self
}
/// Sets an alternative CDN host for settings retrieval. This is useful when
/// a proxy is in use, or settings need to be queried from certain locales at
/// all times (such as the EU). The default value is `cdn-settings.segment.com/v1`.
///
/// - Parameter value: A string representing the desired CDN host.
/// - Returns: The current Configuration.
@discardableResult
func cdnHost(_ value: String) -> Configuration {
values.cdnHost = value
return self
}
/// Sets a block to be used when generating outgoing HTTP requests. Useful in
/// proxying, or adding additional header information for outbound traffic.
///
/// - Parameter value: A block to call when requests are made.
/// - Returns: The current Configuration.
@discardableResult
func requestFactory(_ value: @escaping (URLRequest) -> URLRequest) -> Configuration {
values.requestFactory = value
return self
}
/// Sets an error handler to be called when errors are encountered by the Segment
/// library. See `AnalyticsError` for a list of possible errors that can be
/// encountered.
///
/// - Parameter value: A block to be called when an error occurs.
/// - Returns: The current Configuration.
@discardableResult
func errorHandler(_ value: @escaping (Error) -> Void) -> Configuration {
values.errorHandler = value
return self
}
@discardableResult
func flushPolicies(_ policies: [FlushPolicy]) -> Configuration {
values.flushPolicies = policies
return self
}
/// Informs the Analytics instance of its operating mode/context.
/// Use `.server` when operating in a web service, or when synchronous operation
/// is desired. Use `.client` when operating in a long lived process,
/// desktop/mobile application.
@discardableResult
func operatingMode(_ mode: OperatingMode) -> Configuration {
values.operatingMode = mode
return self
}
/// Specify a custom queue to use when performing a flush operation. The default
/// value is a Segment owned background queue.
@discardableResult
func flushQueue(_ queue: DispatchQueue) -> Configuration {
values.flushQueue = queue
return self
}
/// Specify a custom UserAgent string. This bypasses the OS dependent check entirely.
@discardableResult
func userAgent(_ userAgent: String) -> Configuration {
values.userAgent = userAgent
return self
}
/// This option specifies how NaN/Infinity are handled when encoding JSON.
/// The default is .zero. See JSONSafeEncoder.NonConformingFloatEncodingStrategy for more informatino.
@discardableResult
func jsonNonConformingNumberStrategy(_ strategy: JSONSafeEncoder.NonConformingFloatEncodingStrategy) -> Configuration {
values.jsonNonConformingNumberStrategy = strategy
JSON.jsonNonConformingNumberStrategy = values.jsonNonConformingNumberStrategy
return self
}
/// Specify the storage mode to use. The default is `.disk`.
@discardableResult
func storageMode(_ mode: StorageMode) -> Configuration {
values.storageMode = mode
return self
}
/// Specify a custom anonymousId generator. The default is and instance of `SegmentAnonymousId`.
@discardableResult
func anonymousIdGenerator(_ generator: AnonymousIdGenerator) -> Configuration {
values.anonymousIdGenerator = generator
return self
}
/// Use a custom HTTP session; Useful for non-apple platforms where Swift networking isn't as mature
/// or has issues to work around.
/// - Parameter httpSession: A class conforming to the HTTPSession protocol
/// - Returns: The current configuration
@discardableResult
func httpSession(_ httpSession: @escaping @autoclosure () -> any HTTPSession) -> Configuration {
values.httpSession = httpSession
return self
}
}
extension Analytics {
func configuration<T>(valueFor: () -> T) -> T {
return valueFor()
}
}