@@ -146,6 +146,11 @@ extension Analytics {
146
146
return nil
147
147
}
148
148
149
+ /// Returns the current operating mode this instance was given.
150
+ public var operatingMode : OperatingMode {
151
+ return configuration. values. operatingMode
152
+ }
153
+
149
154
/// Adjusts the flush interval post configuration.
150
155
public var flushInterval : TimeInterval {
151
156
get {
@@ -196,16 +201,52 @@ extension Analytics {
196
201
}
197
202
198
203
/// Tells this instance of Analytics to flush any queued events up to Segment.com. This command will also
199
- /// be sent to each plugin present in the system.
200
- public func flush( ) {
204
+ /// be sent to each plugin present in the system. A completion handler can be optionally given and will be
205
+ /// called when flush has completed.
206
+ public func flush( completion: ( ( ) -> Void ) ? = nil ) {
201
207
// only flush if we're enabled.
202
208
guard enabled == true else { return }
203
209
210
+ let flushGroup = DispatchGroup ( )
211
+ // gotta call enter at least once before we ask to be notified.
212
+ flushGroup. enter ( )
213
+
204
214
apply { plugin in
205
- if let p = plugin as? EventPlugin {
206
- p. flush ( )
215
+ operatingMode. run ( queue: configuration. values. flushQueue) {
216
+ if let p = plugin as? FlushCompletion {
217
+ // this is async
218
+ // flush(group:completion:) handles the enter/leave.
219
+ p. flush ( group: flushGroup) { plugin in
220
+ // we don't really care about the plugin value .. yet.
221
+ }
222
+ } else if let p = plugin as? EventPlugin {
223
+ // we have no idea if this will be async or not, assume it's sync.
224
+ flushGroup. enter ( )
225
+ p. flush ( )
226
+ flushGroup. leave ( )
227
+ }
207
228
}
208
229
}
230
+
231
+ // if we're not in server mode, we need to be notified when it's done.
232
+ if let completion, operatingMode != . synchronous {
233
+ // set up our callback to know when the group has completed, if we're not
234
+ // in .server operating mode.
235
+ flushGroup. notify ( queue: configuration. values. flushQueue) {
236
+ DispatchQueue . main. async { completion ( ) }
237
+ }
238
+ }
239
+
240
+ flushGroup. leave ( ) // matches our initial enter().
241
+
242
+ // if we ARE in server mode, we need to wait on the group.
243
+ // This effectively ends up being a `sync` operation.
244
+ if operatingMode == . synchronous {
245
+ flushGroup. wait ( )
246
+ // we need to call completion on our own since
247
+ // we skipped setting up notify.
248
+ if let completion { DispatchQueue . main. async { completion ( ) } }
249
+ }
209
250
}
210
251
211
252
/// Resets this instance of Analytics to a clean slate. Traits, UserID's, anonymousId, etc are all cleared or reset. This
@@ -384,3 +425,28 @@ extension Analytics {
384
425
return configuration. values. writeKey == Self . deadInstance
385
426
}
386
427
}
428
+
429
+ // MARK: Operating mode based scheduling
430
+
431
+ extension OperatingMode {
432
+ func run( queue: DispatchQueue , task: @escaping ( ) -> Void ) {
433
+ //
434
+ switch self {
435
+ case . asynchronous:
436
+ queue. async {
437
+ task ( )
438
+ }
439
+ case . synchronous:
440
+ // if for some reason, we're told to do all this stuff on
441
+ // main, ignore it, and use the default queue. this prevents
442
+ // a possible deadlock.
443
+ if queue === DispatchQueue . main {
444
+ OperatingMode . defaultQueue. asyncAndWait {
445
+ task ( )
446
+ }
447
+ } else {
448
+ queue. asyncAndWait { task ( ) }
449
+ }
450
+ }
451
+ }
452
+ }
0 commit comments