Skip to content

Commit 6981205

Browse files
committed
Wrapping up tests and moving code to correct folders
1 parent e48f42c commit 6981205

10 files changed

Lines changed: 588 additions & 284 deletions

IFTTT SDK.xcodeproj/project.pbxproj

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
DE2906D3242BF66E00CC2825 /* Connection+Parsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2906D2242BF66E00CC2825 /* Connection+Parsing.swift */; };
6363
DE2AE45C2721DD9E00C4794A /* LocationEventReporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2AE45B2721DD9E00C4794A /* LocationEventReporterTests.swift */; };
6464
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 */; };
6569
DE2F524A2429404200EF986A /* Connection+Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2F52492429404200EF986A /* Connection+Location.swift */; };
6670
DE2F524C242940AD00EF986A /* CLCircularRegion+Parsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE2F524B242940AD00EF986A /* CLCircularRegion+Parsing.swift */; };
6771
DE3074A723DB506D00A3C71F /* AnalyticsNetworkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE3074A623DB506D00A3C71F /* AnalyticsNetworkController.swift */; };
@@ -241,6 +245,10 @@
241245
DE2906D2242BF66E00CC2825 /* Connection+Parsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+Parsing.swift"; sourceTree = "<group>"; };
242246
DE2AE45B2721DD9E00C4794A /* LocationEventReporterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEventReporterTests.swift; sourceTree = "<group>"; };
243247
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>"; };
244252
DE2F52492429404200EF986A /* Connection+Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Location.swift"; sourceTree = "<group>"; };
245253
DE2F524B242940AD00EF986A /* CLCircularRegion+Parsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CLCircularRegion+Parsing.swift"; sourceTree = "<group>"; };
246254
DE3074A623DB506D00A3C71F /* AnalyticsNetworkController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsNetworkController.swift; sourceTree = "<group>"; };
@@ -401,6 +409,15 @@
401409
name = "Web Service Authentication";
402410
sourceTree = "<group>";
403411
};
412+
DE2AE47E27270FD800C4794A /* Reporting */ = {
413+
isa = PBXGroup;
414+
children = (
415+
DE2AE47F27270FED00C4794A /* LocationEventReporter.swift */,
416+
DE2AE4832727106800C4794A /* LocationEventStore.swift */,
417+
);
418+
name = Reporting;
419+
sourceTree = "<group>";
420+
};
404421
DE3074A523DB505400A3C71F /* Analytics */ = {
405422
isa = PBXGroup;
406423
children = (
@@ -603,6 +620,7 @@
603620
FCF29FC32187B903004B1100 /* Internal */ = {
604621
isa = PBXGroup;
605622
children = (
623+
DE2AE47E27270FD800C4794A /* Reporting */,
606624
DE1F036E26A8A33F00B6CF1A /* Web Service Authentication */,
607625
DEC75CB525092B6100F40296 /* Native Services */,
608626
1DFE020C2190E3DB00856ABC /* API.swift */,
@@ -673,6 +691,8 @@
673691
DE641D87252B7BC900C56FF9 /* ConnectButtonController+Public.swift */,
674692
DEC30C01258BECEB00A77A57 /* JSONNetworkController.swift */,
675693
1D981C32219210D7001784C4 /* Result+Queries.swift */,
694+
DE2AE4812727103F00C4794A /* LocationEvent.swift */,
695+
DE2AE4852727132E00C4794A /* RegionEvent.swift */,
676696
);
677697
name = Public;
678698
sourceTree = "<group>";
@@ -920,6 +940,7 @@
920940
DEC08FF02429469C007D7039 /* NativeServices.swift in Sources */,
921941
FC22B1A421ACCDF800738023 /* PassthroughView.swift in Sources */,
922942
DE328DBE243E18BB00603EAC /* Keychain.swift in Sources */,
943+
DE2AE4842727106800C4794A /* LocationEventStore.swift in Sources */,
923944
DEC30C02258BECEB00A77A57 /* JSONNetworkController.swift in Sources */,
924945
FC84B85321C43A3000BAF7ED /* LegalTermsText.swift in Sources */,
925946
DEC75CB42508312F00F40296 /* Connection+Storage.swift in Sources */,
@@ -935,6 +956,7 @@
935956
DE328DB4243CBC9700603EAC /* ConnectionsSynchronizer.swift in Sources */,
936957
DE3074A723DB506D00A3C71F /* AnalyticsNetworkController.swift in Sources */,
937958
DE7666E9239990C5005D6DE3 /* WebServiceAuthentication.swift in Sources */,
959+
DE2AE4862727132E00C4794A /* RegionEvent.swift in Sources */,
938960
1D453EDE2297216A001994FF /* ConnectButton+AnimationState.swift in Sources */,
939961
FC22B19621ACA54A00738023 /* ImageDownloader.swift in Sources */,
940962
FCF29FC52187C7E0004B1100 /* JSON.swift in Sources */,
@@ -981,8 +1003,10 @@
9811003
1D981C33219210D7001784C4 /* Result+Queries.swift in Sources */,
9821004
FC84B84C21C4321F00BAF7ED /* Links.swift in Sources */,
9831005
DE4A4C622436409C004082BF /* SynchronizationScheduler.swift in Sources */,
1006+
DE2AE48027270FED00C4794A /* LocationEventReporter.swift in Sources */,
9841007
1DFE02172190E58700856ABC /* URLSession+JSONTask.swift in Sources */,
9851008
FC7F13A221B06E34002AD8FB /* ImageViewNetworkController.swift in Sources */,
1009+
DE2AE4822727103F00C4794A /* LocationEvent.swift in Sources */,
9861010
1D453ED822971C5C001994FF /* ConnectButton+Style.swift in Sources */,
9871011
DEC75CBB2509741F00F40296 /* LocationLibrary.swift in Sources */,
9881012
DE37620022DF75CB00F8BD38 /* ConnectionDeeplinkAction.swift in Sources */,

IFTTT SDK/ConnectButtonController+Public.swift

Lines changed: 4 additions & 220 deletions
Original file line numberDiff line numberDiff line change
@@ -7,226 +7,6 @@
77

88
import UIKit
99

10-
struct LocationEventStore {
11-
enum EventState: String {
12-
case recorded, uploadStart, uploadSuccess, uploadError
13-
}
14-
15-
struct RecordedEvent {
16-
let state: EventState
17-
let date: Date
18-
19-
init(
20-
state: EventState,
21-
date: Date
22-
) {
23-
self.state = state
24-
self.date = date
25-
}
26-
27-
init?(dictionary: [String: Any]) {
28-
guard let stateRawValue = dictionary["state"] as? String,
29-
let dateRawValue = dictionary["date"] as? TimeInterval,
30-
let state = EventState(rawValue: stateRawValue) else {
31-
return nil
32-
}
33-
self.state = state
34-
self.date = Date(timeIntervalSinceReferenceDate: dateRawValue)
35-
}
36-
37-
var dictionary: [String: Any] {
38-
return [
39-
"state": state.rawValue,
40-
"date": date.timeIntervalSinceReferenceDate
41-
]
42-
}
43-
}
44-
45-
private var eventMap: [String: RecordedEvent]? {
46-
get {
47-
guard let dictionary = UserDefaults.standard.dictionary(forKey: "com.ifttt.locationEventReporter.map") else { return nil }
48-
return dictionary.compactMapValues { value -> RecordedEvent? in
49-
guard let dictionary = value as? [String: Any] else { return nil }
50-
return .init(dictionary: dictionary)
51-
}
52-
}
53-
set {
54-
let mappedDictionary = newValue?.compactMapValues { $0.dictionary }
55-
UserDefaults.standard.set(mappedDictionary, forKey: "com.ifttt.locationEventReporter.map")
56-
}
57-
}
58-
59-
init() {
60-
initializeEventMapIfNecessary()
61-
}
62-
63-
private mutating func initializeEventMapIfNecessary() {
64-
if eventMap == nil {
65-
eventMap = .init()
66-
}
67-
}
68-
69-
subscript(key: String) -> RecordedEvent? {
70-
return eventMap?[key]
71-
}
72-
73-
private mutating func updateRecordedEvent(
74-
_ event: RegionEvent,
75-
state: EventState,
76-
date: Date
77-
) {
78-
initializeEventMapIfNecessary()
79-
var _eventMap = eventMap
80-
_eventMap?[event.recordId.uuidString] = .init(state: state, date: date)
81-
self.eventMap = _eventMap
82-
}
83-
84-
mutating func trackRecordedEvent(_ event: RegionEvent, at date: Date) {
85-
updateRecordedEvent(
86-
event,
87-
state: .recorded,
88-
date: date
89-
)
90-
}
91-
92-
mutating func trackEventUploadStart(_ event: RegionEvent, at date: Date) {
93-
updateRecordedEvent(
94-
event,
95-
state: .uploadStart,
96-
date: date
97-
)
98-
}
99-
100-
mutating func trackEventSuccessfulUpload(_ event: RegionEvent, at date: Date) {
101-
initializeEventMapIfNecessary()
102-
if eventMap?[event.recordId.uuidString] != nil {
103-
var _eventMap = eventMap
104-
_eventMap?[event.recordId.uuidString] = nil
105-
self.eventMap = _eventMap
106-
}
107-
}
108-
109-
mutating func trackEventFailedUpload(_ event: RegionEvent, error: EventUploadError, at date: Date) {
110-
initializeEventMapIfNecessary()
111-
var _eventMap = eventMap
112-
switch error {
113-
case .crossedSanityThreshold:
114-
_eventMap?[event.recordId.uuidString] = nil
115-
case .networkError:
116-
_eventMap?[event.recordId.uuidString] = .init(state: .uploadError, date: date)
117-
}
118-
self.eventMap = _eventMap
119-
}
120-
121-
func delay(for event: RegionEvent, against date: Date) -> TimeInterval {
122-
var delay: TimeInterval = -1
123-
if let record = eventMap?[event.recordId.uuidString] {
124-
delay = date.timeIntervalSince(record.date)
125-
}
126-
return delay
127-
}
128-
129-
mutating func reset() {
130-
eventMap = nil
131-
}
132-
}
133-
134-
final class LocationEventReporter {
135-
private var eventStore: LocationEventStore
136-
137-
var closure: LocationEventsClosure?
138-
139-
init(eventStore: LocationEventStore) {
140-
self.eventStore = eventStore
141-
}
142-
143-
func recordRegionEvent(_ event: RegionEvent, at date: Date = .init()) {
144-
eventStore.trackRecordedEvent(event, at: date)
145-
closure?([.reported(event: event)])
146-
}
147-
148-
func regionEventsStartUpload(_ events: [RegionEvent]) {
149-
process(
150-
events,
151-
state: .uploadStart,
152-
error: nil
153-
)
154-
}
155-
156-
func regionEventsSuccessfulUpload(_ events: [RegionEvent]) {
157-
process(
158-
events,
159-
state: .uploadSuccess,
160-
error: nil
161-
)
162-
}
163-
164-
func regionEventsErrorUpload(_ events: [RegionEvent], error: EventUploadError) {
165-
process(
166-
events,
167-
state: .uploadError,
168-
error: error
169-
)
170-
}
171-
172-
func reset() {
173-
eventStore.reset()
174-
}
175-
176-
private func process(_ events: [RegionEvent], state: LocationEventStore.EventState, error: EventUploadError?) {
177-
let date = Date()
178-
var locationEvents: [LocationEvent]
179-
switch state {
180-
case .recorded:
181-
locationEvents = events.map { event -> LocationEvent in
182-
eventStore.trackRecordedEvent(event, at: date)
183-
return .reported(event: event)
184-
}
185-
case .uploadStart:
186-
locationEvents = events.map { event -> LocationEvent in
187-
let delay = eventStore.delay(for: event, against: date)
188-
eventStore.trackRecordedEvent(event, at: date)
189-
return LocationEvent.uploadAttempted(event: event, delay: delay)
190-
}
191-
case .uploadSuccess:
192-
locationEvents = events.map { event -> LocationEvent in
193-
let delay = eventStore.delay(for: event, against: date)
194-
eventStore.trackRecordedEvent(event, at: date)
195-
return LocationEvent.uploadSuccessful(event: event, delay: delay)
196-
}
197-
case .uploadError:
198-
guard let error = error else {
199-
fatalError("Expecting error to not be nil for this case here")
200-
}
201-
locationEvents = events.map { event -> LocationEvent in
202-
let delay = eventStore.delay(for: event, against: date)
203-
eventStore.trackEventSuccessfulUpload(event, at: date)
204-
return LocationEvent.uploadFailed(event: event, error: error, delay: delay)
205-
}
206-
}
207-
closure?(locationEvents)
208-
}
209-
}
210-
211-
public enum LocationEventKind: String {
212-
case entry = "entry"
213-
case exit = "exit"
214-
}
215-
216-
public enum LocationEvent: Equatable {
217-
case reported(event: RegionEvent)
218-
case uploadAttempted(event: RegionEvent, delay: TimeInterval) // The delay between reporting the event and an attempted upload. This is in seconds.
219-
case uploadSuccessful(event: RegionEvent, delay: TimeInterval) // The delay between attempting the event upload and successfully completing the upload. This is in seconds.
220-
case uploadFailed(event: RegionEvent, error: EventUploadError, delay: TimeInterval) // The delay between attempting the event upload and error completing the upload. This is in seconds.
221-
}
222-
223-
public enum EventUploadError: Error {
224-
case crossedSanityThreshold
225-
case networkError
226-
}
227-
228-
public typealias LocationEventsClosure = ([LocationEvent]) -> Void
229-
23010
/// Describes options to initialize the SDK with
23111
public struct InitializerOptions {
23212

@@ -385,6 +165,10 @@ extension ConnectButtonController {
385165
expirationHandler: expirationHandler)
386166
}
387167

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.
388172
public static func setLocationEventReportedClosure(_ closure: LocationEventsClosure?) {
389173
ConnectionsSynchronizer.shared.setLocationEventReportedClosure(closure: closure)
390174
}

IFTTT SDK/LocationEvent.swift

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

0 commit comments

Comments
 (0)