Skip to content

Commit 708cab7

Browse files
committed
Load events via Bonjour
1 parent 71aac0b commit 708cab7

File tree

6 files changed

+160
-35
lines changed

6 files changed

+160
-35
lines changed

Sources/CoreLock/EventsRequest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public extension EventsNetServiceRequest {
4343
}
4444
}
4545

46-
internal extension LockEvent.FetchRequest {
46+
public extension LockEvent.FetchRequest {
4747

4848
init?(queryItems: [URLQueryItem]) {
4949
guard let offset = queryItems.first(.offset).flatMap(UInt8.init)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//
2+
// Events.swift
3+
// CoreLockWebServer
4+
//
5+
// Created by Alsey Coleman Miller on 10/22/19.
6+
//
7+
8+
import Foundation
9+
import CoreLock
10+
import Kitura
11+
12+
internal extension LockWebServer {
13+
14+
func addEventsRoute() {
15+
16+
router.get("/event") { [unowned self] (request, response, next) in
17+
do {
18+
if let statusCode = try self.getEvents(request: request, response: response) {
19+
_ = response.send(status: statusCode)
20+
}
21+
}
22+
catch {
23+
self.log?("\(request.urlURL.path) Internal server error. \(error.localizedDescription)")
24+
dump(error)
25+
_ = response.send(status: .internalServerError)
26+
}
27+
try response.end()
28+
}
29+
}
30+
31+
private func getEvents(request: RouterRequest, response: RouterResponse) throws -> HTTPStatusCode? {
32+
33+
// authenticate
34+
guard let (key, secret) = try authenticate(request: request) else {
35+
return .unauthorized
36+
}
37+
38+
log?("Key \(key.identifier) \(key.name) requested events list")
39+
40+
var fetchRequest = LockEvent.FetchRequest()
41+
42+
if let urlComponents = URLComponents(url: request.urlURL, resolvingAgainstBaseURL: true),
43+
let queryItems = urlComponents.queryItems,
44+
let queryFetchRequest = LockEvent.FetchRequest(queryItems: queryItems) {
45+
var fetchDescription = ""
46+
dump(fetchRequest, to: &fetchDescription)
47+
log?(fetchDescription)
48+
fetchRequest = queryFetchRequest
49+
}
50+
51+
// enforce permission, non-administrators can only view their own events.
52+
if key.permission.isAdministrator == false {
53+
var predicate = fetchRequest.predicate ?? .empty
54+
predicate.keys = [key.identifier]
55+
fetchRequest.predicate = predicate
56+
}
57+
58+
// execute fetch
59+
let list = try events.fetch(fetchRequest)
60+
61+
// encrypt
62+
let eventsResponse = try EventsResponse(encrypt: list, with: secret, encoder: jsonEncoder)
63+
64+
// respond
65+
response.send(eventsResponse)
66+
return nil
67+
}
68+
}

Sources/CoreLockWebServer/Permissions.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import Foundation
99
import CoreLock
1010
import Kitura
11-
import KituraNet
1211

1312
internal extension LockWebServer {
1413

Sources/CoreLockWebServer/Server.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public final class LockWebServer {
6060

6161
addLockInformationRoute()
6262
addPermissionsRoute()
63+
addEventsRoute()
6364
addUpdateRoute()
6465
}
6566

iOS/LockKit/Controller/LockEventsViewController.swift

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -167,42 +167,46 @@ public final class LockEventsViewController: TableViewController {
167167
let locks = self.locks
168168
let context = Store.shared.backgroundContext
169169

170-
if Store.shared.lockManager.central.state == .poweredOn {
171-
performActivity(queue: .bluetooth, { [weak self] in
172-
for lock in locks {
173-
guard let device = try Store.shared.device(for: lock, scanDuration: 1.0) else {
174-
if self?.lock == nil {
175-
continue
176-
} else {
177-
throw CentralError.unknownPeripheral
178-
}
179-
}
180-
let lastEventDate = try context.performErrorBlockAndWait {
181-
try context.find(identifier: lock, type: LockManagedObject.self)
182-
.flatMap { try $0.lastEvent(in: context)?.date }
183-
}
184-
let fetchRequest = FetchRequest(
185-
offset: 0,
186-
limit: nil,
187-
predicate: Predicate(
188-
keys: nil,
189-
start: lastEventDate,
190-
end: nil
191-
)
170+
performActivity(queue: .app, { [weak self] in
171+
guard let self = self else { return }
172+
let expectsLock = self.lock != nil
173+
for lock in locks {
174+
175+
// fetch request
176+
let lastEventDate = try context.performErrorBlockAndWait {
177+
try context.find(identifier: lock, type: LockManagedObject.self)
178+
.flatMap { try $0.lastEvent(in: context)?.date }
179+
}
180+
let fetchRequest = FetchRequest(
181+
offset: 0,
182+
limit: nil,
183+
predicate: Predicate(
184+
keys: nil,
185+
start: lastEventDate,
186+
end: nil
192187
)
193-
do { try Store.shared.listEvents(device, fetchRequest: fetchRequest) }
194-
catch {
195-
if self?.lock == nil {
196-
continue
197-
} else {
198-
throw error
199-
}
188+
)
189+
// first try via Bonjour
190+
if let netService = try Store.shared.netServiceClient.discover(duration: 1.0, timeout: 10.0).first(where: { $0.identifier == lock }) {
191+
192+
try Store.shared.listEvents(netService, fetchRequest: fetchRequest)
193+
194+
} else if Store.shared.lockManager.central.state == .poweredOn,
195+
let device = try DispatchQueue.bluetooth.sync(execute: { try Store.shared.device(for: lock, scanDuration: 2.0) }) {
196+
197+
try DispatchQueue.bluetooth.sync {
198+
_ = try Store.shared.listEvents(device, fetchRequest: fetchRequest)
200199
}
200+
201+
} else if expectsLock {
202+
throw LockError.notInRange(lock: lock)
203+
} else {
204+
continue
201205
}
202-
}, completion: { (viewController, _) in
203-
viewController.needsKeys.removeAll()
204-
})
205-
}
206+
}
207+
}, completion: { (viewController, _) in
208+
viewController.needsKeys.removeAll()
209+
})
206210
}
207211

208212
private subscript (indexPath: IndexPath) -> EventManagedObject {

iOS/LockKit/Model/Store.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,59 @@ public extension Store {
618618
}
619619
}
620620

621+
// MARK: - Bonjour Requests
622+
623+
#if os(iOS)
624+
625+
public extension Store {
626+
627+
@discardableResult
628+
func listEvents(_ lock: LockNetService,
629+
fetchRequest: LockEvent.FetchRequest? = nil) throws -> Bool {
630+
631+
// get lock key
632+
guard let lockCache = self[lock: lock.identifier],
633+
let keyData = self[key: lockCache.key.identifier]
634+
else { return false }
635+
636+
let key = KeyCredentials(
637+
identifier: lockCache.key.identifier,
638+
secret: keyData
639+
)
640+
641+
let events = try netServiceClient.listEvents(
642+
fetchRequest: fetchRequest,
643+
for: lock,
644+
with: key,
645+
timeout: 30
646+
)
647+
648+
backgroundContext.commit { (context) in
649+
try context.insert(events, for: lock.identifier)
650+
}
651+
652+
#if os(iOS)
653+
if preferences.isCloudBackupEnabled {
654+
DispatchQueue.cloud.async { [weak self] in
655+
// upload to iCloud
656+
do {
657+
for event in events {
658+
let value = LockEvent.Cloud(event: event, for: lock.identifier)
659+
try self?.cloud.upload(value)
660+
}
661+
} catch {
662+
log("⚠️ Could not upload latest events to iCloud: \(error.localizedDescription)")
663+
}
664+
}
665+
}
666+
#endif
667+
668+
return true
669+
}
670+
}
671+
672+
#endif
673+
621674
// MARK: - CloudKit Operations
622675

623676
#if os(iOS)

0 commit comments

Comments
 (0)