Skip to content

Commit 7ce7d88

Browse files
authored
Backfill unit tests/documentation sparodev#13 (sparodev#14)
* WIP add unit tests around TestSessionManager * Add additional documentation * Refactor Client class to instance method based sparodev#13 - Update unit tests for Client refactor - Added additional unit tests to TestSessionTest, TestSessionManagerTest, ClientTest * Begin tracking documentation sparodev#13 * Add more documentation sparodev#13 * Expand documentation sparodev#13 * Add jazzy configuration file sparodev#13 * Add more documentation sparodev#13 * Regenerate documentation sparodev#13 * Refactor Test, TestSession and UploadTarget structs to classes. sparodev#13 - Add additional unit tests. - Remove unused Timer methods.
1 parent c299a29 commit 7ce7d88

File tree

124 files changed

+29719
-268
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

124 files changed

+29719
-268
lines changed

.jazzy.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
author: Sparo
2+
author_url: https://sparohealth.com
3+
github_url: https://github.com/sparodev/WingKit
4+
module: WingKit
5+
output: docs
6+
theme: fullwidth

README.md

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
![WingKit: a Lung Function Test SDK](../wingkit-logo.png)
2+
3+
WingKit is a library that allows third parties to integrate with the Wing REST API to perform lung function tests.
4+
5+
## Requirements
6+
7+
- iOS 9.0+
8+
- Xcode 9.0+
9+
- Swift 4.0+
10+
11+
## Installation
12+
13+
### CocoaPods
14+
15+
[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. To install it, run this command in your terminal:
16+
```bash
17+
$ gem install cocoapods
18+
```
19+
20+
> WingKit requires CocoaPods 1.1+ to build.
21+
22+
To integrate WingKit into your project, specifiy it in your `Podfile`:
23+
24+
#### Production
25+
26+
```ruby
27+
source 'https://github.com/sparodev/Wing-CocoaPodSpecs'
28+
platform :ios, '11.0'
29+
use_frameworks!
30+
31+
target '<Your Target Name>' do
32+
pod 'WingKit', '~> 1.0'
33+
end
34+
```
35+
36+
#### Development
37+
38+
1. Clone the WingKit repository to your mac.
39+
2. Reference the local filepath where you cloned the repo in your Podfile:
40+
41+
```ruby
42+
platform :ios, '11.0'
43+
use_frameworks!
44+
45+
target '<Your Target Name>' do
46+
pod 'WingKit', :path => '<path/to/repo>'
47+
end
48+
```
49+
50+
## Examples
51+
52+
Check out the [WingKitExample repo](https://github.com/sparodev/WingKitExample) for an example of how to integrate with WingKit to perform a lung function test and view the results.

WingKit.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Pod::Spec.new do |s|
1717

1818
s.name = "WingKit"
1919
s.version = "0.0.1"
20-
s.summary = "A short description of WingKit."
20+
s.summary = "WingKit is a library written in Swift that allows third parties to interface with the Wing REST API in order to perform lung function tests."
2121

2222
# This description is used to generate tags and improve search results.
2323
# * Think: What does it do? Why did you write it? What is the focus?

WingKit.xcodeproj/project.pbxproj

+8
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
6C4708CE1F875B28009CE4E7 /* wavdata.h in Headers */ = {isa = PBXBuildFile; fileRef = 6CF1C17F1F8584DF00C4F952 /* wavdata.h */; };
2424
6C4708CF1F875B2D009CE4E7 /* waveTrimming.h in Headers */ = {isa = PBXBuildFile; fileRef = 6CF1C1821F8584DF00C4F952 /* waveTrimming.h */; };
2525
6C4708D01F875B2F009CE4E7 /* trimmingTerminalPoints.h in Headers */ = {isa = PBXBuildFile; fileRef = 6CF1C1831F8584DF00C4F952 /* trimmingTerminalPoints.h */; };
26+
6C48EE131FABB2F60016BF4F /* TestSessionManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C48EE121FABB2F60016BF4F /* TestSessionManagerTest.swift */; };
2627
6C64DD4A1F7C1ECB005ED5AA /* Client+UploadTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C64DD491F7C1ECB005ED5AA /* Client+UploadTarget.swift */; };
2728
6C64DD4C1F7C1EE1005ED5AA /* Client+UploadTargetTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C64DD4B1F7C1EE1005ED5AA /* Client+UploadTargetTest.swift */; };
2829
6C71FD4B1F7AD14C00465F32 /* UploadTargetTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C71FD4A1F7AD14C00465F32 /* UploadTargetTest.swift */; };
@@ -33,6 +34,7 @@
3334
6CA9A0191F7442ED0085E247 /* Client+TestSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA9A0181F7442ED0085E247 /* Client+TestSession.swift */; };
3435
6CA9A01C1F7443950085E247 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA9A01B1F7443950085E247 /* Network.swift */; };
3536
6CA9A01E1F7446D20085E247 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA9A01D1F7446D20085E247 /* Endpoint.swift */; };
37+
6CAD06B21FBA8628009D1262 /* TestSessionRecorderTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CAD06B11FBA8628009D1262 /* TestSessionRecorderTest.swift */; };
3638
6CAF8DEA1F7971B600BD1BCB /* ClientTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CAF8DE91F7971B600BD1BCB /* ClientTest.swift */; };
3739
6CBBB57F1F799A4E00295FFD /* Client+TestSessionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CBBB57E1F799A4E00295FFD /* Client+TestSessionTest.swift */; };
3840
6CBBB5811F79A62600295FFD /* NetworkMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CBBB5801F79A62600295FFD /* NetworkMock.swift */; };
@@ -61,6 +63,7 @@
6163
1B34F01B6ECC5BC596F66232 /* Pods-WingKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WingKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-WingKit/Pods-WingKit.release.xcconfig"; sourceTree = "<group>"; };
6264
292A38E8462E99CA17292CEC /* Pods-WingKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WingKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-WingKitTests/Pods-WingKitTests.release.xcconfig"; sourceTree = "<group>"; };
6365
4961034675C9A40D3CB33C13 /* Pods-WingKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WingKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WingKit/Pods-WingKit.debug.xcconfig"; sourceTree = "<group>"; };
66+
6C48EE121FABB2F60016BF4F /* TestSessionManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSessionManagerTest.swift; sourceTree = "<group>"; };
6467
6C64DD491F7C1ECB005ED5AA /* Client+UploadTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+UploadTarget.swift"; sourceTree = "<group>"; };
6568
6C64DD4B1F7C1EE1005ED5AA /* Client+UploadTargetTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+UploadTargetTest.swift"; sourceTree = "<group>"; };
6669
6C71FD4A1F7AD14C00465F32 /* UploadTargetTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadTargetTest.swift; sourceTree = "<group>"; };
@@ -75,6 +78,7 @@
7578
6CA9A0181F7442ED0085E247 /* Client+TestSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+TestSession.swift"; sourceTree = "<group>"; };
7679
6CA9A01B1F7443950085E247 /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = "<group>"; };
7780
6CA9A01D1F7446D20085E247 /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = "<group>"; };
81+
6CAD06B11FBA8628009D1262 /* TestSessionRecorderTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSessionRecorderTest.swift; sourceTree = "<group>"; };
7882
6CAF8DE91F7971B600BD1BCB /* ClientTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientTest.swift; sourceTree = "<group>"; };
7983
6CBBB57E1F799A4E00295FFD /* Client+TestSessionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Client+TestSessionTest.swift"; sourceTree = "<group>"; };
8084
6CBBB5801F79A62600295FFD /* NetworkMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMock.swift; sourceTree = "<group>"; };
@@ -177,6 +181,8 @@
177181
6CBBB5821F79A74300295FFD /* WingKitTestCase.swift */,
178182
6C71FD4A1F7AD14C00465F32 /* UploadTargetTest.swift */,
179183
6C64DD4B1F7C1EE1005ED5AA /* Client+UploadTargetTest.swift */,
184+
6C48EE121FABB2F60016BF4F /* TestSessionManagerTest.swift */,
185+
6CAD06B11FBA8628009D1262 /* TestSessionRecorderTest.swift */,
180186
);
181187
path = WingKitTests;
182188
sourceTree = "<group>";
@@ -544,9 +550,11 @@
544550
6CE17A781F75965B00789D00 /* TestSessionTest.swift in Sources */,
545551
6CBBB5811F79A62600295FFD /* NetworkMock.swift in Sources */,
546552
6CBBB5831F79A74300295FFD /* WingKitTestCase.swift in Sources */,
553+
6C48EE131FABB2F60016BF4F /* TestSessionManagerTest.swift in Sources */,
547554
6CE17A7A1F75966D00789D00 /* TestTest.swift in Sources */,
548555
6CAF8DEA1F7971B600BD1BCB /* ClientTest.swift in Sources */,
549556
6C64DD4C1F7C1EE1005ED5AA /* Client+UploadTargetTest.swift in Sources */,
557+
6CAD06B21FBA8628009D1262 /* TestSessionRecorderTest.swift in Sources */,
550558
6C71FD4B1F7AD14C00465F32 /* UploadTargetTest.swift in Sources */,
551559
);
552560
runOnlyForDeploymentPostprocessing = 0;

WingKit/Classes/Client/Client.swift

+36-15
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,43 @@
88

99
import Foundation
1010

11+
/// Represents the OAuth credentials used to authenticate an application with the Wing API.
1112
public struct OAuthCredentials {
13+
14+
/// The client id.
1215
public var id: String
16+
17+
/// The client secret.
1318
public var secret: String
1419

20+
/**
21+
Initializes a `OAuthCredentials` structure.
22+
23+
- parameter id: The client id of your application.
24+
- parameter secret: The client secret of your application.
25+
*/
1526
public init(id: String, secret: String) {
1627
self.id = id
1728
self.secret = secret
1829
}
1930
}
2031

21-
struct OAuthParameterKeys {
32+
internal struct OAuthParameterKeys {
2233
static let id = "id"
2334
static let secret = "secret"
2435
}
2536

37+
/// The `ClientError` enum describes domain specific errors for the `Client` class.
2638
public enum ClientError: Error {
39+
40+
/// Indicates the url of the request is invalid.
2741
case invalidURL
42+
43+
/// Indicates the request failed due to authentcataion failing.
2844
case unauthorized
29-
case invalidPatientData
3045
}
3146

32-
fileprivate enum AuthenticationEndpoint: Endpoint {
47+
internal enum AuthenticationEndpoint: Endpoint {
3348
case authenticate
3449

3550
var path: String {
@@ -52,27 +67,33 @@ fileprivate enum AuthenticationEndpoint: Endpoint {
5267
}
5368

5469
/**
55-
The 'Client' class acts as the interface for the Wing REST API. All Wing API Requests are routed through this class
70+
The `Client` class acts as the interface for the Wing REST API. All Wing API Requests are routed through this class
5671
to apply the necessary authentication to the requests.
5772
*/
5873

5974
public class Client {
6075

6176
// MARK: - Properties
6277

63-
public static let baseURLPath = "https://api-development.mywing.io/api/v2"
78+
internal let baseURLPath = "https://api-development.mywing.io/api/v2"
6479

65-
/// The OAuth credentials required to authenticate with the Wing API.
66-
public static var oauth: OAuthCredentials? = nil
80+
/**
81+
The OAuth credentials assigned to your application to access the Wing API. Used to authenticate with the Wing API
82+
in order to receive a token to use for subsequent authorized requests.
83+
*/
84+
public var oauth: OAuthCredentials? = nil
6785

6886
/// The authorization token used to make authorized requests.
69-
public static var token: String? {
70-
return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IktyNDJ5b2pHdzM4V3oiLCJ0eXBlIjoiYXV0aCIsImtleUdlbiI6IjEyRUJBYmxnTHJOSlAiLCJpYXQiOjE1MDk0NzU1ODcsImV4cCI6MTU0MTAxMTU4N30.PG6wEYDBwuZeWaUhIQGRPtH1UwiFqBXHs-zOqkuP3CI"
71-
}
87+
public var token: String?
88+
89+
// MARK: - Initialization
90+
91+
/// Initializes an instance of the `Client` class.
92+
public init() {}
7293

73-
internal static func request(for endpoint: Endpoint,
74-
parameters: [String: Any]? = nil,
75-
headers: [String: String]? = nil) throws -> NetworkRequest {
94+
internal func request(for endpoint: Endpoint,
95+
parameters: [String: Any]? = nil,
96+
headers: [String: String]? = nil) throws -> NetworkRequest {
7697

7798
guard let url = URL(string: baseURLPath + endpoint.path) else {
7899
throw ClientError.invalidURL
@@ -112,7 +133,7 @@ public class Client {
112133
- `NetworkError.invalidResponse` if the token could not be parsed from the response.
113134
- `NetworkError.unacceptableStatusCode` if an failure status code is received in the response.
114135
*/
115-
public static func authenticate(completion: @escaping (_ token: String?, _ error: Error?) -> Void) {
136+
public func authenticate(completion: @escaping (_ token: String?, _ error: Error?) -> Void) {
116137

117138
guard let oauth = oauth else {
118139
completion(nil, ClientError.unauthorized)
@@ -141,7 +162,7 @@ public class Client {
141162

142163
guard let json = json,
143164
let token = json["token"] as? String else {
144-
completion(nil, NetworkError.invalidResponse)
165+
completion(nil, ClientError.unauthorized)
145166
return
146167
}
147168

WingKit/Classes/Extensions/Timer.swift

-45
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,6 @@ extension Timer {
1919
return timer
2020
}
2121

22-
/// Create and schedule a timer that will call `block` repeatedly in specified time intervals.
23-
24-
@discardableResult
25-
internal class func every(_ interval: TimeInterval, _ block: @escaping () -> Void) -> Timer {
26-
let timer = Timer.new(every: interval, block)
27-
timer.start()
28-
return timer
29-
}
30-
31-
/// Create and schedule a timer that will call `block` repeatedly in specified time intervals.
32-
/// (This variant also passes the timer instance to the block)
33-
34-
@nonobjc @discardableResult
35-
internal class func every(_ interval: TimeInterval, _ block: @escaping (Timer) -> Void) -> Timer {
36-
let timer = Timer.new(every: interval, block)
37-
timer.start()
38-
return timer
39-
}
40-
4122
// MARK: Create timers without scheduling
4223

4324
/// Create a timer that will call `block` once after the specified time.
@@ -51,32 +32,6 @@ extension Timer {
5132
}
5233
}
5334

54-
/// Create a timer that will call `block` repeatedly in specified time intervals.
55-
///
56-
/// - Note: The timer won't fire until it's scheduled on the run loop.
57-
/// Use `NSTimer.every` to create and schedule a timer in one step.
58-
/// - Note: The `new` class function is a workaround for a crashing bug when using convenience initializers (rdar://18720947)
59-
internal class func new(every interval: TimeInterval, _ block: @escaping () -> Void) -> Timer {
60-
return CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0) { _ in
61-
block()
62-
}
63-
}
64-
65-
/// Create a timer that will call `block` repeatedly in specified time intervals.
66-
/// (This variant also passes the timer instance to the block)
67-
///
68-
/// - Note: The timer won't fire until it's scheduled on the run loop.
69-
/// Use `NSTimer.every` to create and schedule a timer in one step.
70-
/// - Note: The `new` class function is a workaround for a crashing bug when using convenience initializers (rdar://18720947)
71-
72-
@nonobjc internal class func new(every interval: TimeInterval, _ block: @escaping (Timer) -> Void) -> Timer {
73-
var timer: Timer!
74-
timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0) { _ in
75-
block(timer)
76-
}
77-
return timer
78-
}
79-
8035
/// Schedule this timer on the run loop
8136
///
8237
/// By default, the timer is scheduled on the current run loop for the default mode.

0 commit comments

Comments
 (0)