Skip to content

Commit d0c26f5

Browse files
committed
HMAccessoryBrowser promises
1 parent 059ea08 commit d0c26f5

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// HMAcessoryBrowser+Promise.swift
3+
// Onboarding
4+
//
5+
// Created by Chris Chares on 6/29/18.
6+
// Copyright © 2018 Hunter Douglas. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import HomeKit
11+
import PromiseKit
12+
13+
public enum HMPromiseAccessoryBrowserError: Error {
14+
case noAccessoryFound
15+
}
16+
17+
public class HMPromiseAccessoryBrowser {
18+
private var proxy: BrowserProxy?
19+
20+
public func start(scanInterval: ScanInterval) -> Promise<[HMAccessory]> {
21+
return BrowserProxy(scanInterval: scanInterval).promise
22+
}
23+
24+
public func stop() {
25+
proxy?.cancel()
26+
}
27+
}
28+
29+
private class BrowserProxy: PromiseProxy<[HMAccessory]>, HMAccessoryBrowserDelegate {
30+
let browser = HMAccessoryBrowser()
31+
let scanInterval: ScanInterval
32+
33+
init(scanInterval: ScanInterval) {
34+
self.scanInterval = scanInterval
35+
super.init()
36+
37+
browser.delegate = self;
38+
browser.startSearchingForNewAccessories()
39+
40+
//if we have a timeout, set it up
41+
var timeout: TimeInterval? = nil
42+
switch scanInterval {
43+
case .returnAll(let interval): timeout = interval
44+
case .returnFirst(let interval): timeout = interval
45+
}
46+
47+
if let timeout = timeout {
48+
after(seconds: timeout)
49+
.done { [weak self] () -> Void in
50+
guard let _self = self else { return }
51+
_self.reject(HMPromiseAccessoryBrowserError.noAccessoryFound)
52+
}
53+
}
54+
}
55+
56+
override func fulfill(_ value: [HMAccessory]) {
57+
browser.stopSearchingForNewAccessories()
58+
super.fulfill(value)
59+
}
60+
61+
override func reject(_ error: Error ) {
62+
browser.stopSearchingForNewAccessories()
63+
super.reject(error)
64+
}
65+
66+
override func cancel() {
67+
browser.stopSearchingForNewAccessories()
68+
super.cancel()
69+
}
70+
71+
/**
72+
HMAccessoryBrowser delegate
73+
*/
74+
func accessoryBrowser(_ browser: HMAccessoryBrowser, didFindNewAccessory accessory: HMAccessory) {
75+
if case .returnFirst = scanInterval {
76+
fulfill(browser.discoveredAccessories)
77+
}
78+
}
79+
}

Sources/PromiseProxy.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// PromiseProxy.swift
3+
// HDSDK
4+
//
5+
// Created by Chris Chares on 7/16/18.
6+
// Copyright © 2018 Hunter Douglas. All rights reserved.
7+
//
8+
9+
import PromiseKit
10+
11+
/**
12+
Commonly used functionality when promisifying a delegate pattern
13+
*/
14+
internal class PromiseProxy<T>: NSObject {
15+
internal let (promise, seal) = Promise<T>.pending();
16+
17+
private var retainCycle: PromiseProxy?
18+
19+
override init() {
20+
super.init()
21+
// Create the retain cycle
22+
self.retainCycle = self
23+
// And ensure we break it
24+
_ = promise.ensure { self.retainCycle = nil }
25+
}
26+
27+
/// These functions ensure we only resolve the promise once
28+
func fulfill(_ value: T) {
29+
guard self.promise.isResolved == false else { return }
30+
seal.fulfill(value)
31+
}
32+
func reject(_ error: Error) {
33+
guard self.promise.isResolved == false else { return }
34+
seal.reject(error)
35+
}
36+
37+
func cancel() {
38+
self.reject(PMKError.cancelled)
39+
}
40+
}
41+
42+
/*
43+
Commonly used intervals for scanning
44+
*/
45+
public enum ScanInterval {
46+
// Return after our first item with an optional time limit
47+
case returnFirst(timeout: TimeInterval?)
48+
// Scan for this duration before returning all
49+
case returnAll(interval: TimeInterval)
50+
}

0 commit comments

Comments
 (0)