Skip to content

Commit 08d75bd

Browse files
author
Rover Release Bot 🤖
committed
Releasing 4.1.0
1 parent f1b64c2 commit 08d75bd

29 files changed

+399
-104
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ let package = Package(
9191
path: "Sources/Location"),
9292
.target(
9393
name: "RoverNotifications",
94-
dependencies: ["RoverUI"],
94+
dependencies: ["RoverData", "RoverUI"],
9595
path: "Sources/Notifications"),
9696
.target(
9797
name: "RoverTelephony",
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved.
2+
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
3+
// copy, modify, and distribute this software in source code or binary form for use
4+
// in connection with the web services and APIs provided by Rover.
5+
//
6+
// This copyright notice shall be included in all copies or substantial portions of
7+
// the software.
8+
//
9+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11+
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13+
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15+
16+
public protocol AppLastSeenTimestampManager {
17+
func markAppLastSeen()
18+
}

Sources/Data/Context/Context.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ public struct Context: Codable, Equatable {
181181
// MARK: Conversions
182182
public var conversions: [String]?
183183

184+
// MARK: Last Seen Timestamp
185+
public var lastSeen: Date?
186+
184187
public init(
185188
advertisingIdentifier: String?,
186189
isDarkModeEnabled: Bool?,
@@ -214,7 +217,8 @@ public struct Context: Codable, Equatable {
214217
isTestDevice: Bool?,
215218
timeZone: String?,
216219
userInfo: Attributes?,
217-
conversions: [String]?
220+
conversions: [String]?,
221+
lastSeen: Date?
218222
) {
219223
self.advertisingIdentifier = advertisingIdentifier
220224
self.isDarkModeEnabled = isDarkModeEnabled
@@ -249,5 +253,6 @@ public struct Context: Codable, Equatable {
249253
self.timeZone = timeZone
250254
self.userInfo = userInfo
251255
self.conversions = conversions
256+
self.lastSeen = lastSeen
252257
}
253258
}

Sources/Data/Context/ContextManager.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class ContextManager {
2121
let persistedPushToken = PersistedValue<Context.PushToken>(storageKey: "io.rover.RoverData.pushToken")
2222
let persistedUserInfo = PersistedValue<Attributes>(storageKey: "io.rover.RoverData.userInfo")
2323
let persistedDeviceName = PersistedValue<String>(storageKey: "io.rover.RoverData.deviceName")
24+
let persistedAppLastSeenTimestamp = PersistedValue<Date>(storageKey: "io.rover.RoverData.appLastSeenTimestamp")
2425
let reachability = Reachability(hostname: "google.com")!
2526

2627
init() { }
@@ -264,3 +265,19 @@ extension ContextManager: DeviceNameManager {
264265
self.persistedDeviceName.value = deviceName
265266
}
266267
}
268+
269+
// MARK: AppLastSeenTimestampManager
270+
271+
extension ContextManager: AppLastSeenTimestampManager {
272+
func markAppLastSeen() {
273+
self.persistedAppLastSeenTimestamp.value = Date()
274+
}
275+
}
276+
277+
// MARK: AppLastSeenContextProvider
278+
279+
extension ContextManager: AppLastSeenContextProvider {
280+
var appLastSeen: Date? {
281+
return self.persistedAppLastSeenTimestamp.value
282+
}
283+
}

Sources/Data/Context/DeviceModel.swift

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ enum DeviceModel {
4343
case iPhone13Pro
4444
case iPhone13ProMax
4545
case iPhone13Mini
46+
case iPhoneSE3rdGen
47+
case iPhone14
48+
case iPhone14Plus
49+
case iPhone14Pro
50+
case iPhone14ProMax
4651
case iPodTouch1stGen
4752
case iPodTouch2ndGen
4853
case iPodTouch3rdGen
@@ -70,7 +75,13 @@ enum DeviceModel {
7075
case iPadMini5thGen
7176
case iPadAir3rdGen
7277
case iPad8thGen
78+
case iPad9thGen
79+
case iPadMini6thGen
7380
case iPadAir4thGen
81+
case iPadPro5thGen
82+
case iPadAir5thGen
83+
case iPad10thGen
84+
case iPadPro6thGen
7485

7586
var description: String {
7687
switch self {
@@ -171,7 +182,17 @@ enum DeviceModel {
171182
case .iPhone13ProMax:
172183
return "iPhone 13 Pro Max"
173184
case .iPhone13Mini:
174-
return "iPHone 13 Mini"
185+
return "iPhone 13 Mini"
186+
case .iPhoneSE3rdGen:
187+
return "iPhone SE 3rd Gen"
188+
case .iPhone14:
189+
return "iPhone 14"
190+
case .iPhone14Plus:
191+
return "iPhone 14 Plus"
192+
case .iPhone14Pro:
193+
return "iPhone 14 Pro"
194+
case .iPhone14ProMax:
195+
return "iPhone 14 Pro Max"
175196
case .iPodTouch7thGen:
176197
return "iPod Touch 7th Gen"
177198
case .iPad7thGen:
@@ -186,8 +207,20 @@ enum DeviceModel {
186207
return "iPad Air 3rd Gen"
187208
case .iPad8thGen:
188209
return "iPad 8th Gen"
210+
case .iPad9thGen:
211+
return "iPad 9th Gen"
212+
case .iPadMini6thGen:
213+
return "iPad Mini 6th Gen"
189214
case .iPadAir4thGen:
190215
return "iPad Air 4th Gen"
216+
case .iPadPro5thGen:
217+
return "iPad Pro 5th Gen"
218+
case .iPadAir5thGen:
219+
return "iPad Air 5th Gen"
220+
case .iPad10thGen:
221+
return "iPad 10th Gen"
222+
case .iPadPro6thGen:
223+
return "iPad Pro 6th Gen"
191224
}
192225
}
193226

@@ -253,6 +286,16 @@ enum DeviceModel {
253286
self = .iPhone13Mini
254287
case "iPhone14,5":
255288
self = .iPhone13
289+
case "iPhone14,6":
290+
self = .iPhoneSE3rdGen
291+
case "iPhone14,7":
292+
self = .iPhone14
293+
case "iPhone14,8":
294+
self = .iPhone14Plus
295+
case "iPhone15,2":
296+
self = .iPhone14Pro
297+
case "iPhone15,3":
298+
self = .iPhone14ProMax
256299
case "iPod1,1":
257300
self = .iPodTouch1stGen
258301
case "iPod2,1":
@@ -307,8 +350,20 @@ enum DeviceModel {
307350
self = .iPadAir3rdGen
308351
case "iPad11,6", "iPad11,7":
309352
self = .iPad8thGen
353+
case "iPad12,1", "iPad12,2":
354+
self = .iPad9thGen
310355
case "iPad13,1", "iPad13,2":
311356
self = .iPadAir4thGen
357+
case "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7", "iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11":
358+
self = .iPadPro5thGen
359+
case "iPad13,16", "iPad13,17":
360+
self = .iPadAir5thGen
361+
case "iPad13,18", "iPad13,19":
362+
self = .iPad10thGen
363+
case "iPad14,1", "iPad14,2":
364+
self = .iPadMini6thGen
365+
case "iPad14,3", "iPad14,4", "iPad14,5", "iPad14,6":
366+
self = .iPadPro6thGen
312367

313368
default:
314369
return nil

Sources/Data/Context/ModularContextProvider.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class ModularContextProvider {
2828
weak var timeZoneContextProvider: TimeZoneContextProvider?
2929
weak var userInfoContextProvider: UserInfoContextProvider?
3030
weak var conversionsContextProvider: ConversionsContextProvider?
31+
weak var appLastSeenContextProvider: AppLastSeenContextProvider?
3132

3233
init(
3334
adSupportContextProvider: AdSupportContextProvider?,
@@ -43,7 +44,8 @@ class ModularContextProvider {
4344
telephonyContextProvider: TelephonyContextProvider?,
4445
timeZoneContextProvider: TimeZoneContextProvider?,
4546
userInfoContextProvider: UserInfoContextProvider?,
46-
conversionsContextProvider:ConversionsContextProvider?
47+
conversionsContextProvider: ConversionsContextProvider?,
48+
appLastSeenContextProvider: AppLastSeenContextProvider?
4749
) {
4850
self.adSupportContextProvider = adSupportContextProvider
4951
self.bluetoothContextProvider = bluetoothContextProvider
@@ -59,6 +61,7 @@ class ModularContextProvider {
5961
self.timeZoneContextProvider = timeZoneContextProvider
6062
self.userInfoContextProvider = userInfoContextProvider
6163
self.conversionsContextProvider = conversionsContextProvider
64+
self.appLastSeenContextProvider = appLastSeenContextProvider
6265
}
6366
}
6467

@@ -97,7 +100,8 @@ extension ModularContextProvider: ContextProvider {
97100
isTestDevice: self.debugContextProvider?.isTestDevice,
98101
timeZone: self.timeZoneContextProvider?.timeZone,
99102
userInfo: self.userInfoContextProvider?.userInfo,
100-
conversions: self.conversionsContextProvider?.conversions
103+
conversions: self.conversionsContextProvider?.conversions,
104+
lastSeen: self.appLastSeenContextProvider?.appLastSeen
101105
)
102106
}
103107
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved.
2+
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
3+
// copy, modify, and distribute this software in source code or binary form for use
4+
// in connection with the web services and APIs provided by Rover.
5+
//
6+
// This copyright notice shall be included in all copies or substantial portions of
7+
// the software.
8+
//
9+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11+
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13+
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15+
16+
import Foundation
17+
18+
public protocol AppLastSeenContextProvider: AnyObject {
19+
var appLastSeen: Date? { get }
20+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved.
2+
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
3+
// copy, modify, and distribute this software in source code or binary form for use
4+
// in connection with the web services and APIs provided by Rover.
5+
//
6+
// This copyright notice shall be included in all copies or substantial portions of
7+
// the software.
8+
//
9+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11+
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13+
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15+
16+
import Foundation
17+
import os.log
18+
import RoverFoundation
19+
20+
public class ConversionsManager: ConversionsContextProvider, ConversionsTrackerService {
21+
private let persistedConversions = PersistedValue<Array<String>>(storageKey: "io.rover.data.conversions")
22+
23+
private let oldPersistedConversions = PersistedValue<Set<LegacyTag>>(storageKey: "io.rover.RoverExperiences.conversions")
24+
25+
public var conversions: [String]? {
26+
guard let persistedConversions = self.persistedConversions.value else {
27+
return nil
28+
}
29+
30+
return Array(persistedConversions.prefix(100))
31+
}
32+
33+
public func track(_ tag: String) {
34+
guard var result = self.persistedConversions.value else {
35+
self.persistedConversions.value = [tag]
36+
return
37+
}
38+
39+
result.removeAll(where: { $0 == tag })
40+
result.insert(tag, at: 0)
41+
self.persistedConversions.value = Array(result.prefix(100))
42+
}
43+
}
44+
45+
//This extension is for converting tags from previous versions of the Rover SDK.
46+
//Will be removed at some point.
47+
internal extension ConversionsManager {
48+
func migrateTags() {
49+
guard let oldPersistedConversions = self.oldPersistedConversions.value else {
50+
return
51+
}
52+
53+
let sortedConversions = oldPersistedConversions.sorted {
54+
$0.expiresAt > $1.expiresAt
55+
}.prefix(100)
56+
57+
for tag in sortedConversions {
58+
track(tag.rawValue)
59+
}
60+
61+
self.oldPersistedConversions.value = nil
62+
}
63+
64+
private struct LegacyTag: Codable, Equatable, Hashable {
65+
public static func == (lhs: LegacyTag, rhs: LegacyTag) -> Bool {
66+
return lhs.rawValue == rhs.rawValue
67+
}
68+
69+
public func hash(into hasher: inout Hasher) {
70+
hasher.combine(rawValue)
71+
}
72+
73+
public var rawValue: String
74+
public var expiresAt: Date = Date()
75+
}
76+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2020-present, Rover Labs, Inc. All rights reserved.
2+
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
3+
// copy, modify, and distribute this software in source code or binary form for use
4+
// in connection with the web services and APIs provided by Rover.
5+
//
6+
// This copyright notice shall be included in all copies or substantial portions of
7+
// the software.
8+
//
9+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11+
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13+
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15+
16+
public protocol ConversionsTrackerService: AnyObject {
17+
func track(_ tag: String)
18+
}

Sources/Data/DataAssembler.swift

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public struct DataAssembler: Assembler {
6060
telephonyContextProvider: resolver.resolve(TelephonyContextProvider.self),
6161
timeZoneContextProvider: resolver.resolve(TimeZoneContextProvider.self),
6262
userInfoContextProvider: resolver.resolve(UserInfoContextProvider.self),
63-
conversionsContextProvider: resolver.resolve(ConversionsContextProvider.self)
63+
conversionsContextProvider: resolver.resolve(ConversionsContextProvider.self),
64+
appLastSeenContextProvider: resolver.resolve(AppLastSeenContextProvider.self)
6465
)
6566
}
6667

@@ -170,6 +171,36 @@ public struct DataAssembler: Assembler {
170171
container.register(UserInfoContextProvider.self) { resolver in
171172
resolver.resolve(ContextManager.self)!
172173
}
174+
175+
// MARK: ConversionsManager
176+
177+
container.register(ConversionsManager.self) { resolver in
178+
ConversionsManager()
179+
}
180+
181+
// MARK: ConversionsContextProvider
182+
183+
container.register(ConversionsContextProvider.self) { resolver in
184+
resolver.resolve(ConversionsManager.self)!
185+
}
186+
187+
// MARK: ConversionsTrackerService
188+
189+
container.register(ConversionsTrackerService.self) { resolver in
190+
resolver.resolve(ConversionsManager.self)!
191+
}
192+
193+
// MARK: AppLastSeenContextManager
194+
195+
container.register(AppLastSeenTimestampManager.self) { resolver in
196+
resolver.resolve(ContextManager.self)!
197+
}
198+
199+
// MARK: AppLastSeenContextProvider
200+
201+
container.register(AppLastSeenContextProvider.self) { resolver in
202+
resolver.resolve(ContextManager.self)!
203+
}
173204
}
174205

175206
public func containerDidAssemble(resolver: Resolver) {
@@ -178,5 +209,9 @@ public struct DataAssembler: Assembler {
178209

179210
// Set the context provider on the event queue after assembly to allow circular dependency injection
180211
eventQueue.contextProvider = resolver.resolve(ContextProvider.self)!
212+
213+
let conversionsManager = resolver.resolve(ConversionsManager.self)!
214+
// Migrate any conversion tags from previous versions of Rover
215+
conversionsManager.migrateTags()
181216
}
182217
}

0 commit comments

Comments
 (0)