Skip to content

Commit 757343f

Browse files
authored
Add monitor classes for pretest checklist sparodev#2 (sparodev#5)
* Add monitors for pretest checklist sparodev#2 * Update author in podspec sparodev#2
1 parent d6067f0 commit 757343f

File tree

8 files changed

+345
-5
lines changed

8 files changed

+345
-5
lines changed

.swift-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4
1+
4.0

Podfile

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ target 'WingKit' do
1010
pod 'AWSS3'
1111
pod 'AWSCognito'
1212
pod 'AWSCognitoIdentityProvider'
13+
pod 'ReachabilitySwift'
1314

1415
target 'WingKitTests' do
1516
inherit! :search_paths

Podfile.lock

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,22 @@ PODS:
66
- AWSCore (2.6.1)
77
- AWSS3 (2.6.1):
88
- AWSCore (= 2.6.1)
9+
- ReachabilitySwift (3)
910

1011
DEPENDENCIES:
1112
- AWSCognito
1213
- AWSCognitoIdentityProvider
1314
- AWSCore
1415
- AWSS3
16+
- ReachabilitySwift
1517

1618
SPEC CHECKSUMS:
1719
AWSCognito: 9aad635e11de0febf026e8a8acbbd345b2016801
1820
AWSCognitoIdentityProvider: a335678a0024fa2b44213bc4e86b46b21773e567
1921
AWSCore: e7d37fa0715def975acf1b0d75556576780088b8
2022
AWSS3: 2e18ede28199a5530955dc1a65434da43dcc4d2c
23+
ReachabilitySwift: f5b9bb30a0777fac8f09ce8b067e32faeb29bb64
2124

22-
PODFILE CHECKSUM: c613f50bf91b8944df444eb843e3f53034dbbe6e
25+
PODFILE CHECKSUM: f4811200c8b1ca96836fae5982534dab84a4ad13
2326

2427
COCOAPODS: 1.2.1

WingKit.podspec

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Pod::Spec.new do |s|
2525
# * Write the description between the DESC delimiters below.
2626
# * Finally, don't worry about the indent, CocoaPods strips it!
2727
s.description = <<-DESC
28-
Provides the ability for third party applications to integrate with the Wing REST API and perform lung function tests.
28+
An SDK that allows third parties to interface with Wing API and perform lung function tests.
2929
DESC
3030

3131
s.homepage = "https://github.com/sparodev/WingKit"
@@ -80,7 +80,7 @@ Pod::Spec.new do |s|
8080
# Supports git, hg, bzr, svn and HTTP.
8181
#
8282

83-
s.source = { :git => "https://github.com/sparodev/WingKit.git", :tag => "#{s.version}" }
83+
s.source = { :git => "git@github.com:mwahlig/WingKit.git", :tag => "#{s.version}" }
8484

8585

8686
# ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
@@ -91,7 +91,7 @@ Pod::Spec.new do |s|
9191
# Not including the public_header_files will make all headers public.
9292
#
9393

94-
s.source_files = "Classes", "WingKit/Classes/**/*.swift"
94+
s.source_files = "WingKit/Classes", "WingKit/Classes/**/*.swift"
9595
s.exclude_files = "Classes/Exclude"
9696

9797
# s.public_header_files = "Classes/**/*.h"

WingKit.xcodeproj/project.pbxproj

+14
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@
6868
6CE17A741F74D88800789D00 /* Date+ISO8601.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+ISO8601.swift"; sourceTree = "<group>"; };
6969
6CE17A771F75965B00789D00 /* TestSessionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSessionTest.swift; sourceTree = "<group>"; };
7070
6CE17A791F75966D00789D00 /* TestTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestTest.swift; sourceTree = "<group>"; };
71+
6CF1C1721F855FC900C4F952 /* SensorMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SensorMonitor.swift; sourceTree = "<group>"; };
72+
6CF1C1731F855FC900C4F952 /* AmbientNoiseMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmbientNoiseMonitor.swift; sourceTree = "<group>"; };
73+
6CF1C1741F855FC900C4F952 /* ReachabilityMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReachabilityMonitor.swift; sourceTree = "<group>"; };
7174
846069578BD4FDF47618DFE6 /* Pods_WingKitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WingKitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7275
8EDA753260E429AB8E829D6B /* Pods_WingKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WingKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7376
9DA5ECD775C30D4D089E9857 /* Pods-WingKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WingKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WingKitTests/Pods-WingKitTests.debug.xcconfig"; sourceTree = "<group>"; };
@@ -152,6 +155,7 @@
152155
6CA9A0101F743B0B0085E247 /* Classes */ = {
153156
isa = PBXGroup;
154157
children = (
158+
6CF1C1711F855FC900C4F952 /* Monitors */,
155159
6CE17A721F74D7E500789D00 /* Extensions */,
156160
6CE17A6F1F74D25E00789D00 /* Protocols */,
157161
6CE17A6C1F74C9F000789D00 /* UploadTarget */,
@@ -230,6 +234,16 @@
230234
path = Extensions;
231235
sourceTree = "<group>";
232236
};
237+
6CF1C1711F855FC900C4F952 /* Monitors */ = {
238+
isa = PBXGroup;
239+
children = (
240+
6CF1C1721F855FC900C4F952 /* SensorMonitor.swift */,
241+
6CF1C1731F855FC900C4F952 /* AmbientNoiseMonitor.swift */,
242+
6CF1C1741F855FC900C4F952 /* ReachabilityMonitor.swift */,
243+
);
244+
path = Monitors;
245+
sourceTree = "<group>";
246+
};
233247
9345E7DAD294DB07AFEC62A7 /* Pods */ = {
234248
isa = PBXGroup;
235249
children = (
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//
2+
// AmbientNoiseMonitor.swift
3+
// AWSCognito
4+
//
5+
// Created by Matt Wahlig on 10/4/17.
6+
//
7+
8+
import Foundation
9+
import AVFoundation
10+
11+
public protocol AmbientNoiseMonitorDelegate: class {
12+
func ambientNoiseMonitorDidChangeState(_ monitor: AmbientNoiseMonitor)
13+
}
14+
15+
public class AmbientNoiseMonitor {
16+
17+
public enum Error: Swift.Error {
18+
case microphonePermissionDenied
19+
case recorderConfigurationError
20+
}
21+
22+
public weak var delegate: AmbientNoiseMonitorDelegate?
23+
24+
fileprivate(set) var isActive = false
25+
26+
fileprivate var recorder: AVAudioRecorder?
27+
fileprivate var audioSession = AVAudioSession.sharedInstance()
28+
29+
public fileprivate(set) var isBelowThreshold: Bool = true
30+
31+
public var noiseThreshold: Float = -10.0
32+
public var noiseCheckInterval: TimeInterval = 0.25
33+
34+
fileprivate var noiseCheckTimer: Timer?
35+
36+
public init() {}
37+
38+
public func start(completion: @escaping (Error?) -> Void) {
39+
40+
guard !isActive else { return }
41+
42+
configureAudioSession { error in
43+
44+
if let error = error {
45+
completion(error)
46+
return
47+
}
48+
49+
self.isActive = true
50+
51+
self.startRecorder()
52+
self.startTimer()
53+
54+
completion(nil)
55+
}
56+
}
57+
58+
public func stop() {
59+
60+
isActive = false
61+
62+
stopRecorder()
63+
stopTimer()
64+
}
65+
66+
fileprivate func configureAudioSession(completion: @escaping (Error?) -> Void) {
67+
68+
if recorder == nil {
69+
audioSession.requestRecordPermission({ (granted) in
70+
71+
guard granted else {
72+
completion(Error.microphonePermissionDenied)
73+
return
74+
}
75+
76+
do {
77+
try self.audioSession.setCategory(
78+
AVAudioSessionCategoryPlayAndRecord,
79+
with: .defaultToSpeaker)
80+
try self.audioSession.setActive(true)
81+
} catch {
82+
completion(Error.recorderConfigurationError)
83+
}
84+
85+
let manager = FileManager()
86+
guard let cachesDirectoryURL = manager.urls(for: .cachesDirectory, in: .userDomainMask).first else {
87+
completion(Error.recorderConfigurationError)
88+
return
89+
}
90+
91+
let recordingFileURL = cachesDirectoryURL.appendingPathComponent("recordTest.caf")
92+
let recordSettings: [String : AnyObject] = [
93+
AVFormatIDKey: Int(kAudioFormatAppleIMA4) as AnyObject,
94+
AVSampleRateKey: 44100.0 as AnyObject,
95+
AVNumberOfChannelsKey: 1 as AnyObject,
96+
AVEncoderBitRateKey: 12800 as AnyObject,
97+
AVLinearPCMBitDepthKey: 16 as AnyObject,
98+
AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue as AnyObject
99+
]
100+
101+
do {
102+
self.recorder = try AVAudioRecorder(url: recordingFileURL, settings: recordSettings)
103+
} catch {
104+
completion(Error.recorderConfigurationError)
105+
}
106+
107+
completion(nil)
108+
109+
})
110+
}
111+
}
112+
113+
// MARK: - Recorder
114+
115+
fileprivate func startRecorder() {
116+
self.recorder?.prepareToRecord()
117+
self.recorder?.isMeteringEnabled = true
118+
self.recorder?.record()
119+
}
120+
121+
fileprivate func stopRecorder() {
122+
recorder?.stop()
123+
}
124+
125+
// MARK: - Timer
126+
127+
fileprivate func startTimer() {
128+
noiseCheckTimer = Timer.scheduledTimer(timeInterval: noiseCheckInterval,
129+
target: self, selector: #selector(checkAmbientNoise),
130+
userInfo: nil, repeats: true)
131+
}
132+
133+
fileprivate func stopTimer() {
134+
noiseCheckTimer?.invalidate()
135+
noiseCheckTimer = nil
136+
}
137+
138+
/**
139+
Checks whether the ambient noise is above a threshold
140+
*/
141+
@objc func checkAmbientNoise() {
142+
recorder?.updateMeters()
143+
144+
let previousState = isBelowThreshold
145+
if let averagePower = recorder?.averagePower(forChannel: 0) {
146+
isBelowThreshold = !(averagePower > noiseThreshold)
147+
} else {
148+
isBelowThreshold = false
149+
}
150+
151+
if previousState != isBelowThreshold {
152+
delegate?.ambientNoiseMonitorDidChangeState(self)
153+
}
154+
}
155+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// ReachabilityMonitor.swift
3+
// AWSCognito
4+
//
5+
// Created by Matt Wahlig on 10/4/17.
6+
//
7+
8+
import Foundation
9+
import ReachabilitySwift
10+
11+
enum ReachabilityMonitorError: Error {
12+
case configurationError
13+
case monitorUnavailable
14+
}
15+
16+
public protocol ReachabilityMonitorDelegate: class {
17+
func reachabilityMonitorDidChangeReachability(_ manager: ReachabilityMonitor)
18+
}
19+
20+
public class ReachabilityMonitor {
21+
22+
public weak var delegate: ReachabilityMonitorDelegate?
23+
24+
fileprivate(set) var isActive = false
25+
26+
public var isConnectedToInternet: Bool {
27+
return reachability?.isReachable ?? false
28+
}
29+
30+
fileprivate let reachability = Reachability()
31+
32+
// MARK: - Init
33+
34+
public init() {
35+
configure()
36+
}
37+
38+
deinit {
39+
NotificationCenter.default.removeObserver(self)
40+
}
41+
42+
// MARK: - Start/Stop
43+
44+
public func start() throws {
45+
46+
guard !isActive else { return }
47+
48+
guard let reachability = reachability else {
49+
throw ReachabilityMonitorError.configurationError
50+
}
51+
52+
do {
53+
try reachability.startNotifier()
54+
55+
self.isActive = true
56+
57+
} catch {
58+
throw ReachabilityMonitorError.monitorUnavailable
59+
}
60+
}
61+
62+
public func stop() {
63+
isActive = false
64+
65+
reachability?.stopNotifier()
66+
}
67+
68+
// MARK: - Configure
69+
70+
fileprivate func configure() {
71+
guard let reachability = reachability else {
72+
return
73+
}
74+
75+
NotificationCenter.default.addObserver(
76+
self, selector: #selector(reachabilityChanged(_:)),
77+
name: ReachabilityChangedNotification, object: reachability)
78+
}
79+
80+
// MARK: - Reachability Changed Observer
81+
82+
/**
83+
Triggered when the network reachability changes
84+
85+
- parameter notification:
86+
*/
87+
@objc func reachabilityChanged(_ notification: Notification) {
88+
DispatchQueue.main.async {
89+
self.delegate?.reachabilityMonitorDidChangeReachability(self)
90+
}
91+
}
92+
}
93+

0 commit comments

Comments
 (0)