Skip to content

Commit 165db3d

Browse files
committed
Updating event registration to occur on start/stop of the scheduler. Updating scheduler unit tests
1 parent ec6ee57 commit 165db3d

7 files changed

Lines changed: 145 additions & 94 deletions

File tree

Examples/GroceryExpress/SettingsViewController.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ class SettingsViewController: UIViewController {
5757
}
5858
@IBAction func loginTapped(_ sender: Any) {
5959
attemptLogin()
60+
ConnectButtonController.activate(
61+
connections: [DisplayInformation.locationConnection.connectionId],
62+
lifecycleSynchronizationOptions: .all
63+
)
6064
}
6165
@IBAction func logoutTapped(_ sender: Any) {
6266
ConnectionCredentials(settings: settings).logout()

IFTTT SDK.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
B556033C25A851EE00A29A01 /* Localizable_cs.strings in Resources */ = {isa = PBXBuildFile; fileRef = B556032425A851EE00A29A01 /* Localizable_cs.strings */; };
5555
B556033D25A851EE00A29A01 /* Localizable_nl.strings in Resources */ = {isa = PBXBuildFile; fileRef = B556032525A851EE00A29A01 /* Localizable_nl.strings */; };
5656
DE25265623D8C49D0019C9CB /* Analytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE25265523D8C49D0019C9CB /* Analytics.swift */; };
57+
DE260F6B26CAFC20004191D1 /* SynchronizationSchedulerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE260F6A26CAFC20004191D1 /* SynchronizationSchedulerTests.swift */; };
5758
DE2906D3242BF66E00CC2825 /* Connection+Parsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2906D2242BF66E00CC2825 /* Connection+Parsing.swift */; };
5859
DE2F524A2429404200EF986A /* Connection+Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2F52492429404200EF986A /* Connection+Location.swift */; };
5960
DE2F524C242940AD00EF986A /* CLCircularRegion+Parsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2F524B242940AD00EF986A /* CLCircularRegion+Parsing.swift */; };
@@ -226,6 +227,7 @@
226227
B556032525A851EE00A29A01 /* Localizable_nl.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = Localizable_nl.strings; path = Resources/Localizable_nl.strings; sourceTree = "<group>"; };
227228
DE1712BC2565B295000B13E6 /* RegionEventsRegistryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionEventsRegistryTests.swift; sourceTree = "<group>"; };
228229
DE25265523D8C49D0019C9CB /* Analytics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Analytics.swift; sourceTree = "<group>"; };
230+
DE260F6A26CAFC20004191D1 /* SynchronizationSchedulerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizationSchedulerTests.swift; sourceTree = "<group>"; };
229231
DE2906D2242BF66E00CC2825 /* Connection+Parsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+Parsing.swift"; sourceTree = "<group>"; };
230232
DE2F52492429404200EF986A /* Connection+Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Location.swift"; sourceTree = "<group>"; };
231233
DE2F524B242940AD00EF986A /* CLCircularRegion+Parsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CLCircularRegion+Parsing.swift"; sourceTree = "<group>"; };
@@ -439,6 +441,7 @@
439441
DEC29DE525840DE300BF56EE /* LocationServiceTests.swift */,
440442
DEC29EE7258419FC00BF56EE /* Info.plist */,
441443
DEF4A4962587BA1A00735E98 /* ArrayHelpersTests.swift */,
444+
DE260F6A26CAFC20004191D1 /* SynchronizationSchedulerTests.swift */,
442445
);
443446
path = SDKHostAppTests;
444447
sourceTree = "<group>";
@@ -864,6 +867,7 @@
864867
DEC29F0125841A0800BF56EE /* String_EmailDataDetectorTests.swift in Sources */,
865868
DEC29F0525841A0800BF56EE /* RegionsMonitorTests.swift in Sources */,
866869
DEC29F0625841A0800BF56EE /* Connection_ParsingTests.swift in Sources */,
870+
DE260F6B26CAFC20004191D1 /* SynchronizationSchedulerTests.swift in Sources */,
867871
DEF4A4972587BA1A00735E98 /* ArrayHelpersTests.swift in Sources */,
868872
DEC29F0225841A0800BF56EE /* EventPublisherTests.swift in Sources */,
869873
);

IFTTT SDK/ConnectButtonController+Public.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,25 +70,24 @@ extension ConnectButtonController {
7070
}
7171
}
7272

73-
/// Performs setup of the SDK. Starts the synchronization of in the SDK. Registers background process with the system if desired.
73+
/// Performs setup of the SDK.Activated synchronization
7474
///
7575
/// - Parameters:
7676
/// - credentials: An optional object conforming to `ConnectionCredentialProvider` which is used to setup the SDK. If this is nil, the SDK will attempt to use cached values.
77-
/// - lifecycleSynchronizationOptions: An instance of `ApplicationLifecycleSynchronizationOptions` that defines which app lifecycle events the synchronization should occur on. If this parameter is not set, a default value of `ApplicationLifecycleSynchronizationOptions.all` will be used.
78-
public static func setup(with credentials: ConnectionCredentialProvider?,
79-
lifecycleSynchronizationOptions: ApplicationLifecycleSynchronizationOptions = .all) {
77+
public static func setup(with credentials: ConnectionCredentialProvider?) {
8078
if let credentials = credentials {
8179
Keychain.update(with: credentials)
8280
}
83-
ConnectionsSynchronizer.shared.setup(lifecycleSynchronizationOptions: lifecycleSynchronizationOptions)
8481
}
8582

8683
/// Call this method to activate the synchronization. This starts synchronization for the parameter connections.
8784
///
8885
/// - Parameters:
8986
/// - connections: An optional list of `Connection` to activate synchronization with.
90-
public static func activate(connections ids: [String]? = nil) {
91-
ConnectionsSynchronizer.shared.activate(connections: ids)
87+
/// - lifecycleSynchronizationOptions: An instance of `ApplicationLifecycleSynchronizationOptions` that defines which app lifecycle events the synchronization should occur on. If this parameter is not set, a default value of `ApplicationLifecycleSynchronizationOptions.all` will be used.
88+
public static func activate(connections ids: [String]? = nil,
89+
lifecycleSynchronizationOptions: ApplicationLifecycleSynchronizationOptions = .all) {
90+
ConnectionsSynchronizer.shared.activate(connections: ids, lifecycleSynchronizationOptions: lifecycleSynchronizationOptions)
9291
}
9392

9493
/// Call this method to deactivate the synchronization of connection and native service data. This stops synchronization and performs cleanup of any stored data. This will also remove any registered geofences.

IFTTT SDK/ConnectionsSynchronizer.swift

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,6 @@ final class ConnectionsSynchronizer {
133133
location.start()
134134
}
135135

136-
/// Performs basic setup of the SDK with Application lifecycle synchronization options.
137-
///
138-
/// - Parameters:
139-
/// - lifecycleSynchronizationOptions: The synchronization options to use in setting up App Lifecycle notification observers.
140-
func setup(lifecycleSynchronizationOptions: ApplicationLifecycleSynchronizationOptions) {
141-
scheduler.setup(lifecycleSynchronizationOptions: lifecycleSynchronizationOptions)
142-
}
143-
144136
/// Can be used to force a synchronization.
145137
///
146138
/// - Parameters:
@@ -151,18 +143,19 @@ final class ConnectionsSynchronizer {
151143
eventPublisher.onNext(event)
152144
}
153145

154-
/// Used to start the synchronization with an optional list of connection ids to monitor.
146+
/// Used to start the synchronization.
155147
///
156148
/// - Parameters:
157149
/// - connections: An optional list of connections to start monitoring.
158-
func activate(connections ids: [String]? = nil) {
150+
/// - lifecycleSynchronizationOptions: The app lifecycle synchronization options to use with the scheduler
151+
func activate(connections ids: [String]? = nil, lifecycleSynchronizationOptions: ApplicationLifecycleSynchronizationOptions) {
159152
if let ids = ids {
160153
registry.addConnections(with: ids, shouldNotify: false)
161154
ConnectButtonController.synchronizationLog("Activated synchronization with connection ids: \(ids)")
162155
} else {
163156
ConnectButtonController.synchronizationLog("Activated synchronization")
164157
}
165-
start()
158+
start(lifecycleSynchronizationOptions: lifecycleSynchronizationOptions)
166159
update(isActivation: true)
167160
}
168161

@@ -174,19 +167,17 @@ final class ConnectionsSynchronizer {
174167
}
175168

176169
/// Call this to start the synchronization. Safe to be called multiple times.
177-
private func start() {
170+
private func start(lifecycleSynchronizationOptions: ApplicationLifecycleSynchronizationOptions) {
178171
if state == .running { return }
179172

180-
setupNotifications()
181173
performPreflightChecks()
182174
Keychain.resetIfNecessary(force: false)
183-
scheduler.start()
175+
scheduler.start(lifecycleSynchronizationOptions: lifecycleSynchronizationOptions)
184176
state = .running
185177
}
186178

187179
/// Call this to stop the synchronization completely. Safe to be called multiple times.
188180
private func stop() {
189-
stopNotifications()
190181
Keychain.resetIfNecessary(force: true)
191182
scheduler.stop()
192183
state = .stopped
@@ -198,22 +189,7 @@ final class ConnectionsSynchronizer {
198189
ConnectButtonController.synchronizationLog("Background location not enabled for this target! Enable background location to allow location updates to be delivered to the app in the background.")
199190
}
200191
}
201-
202-
/// Peforms internal setup to allow the SDK to perform work in response to notification center notifications.
203-
private func setupNotifications() {
204-
if #available(iOS 13.0, *) {
205-
NotificationCenter.default.addObserver(self,
206-
selector: #selector(applicationDidEnterBackground),
207-
name: UIApplication.didEnterBackgroundNotification,
208-
object: nil)
209-
}
210-
}
211-
212-
/// Stops notification observation
213-
private func stopNotifications() {
214-
NotificationCenter.default.removeObserver(self)
215-
}
216-
192+
217193
private func setupRegistryNotifications() {
218194
NotificationCenter.default.addObserver(forName: .UpdateConnectionsName,
219195
object: nil,

IFTTT SDK/SynchronizationManager.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ final class SynchronizationManager {
3636
/// Creates a `SyncManager`
3737
///
3838
/// - Parameter subscribers: The `SyncSubscribers to perform during syncs
39-
init(subscribers: Array<SynchronizationSubscriber>) {
39+
init(subscribers: [SynchronizationSubscriber]) {
4040
self.subscribers = subscribers
4141
}
4242

@@ -126,7 +126,7 @@ extension SynchronizationManager {
126126
finish()
127127
}
128128

129-
private(set) var subscribers: Array<SynchronizationSubscriber> = []
129+
private(set) var subscribers = [SynchronizationSubscriber]()
130130

131131
private var resultsBySubscriber: [String : UIBackgroundFetchResult] = [:]
132132

IFTTT SDK/SynchronizationScheduler.swift

Lines changed: 78 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ final class SynchronizationScheduler {
1818
private let manager: SynchronizationManager
1919

2020
/// The token corresponding to the subscriber.
21-
private var subscriberToken: UUID?
21+
private(set) var subscriberToken: UUID?
2222

2323
/// The tokens that get generated when the SDK sets up application lifecycle NotificationCenter observers
24-
private var applicationLifecycleNotificationCenterTokens: [Any] = []
24+
private(set) var applicationLifecycleNotificationCenterTokens: [Any] = []
2525

2626
/// The tokens that get generated when the SDK sets up other NotificationCenter observers
27-
private var sdkGeneratedNotificationCenterTokens: [Any] = []
27+
private(set) var sdkGeneratedNotificationCenterTokens: [Any] = []
2828

2929
/// Has the app opted into using background process?
30-
private var optedInToUsingSDKBackgroundProcess: Bool = false
30+
private(set) var optedInToUsingSDKBackgroundProcess: Bool = false
3131

3232
/// The triggers to kick off synchronizations
3333
private let triggers: EventPublisher<SynchronizationTriggerEvent>
@@ -49,53 +49,29 @@ final class SynchronizationScheduler {
4949
/// - Parameters:
5050
/// - syncManager: The `SyncManager` to use when scheduling a sync
5151
/// - triggers: The publisher to use in publishing any possible events we might need to.
52-
init(manager: SynchronizationManager,
53-
triggers: EventPublisher<SynchronizationTriggerEvent>) {
52+
init(manager: SynchronizationManager, triggers: EventPublisher<SynchronizationTriggerEvent>) {
5453
self.manager = manager
5554
self.triggers = triggers
56-
setupSubscribers()
57-
}
58-
59-
func setup(lifecycleSynchronizationOptions: ApplicationLifecycleSynchronizationOptions) {
60-
removeLifecycleNotificationObservers()
61-
62-
// Start synchronization on system events
63-
var appLifecycleEventTuples = [(NSNotification.Name, SynchronizationSource, Bool)]()
64-
65-
if lifecycleSynchronizationOptions.contains(.applicationDidBecomeActive) {
66-
appLifecycleEventTuples.append((UIApplication.didBecomeActiveNotification, .appDidBecomeActive, false))
67-
}
68-
if lifecycleSynchronizationOptions.contains(.applicationDidEnterBackground) {
69-
appLifecycleEventTuples.append((UIApplication.didEnterBackgroundNotification, .appBackgrounded, true))
70-
}
71-
72-
self.applicationLifecycleNotificationCenterTokens = appLifecycleEventTuples.map {
73-
return scheduleSynchronization(on: $0.0,
74-
source: $0.1,
75-
shouldRunInBackground: $0.2)
76-
}
55+
startAppSubscribers()
7756
}
7857

7958
/// Performs registration for system and SDK generated events for kicking off synchronizations
8059
/// Should get called when the scheduler is to start.
81-
func start() {
82-
// Remove any previous tokens that might still be aroind
60+
func start(lifecycleSynchronizationOptions: ApplicationLifecycleSynchronizationOptions) {
61+
// Remove any previous tokens that might still be around
8362
removeSDKGeneratedNotificationObservers()
63+
removeLifecycleNotificationObservers()
64+
removeAppSubscribers()
8465

8566
// Start the manager
8667
manager.start()
8768

88-
// Start monitoring the notifications related to Connection CRUD operations
89-
let eventTuples: [(NSNotification.Name, SynchronizationSource, Bool)] = [
90-
(.ConnectionUpdatedNotification, .connectionsUpdate, true),
91-
(.ConnectionAddedNotification, .connectionAddition, true)
92-
]
93-
94-
self.sdkGeneratedNotificationCenterTokens = eventTuples.map {
95-
return scheduleSynchronization(on: $0.0,
96-
source: $0.1,
97-
shouldRunInBackground: $0.2)
98-
}
69+
// Start SDK generated subscribers
70+
startSDKGeneratedSubscribers()
71+
// Start app lifecycle subscribers
72+
startApplicationLifecycleSubscribers(lifecycleSynchronizationOptions: lifecycleSynchronizationOptions)
73+
// Start app generated subscribers()
74+
startAppSubscribers()
9975
}
10076

10177
/// Unregisters from system and SDK generated events for synchronizations.
@@ -104,39 +80,72 @@ final class SynchronizationScheduler {
10480
// Reset the manager
10581
manager.reset()
10682

107-
// Unregister from background process
10883
// Remove observers from notification center
10984
removeLifecycleNotificationObservers()
11085
removeSDKGeneratedNotificationObservers()
111-
112-
applicationLifecycleNotificationCenterTokens = []
113-
sdkGeneratedNotificationCenterTokens = []
86+
removeAppSubscribers()
11487

11588
// Cancel background tasks associated with the SDK
11689
cancelBackgroundProcess()
117-
118-
if let subscriberToken = subscriberToken {
119-
triggers.removeSubscriber(subscriberToken)
120-
}
121-
122-
// Nil out the subscriber token
123-
subscriberToken = nil
12490
}
12591

12692
private func removeLifecycleNotificationObservers() {
12793
applicationLifecycleNotificationCenterTokens.forEach {
12894
NotificationCenter.default.removeObserver($0)
12995
}
96+
applicationLifecycleNotificationCenterTokens = []
13097
}
13198

13299
private func removeSDKGeneratedNotificationObservers() {
133100
sdkGeneratedNotificationCenterTokens.forEach {
134101
NotificationCenter.default.removeObserver($0)
135102
}
103+
sdkGeneratedNotificationCenterTokens = []
104+
}
105+
106+
private func removeAppSubscribers() {
107+
if let subscriberToken = subscriberToken {
108+
triggers.removeSubscriber(subscriberToken)
109+
}
110+
111+
// Nil out the subscriber token
112+
subscriberToken = nil
136113
}
137114

138-
/// Sets up subscriber with app generated triggers.
139-
private func setupSubscribers() {
115+
/// Sets up subscribers with app lifecycle events
116+
private func startApplicationLifecycleSubscribers(lifecycleSynchronizationOptions: ApplicationLifecycleSynchronizationOptions) {
117+
// Start synchronization on system events
118+
var appLifecycleEventTuples = [(NSNotification.Name, SynchronizationSource, Bool)]()
119+
120+
if lifecycleSynchronizationOptions.contains(.applicationDidBecomeActive) {
121+
appLifecycleEventTuples.append((UIApplication.didBecomeActiveNotification, .appDidBecomeActive, false))
122+
}
123+
if lifecycleSynchronizationOptions.contains(.applicationDidEnterBackground) {
124+
appLifecycleEventTuples.append((UIApplication.didEnterBackgroundNotification, .appBackgrounded, true))
125+
}
126+
127+
var tokens = appLifecycleEventTuples.map {
128+
return scheduleSynchronization(on: $0.0,
129+
source: $0.1,
130+
shouldRunInBackground: $0.2)
131+
}
132+
133+
if #available(iOS 13.0, *) {
134+
let token = NotificationCenter.default.addObserver(
135+
forName: UIApplication.didEnterBackgroundNotification,
136+
object: nil,
137+
queue: nil)
138+
{ [weak self] _ in
139+
self?.applicationDidEnterBackground()
140+
}
141+
tokens.append(token)
142+
}
143+
144+
self.applicationLifecycleNotificationCenterTokens = tokens
145+
}
146+
147+
/// Starts subscribers with app generated triggers.
148+
private func startAppSubscribers() {
140149
guard subscriberToken == nil else { return }
141150

142151
self.subscriberToken = triggers.addSubscriber { [weak self] (triggerEvent) in
@@ -150,6 +159,21 @@ final class SynchronizationScheduler {
150159
}
151160
}
152161

162+
/// Starts subscribers with SDK generated triggers.
163+
private func startSDKGeneratedSubscribers() {
164+
// Start monitoring the notifications related to Connection CRUD operations
165+
let eventTuples: [(NSNotification.Name, SynchronizationSource, Bool)] = [
166+
(.ConnectionUpdatedNotification, .connectionsUpdate, true),
167+
(.ConnectionAddedNotification, .connectionAddition, true)
168+
]
169+
170+
self.sdkGeneratedNotificationCenterTokens = eventTuples.map {
171+
return scheduleSynchronization(on: $0.0,
172+
source: $0.1,
173+
shouldRunInBackground: $0.2)
174+
}
175+
}
176+
153177
/// Helper method to schedule a synchronization with a `NSNotification`.
154178
///
155179
/// - Parameters:
@@ -215,7 +239,7 @@ final class SynchronizationScheduler {
215239
}
216240

217241
/// Hook that should get called when the app enters the background. Schedules background process with the system.
218-
func applicationDidEnterBackground() {
242+
@objc func applicationDidEnterBackground() {
219243
if #available(iOS 13.0, *) {
220244
guard Bundle.main.backgroundProcessingEnabled else { return }
221245
guard Bundle.main.containsIFTTTBackgroundProcessingIdentifier else { return }

0 commit comments

Comments
 (0)