Skip to content

Commit b1493be

Browse files
authored
Merge pull request #293 from IFTTT/feature/location_event_reporting
Location event reporting
2 parents 63b656b + 6dbbc04 commit b1493be

17 files changed

+885
-112
lines changed

.github/workflows/ios.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
jobs:
1010
build:
1111
name: Build and Test SDKHostApp scheme using any available iPhone simulator
12-
runs-on: macos-latest
12+
runs-on: macos-11
1313

1414
steps:
1515
- name: Checkout
@@ -18,21 +18,23 @@ jobs:
1818
env:
1919
scheme: ${{ 'SDKHostApp' }}
2020
platform: ${{ 'iOS Simulator' }}
21+
os: ${{ '15.0' }}
22+
device: ${{ 'iPhone 13' }}
2123
run: |
2224
# xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)
23-
device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}'`
2425
if [ $scheme = default ]; then scheme=$(cat default); fi
2526
if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
2627
file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`
27-
xcodebuild build-for-testing -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device"
28+
xcodebuild build-for-testing -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,OS=$os,name=$device"
2829
- name: Test
2930
env:
3031
scheme: ${{ 'SDKHostApp' }}
3132
platform: ${{ 'iOS Simulator' }}
33+
os: ${{ '15.0' }}
34+
device: ${{ 'iPhone 13' }}
3235
run: |
3336
# xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)
34-
device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}'`
3537
if [ $scheme = default ]; then scheme=$(cat default); fi
3638
if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
3739
file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`
38-
xcodebuild test-without-building -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device"
40+
xcodebuild test-without-building -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,OS=$os,name=$device"

Examples/GroceryExpress/AppDelegate.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
2727
} else {
2828
ConnectButtonController.deactivate()
2929
}
30+
3031
ConnectButtonController.setBackgroundProcessClosures {
3132
print("Background process started!")
3233
} expirationHandler: {
3334
print("Background process expired!")
3435
}
36+
37+
ConnectButtonController.setLocationEventReportedClosure { events in
38+
print(events)
39+
}
3540

3641
return true
3742
}

IFTTT SDK.xcodeproj/project.pbxproj

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@
6060
DE25265623D8C49D0019C9CB /* Analytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE25265523D8C49D0019C9CB /* Analytics.swift */; };
6161
DE260F6B26CAFC20004191D1 /* SynchronizationSchedulerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE260F6A26CAFC20004191D1 /* SynchronizationSchedulerTests.swift */; };
6262
DE2906D3242BF66E00CC2825 /* Connection+Parsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2906D2242BF66E00CC2825 /* Connection+Parsing.swift */; };
63+
DE2AE45C2721DD9E00C4794A /* LocationEventReporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2AE45B2721DD9E00C4794A /* LocationEventReporterTests.swift */; };
64+
DE2AE45E2721EBFE00C4794A /* LocationEventStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2AE45D2721EBFD00C4794A /* LocationEventStoreTests.swift */; };
65+
DE2AE48027270FED00C4794A /* LocationEventReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2AE47F27270FED00C4794A /* LocationEventReporter.swift */; };
66+
DE2AE4822727103F00C4794A /* LocationEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2AE4812727103F00C4794A /* LocationEvent.swift */; };
67+
DE2AE4842727106800C4794A /* LocationEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2AE4832727106800C4794A /* LocationEventStore.swift */; };
68+
DE2AE4862727132E00C4794A /* RegionEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2AE4852727132E00C4794A /* RegionEvent.swift */; };
6369
DE2F524A2429404200EF986A /* Connection+Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2F52492429404200EF986A /* Connection+Location.swift */; };
6470
DE2F524C242940AD00EF986A /* CLCircularRegion+Parsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2F524B242940AD00EF986A /* CLCircularRegion+Parsing.swift */; };
6571
DE3074A723DB506D00A3C71F /* AnalyticsNetworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3074A623DB506D00A3C71F /* AnalyticsNetworkController.swift */; };
@@ -237,6 +243,12 @@
237243
DE25265523D8C49D0019C9CB /* Analytics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Analytics.swift; sourceTree = "<group>"; };
238244
DE260F6A26CAFC20004191D1 /* SynchronizationSchedulerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizationSchedulerTests.swift; sourceTree = "<group>"; };
239245
DE2906D2242BF66E00CC2825 /* Connection+Parsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+Parsing.swift"; sourceTree = "<group>"; };
246+
DE2AE45B2721DD9E00C4794A /* LocationEventReporterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEventReporterTests.swift; sourceTree = "<group>"; };
247+
DE2AE45D2721EBFD00C4794A /* LocationEventStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEventStoreTests.swift; sourceTree = "<group>"; };
248+
DE2AE47F27270FED00C4794A /* LocationEventReporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEventReporter.swift; sourceTree = "<group>"; };
249+
DE2AE4812727103F00C4794A /* LocationEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEvent.swift; sourceTree = "<group>"; };
250+
DE2AE4832727106800C4794A /* LocationEventStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEventStore.swift; sourceTree = "<group>"; };
251+
DE2AE4852727132E00C4794A /* RegionEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionEvent.swift; sourceTree = "<group>"; };
240252
DE2F52492429404200EF986A /* Connection+Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Location.swift"; sourceTree = "<group>"; };
241253
DE2F524B242940AD00EF986A /* CLCircularRegion+Parsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CLCircularRegion+Parsing.swift"; sourceTree = "<group>"; };
242254
DE3074A623DB506D00A3C71F /* AnalyticsNetworkController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsNetworkController.swift; sourceTree = "<group>"; };
@@ -397,6 +409,15 @@
397409
name = "Web Service Authentication";
398410
sourceTree = "<group>";
399411
};
412+
DE2AE47E27270FD800C4794A /* Reporting */ = {
413+
isa = PBXGroup;
414+
children = (
415+
DE2AE47F27270FED00C4794A /* LocationEventReporter.swift */,
416+
DE2AE4832727106800C4794A /* LocationEventStore.swift */,
417+
);
418+
name = Reporting;
419+
sourceTree = "<group>";
420+
};
400421
DE3074A523DB505400A3C71F /* Analytics */ = {
401422
isa = PBXGroup;
402423
children = (
@@ -463,6 +484,8 @@
463484
DEC29EE7258419FC00BF56EE /* Info.plist */,
464485
DEF4A4962587BA1A00735E98 /* ArrayHelpersTests.swift */,
465486
DE260F6A26CAFC20004191D1 /* SynchronizationSchedulerTests.swift */,
487+
DE2AE45B2721DD9E00C4794A /* LocationEventReporterTests.swift */,
488+
DE2AE45D2721EBFD00C4794A /* LocationEventStoreTests.swift */,
466489
);
467490
path = SDKHostAppTests;
468491
sourceTree = "<group>";
@@ -597,6 +620,7 @@
597620
FCF29FC32187B903004B1100 /* Internal */ = {
598621
isa = PBXGroup;
599622
children = (
623+
DE2AE47E27270FD800C4794A /* Reporting */,
600624
DE1F036E26A8A33F00B6CF1A /* Web Service Authentication */,
601625
DEC75CB525092B6100F40296 /* Native Services */,
602626
1DFE020C2190E3DB00856ABC /* API.swift */,
@@ -667,6 +691,8 @@
667691
DE641D87252B7BC900C56FF9 /* ConnectButtonController+Public.swift */,
668692
DEC30C01258BECEB00A77A57 /* JSONNetworkController.swift */,
669693
1D981C32219210D7001784C4 /* Result+Queries.swift */,
694+
DE2AE4812727103F00C4794A /* LocationEvent.swift */,
695+
DE2AE4852727132E00C4794A /* RegionEvent.swift */,
670696
);
671697
name = Public;
672698
sourceTree = "<group>";
@@ -881,11 +907,13 @@
881907
buildActionMask = 2147483647;
882908
files = (
883909
DEC29F0425841A0800BF56EE /* CLRegion+Parsing_spec.swift in Sources */,
910+
DE2AE45C2721DD9E00C4794A /* LocationEventReporterTests.swift in Sources */,
884911
DEC29F0325841A0800BF56EE /* RegionEventsRegistryTests.swift in Sources */,
885912
DEC29F0825841A0800BF56EE /* LocationServiceTests.swift in Sources */,
886913
DEC29F0725841A0800BF56EE /* ConnectionsRegistryTests.swift in Sources */,
887914
DEC29F0125841A0800BF56EE /* String_EmailDataDetectorTests.swift in Sources */,
888915
DEC29F0525841A0800BF56EE /* RegionsMonitorTests.swift in Sources */,
916+
DE2AE45E2721EBFE00C4794A /* LocationEventStoreTests.swift in Sources */,
889917
DEC29F0625841A0800BF56EE /* Connection_ParsingTests.swift in Sources */,
890918
DE260F6B26CAFC20004191D1 /* SynchronizationSchedulerTests.swift in Sources */,
891919
DEF4A4972587BA1A00735E98 /* ArrayHelpersTests.swift in Sources */,
@@ -912,6 +940,7 @@
912940
DEC08FF02429469C007D7039 /* NativeServices.swift in Sources */,
913941
FC22B1A421ACCDF800738023 /* PassthroughView.swift in Sources */,
914942
DE328DBE243E18BB00603EAC /* Keychain.swift in Sources */,
943+
DE2AE4842727106800C4794A /* LocationEventStore.swift in Sources */,
915944
DEC30C02258BECEB00A77A57 /* JSONNetworkController.swift in Sources */,
916945
FC84B85321C43A3000BAF7ED /* LegalTermsText.swift in Sources */,
917946
DEC75CB42508312F00F40296 /* Connection+Storage.swift in Sources */,
@@ -927,6 +956,7 @@
927956
DE328DB4243CBC9700603EAC /* ConnectionsSynchronizer.swift in Sources */,
928957
DE3074A723DB506D00A3C71F /* AnalyticsNetworkController.swift in Sources */,
929958
DE7666E9239990C5005D6DE3 /* WebServiceAuthentication.swift in Sources */,
959+
DE2AE4862727132E00C4794A /* RegionEvent.swift in Sources */,
930960
1D453EDE2297216A001994FF /* ConnectButton+AnimationState.swift in Sources */,
931961
FC22B19621ACA54A00738023 /* ImageDownloader.swift in Sources */,
932962
FCF29FC52187C7E0004B1100 /* JSON.swift in Sources */,
@@ -973,8 +1003,10 @@
9731003
1D981C33219210D7001784C4 /* Result+Queries.swift in Sources */,
9741004
FC84B84C21C4321F00BAF7ED /* Links.swift in Sources */,
9751005
DE4A4C622436409C004082BF /* SynchronizationScheduler.swift in Sources */,
1006+
DE2AE48027270FED00C4794A /* LocationEventReporter.swift in Sources */,
9761007
1DFE02172190E58700856ABC /* URLSession+JSONTask.swift in Sources */,
9771008
FC7F13A221B06E34002AD8FB /* ImageViewNetworkController.swift in Sources */,
1009+
DE2AE4822727103F00C4794A /* LocationEvent.swift in Sources */,
9781010
1D453ED822971C5C001994FF /* ConnectButton+Style.swift in Sources */,
9791011
DEC75CBB2509741F00F40296 /* LocationLibrary.swift in Sources */,
9801012
DE37620022DF75CB00F8BD38 /* ConnectionDeeplinkAction.swift in Sources */,

IFTTT SDK/ConnectButtonController+Public.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,12 @@ extension ConnectButtonController {
164164
ConnectionsSynchronizer.shared.setDeveloperBackgroundProcessClosures(launchHandler: launchHandler,
165165
expirationHandler: expirationHandler)
166166
}
167+
168+
/// Sets a closure to be invoked whenever a `LocationEvent` occurs. For more information on the events, see `LocationEvent`.
169+
///
170+
/// - Parameters:
171+
/// - closure: The closure to invoke whenever a `LocationEvent` occurs.
172+
public static func setLocationEventReportedClosure(_ closure: LocationEventsClosure?) {
173+
ConnectionsSynchronizer.shared.setLocationEventReportedClosure(closure: closure)
174+
}
167175
}

IFTTT SDK/ConnectionsSynchronizer.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ final class ConnectionsSynchronizer {
7676
private let subscribers: [SynchronizationSubscriber]
7777
private var scheduler: SynchronizationScheduler
7878
private let permissionsRequestor: PermissionsRequestor
79+
private let locationEventReporter: LocationEventReporter
7980

8081
private var state: RunState = .unknown
8182

@@ -99,12 +100,16 @@ final class ConnectionsSynchronizer {
99100
let regionsMonitor = RegionsMonitor(allowsBackgroundLocationUpdates: Bundle.main.backgroundLocationEnabled)
100101
let locationSessionManager = RegionEventsSessionManager(networkController: .init(urlSession: .regionEventsURLSession),
101102
regionEventsRegistry: regionEventsRegistry)
103+
let locationEventReporter = LocationEventReporter(eventStore: .init())
102104

103-
let location = LocationService(regionsMonitor: regionsMonitor,
104-
regionEventsRegistry: regionEventsRegistry,
105-
connectionsRegistry: connectionsRegistry,
106-
sessionManager: locationSessionManager,
107-
eventPublisher: eventPublisher)
105+
let location = LocationService(
106+
regionsMonitor: regionsMonitor,
107+
regionEventsRegistry: regionEventsRegistry,
108+
connectionsRegistry: connectionsRegistry,
109+
sessionManager: locationSessionManager,
110+
eventPublisher: eventPublisher,
111+
eventReporter: locationEventReporter
112+
)
108113

109114
let connectionsMonitor = ConnectionsMonitor(connectionsRegistry: connectionsRegistry)
110115
let nativeServicesCoordinator = NativeServicesCoordinator(locationService: location,
@@ -121,6 +126,7 @@ final class ConnectionsSynchronizer {
121126
self.location = location
122127
self.connectionsMonitor = connectionsMonitor
123128
self.permissionsRequestor = permissionsRequestor
129+
self.locationEventReporter = locationEventReporter
124130

125131
let manager = SynchronizationManager(subscribers: subscribers)
126132
self.scheduler = SynchronizationScheduler(manager: manager,
@@ -259,6 +265,10 @@ final class ConnectionsSynchronizer {
259265
scheduler.developerBackgroundProcessLaunchClosure = launchHandler
260266
scheduler.developerBackgroundProcessExpirationClosure = expirationHandler
261267
}
268+
269+
func setLocationEventReportedClosure(closure: LocationEventsClosure?) {
270+
locationEventReporter.closure = closure
271+
}
262272
}
263273

264274
/// Handles coordination of native services with a set of connections

IFTTT SDK/LocationEvent.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// LocationEvent.swift
3+
// IFTTTConnectSDK
4+
//
5+
// Created by Siddharth Sathyam on 10/25/21.
6+
//
7+
8+
import Foundation
9+
10+
/// Descibes a closure that is invoked whenever the SDK generates a `[LocationEvent]`
11+
public typealias LocationEventsClosure = ([LocationEvent]) -> Void
12+
13+
/// Describes the kinds of region events that can be reported.
14+
public enum RegionEventKind: String {
15+
/// The user entered the region.
16+
case entry = "entry"
17+
18+
/// The user exited the region.
19+
case exit = "exit"
20+
}
21+
22+
/// Describes the reasons why an event was not uploaded.
23+
public enum EventUploadError: Error {
24+
25+
/// The total number of events to be uploaded exceeds a sanity threshold.
26+
case crossedSanityThreshold
27+
28+
/// A network error ocurred in uploading the event.
29+
case networkError
30+
}
31+
32+
/// Describes all of the possible events in the Location monitoring flow.
33+
public enum LocationEvent: Equatable {
34+
35+
/// The location event was recorded by the SDK.
36+
///
37+
/// - Parameters:
38+
/// - `region`: The details of the region that was recorded.
39+
case reported(region: RegionEvent)
40+
41+
/// The SDK attempted to upload a region event.
42+
///
43+
/// - Parameters:
44+
/// - `region`: The details of the region that was attempted to be uploaded.
45+
/// - `delay`: The time in seconds between reporting the event and an attempted upload.
46+
case uploadAttempted(region: RegionEvent, delay: TimeInterval)
47+
48+
/// The SDK successfully uploaded the region event.
49+
///
50+
/// - Parameters:
51+
/// - `region`: The details of the region that was successfully uploaded.
52+
/// - `delay`: The time in seconds between reporting the event and a successful upload.
53+
case uploadSuccessful(region: RegionEvent, delay: TimeInterval)
54+
55+
/// The SDK failed to upload the region event.
56+
///
57+
/// - Parameters:
58+
/// - `region`: The details of the region that was successfully uploaded.
59+
/// - `error`: The `EventUploadError` that occurred.
60+
/// - `delay`: The time in seconds between attempting the event upload and error in completing the upload.
61+
case uploadFailed(region: RegionEvent, error: EventUploadError, delay: TimeInterval)
62+
}

0 commit comments

Comments
 (0)