mirror of
https://gitlab.silvrtree.co.uk/martind2000/WolkSense-Hexiwear.git
synced 2025-01-11 06:15:08 +00:00
704 lines
30 KiB
Swift
704 lines
30 KiB
Swift
//
|
|
// Hexiwear application is used to pair with Hexiwear BLE devices
|
|
// and send sensor readings to WolkSense sensor data cloud
|
|
//
|
|
// Copyright (C) 2016 WolkAbout Technology s.r.o.
|
|
//
|
|
// Hexiwear is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Hexiwear is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
//
|
|
// WebAPI.swift
|
|
//
|
|
|
|
import Foundation
|
|
import UIKit
|
|
import CoreLocation
|
|
|
|
|
|
public enum Reason {
|
|
case InvalidRequest
|
|
case AccessTokenExpired
|
|
case Cancelled
|
|
case CouldNotParseResponse
|
|
case NoData
|
|
case NoSuccessStatusCode(statusCode: Int)
|
|
case Other(NSError)
|
|
}
|
|
|
|
func defaultSimpleFailureHandler(failureReason: Reason) {
|
|
switch failureReason {
|
|
case .InvalidRequest:
|
|
print("Invalid request")
|
|
case .AccessTokenExpired:
|
|
print("Access token expired")
|
|
case .Cancelled:
|
|
print("Request cancelled")
|
|
case .CouldNotParseResponse:
|
|
print("Could not parse JSON")
|
|
case .NoData:
|
|
print("No data")
|
|
case .NoSuccessStatusCode(let statusCode):
|
|
print("No success status code: \(statusCode)")
|
|
case .Other(let err):
|
|
print("Other error \(err.description)")
|
|
}
|
|
}
|
|
|
|
func setNetworkActivityIndicatorVisible(visible: Bool) {
|
|
dispatch_async(dispatch_get_main_queue()) {
|
|
UIApplication.sharedApplication().networkActivityIndicatorVisible = visible
|
|
}
|
|
}
|
|
|
|
public class WebAPIConfiguration {
|
|
let session: NSURLSession
|
|
let refreshTokenTimeout: NSTimeInterval
|
|
let pageSize: Int
|
|
let isNetworkActivityIndicated: Bool
|
|
let userCredentials: UserCredentials
|
|
let wolkaboutURL = "https://api.wolksense.com/api"
|
|
|
|
init(session: NSURLSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()),
|
|
refreshTokenTimeout: NSTimeInterval = 20 * 60, // 20 minutes
|
|
pageSize: Int = 20,
|
|
isNetworkActivityIndicated: Bool = true,
|
|
userCredentials: UserCredentials = UserCredentials())
|
|
{
|
|
self.session = session
|
|
self.refreshTokenTimeout = refreshTokenTimeout
|
|
self.pageSize = pageSize
|
|
self.isNetworkActivityIndicated = isNetworkActivityIndicated
|
|
self.userCredentials = userCredentials
|
|
}
|
|
}
|
|
|
|
public class WebAPI {
|
|
|
|
public static let sharedWebAPI = WebAPI()
|
|
|
|
var defaultFailureHandler: (Reason) -> () {
|
|
get {
|
|
return defaultSimpleFailureHandler
|
|
}
|
|
}
|
|
|
|
private let HTTP_OK = 200
|
|
private let configuration: WebAPIConfiguration
|
|
|
|
private lazy var requestQueue: NSOperationQueue = {
|
|
var queue = NSOperationQueue()
|
|
queue.name = "com.wolkabout.Hexiwear.webAPIQueue"
|
|
queue.maxConcurrentOperationCount = 1 // 1 running request allowed
|
|
return queue
|
|
}()
|
|
|
|
init(webAPIConfiguration: WebAPIConfiguration = WebAPIConfiguration()) {
|
|
self.configuration = webAPIConfiguration
|
|
}
|
|
|
|
deinit {
|
|
cancelRequests()
|
|
}
|
|
|
|
func cancelRequests() {
|
|
if configuration.isNetworkActivityIndicated { setNetworkActivityIndicatorVisible(false) }
|
|
requestQueue.cancelAllOperations()
|
|
}
|
|
|
|
|
|
//MARK: - Device management functions
|
|
|
|
// GET ACTIVATION STATUS
|
|
func getDeviceActivationStatus(deviceSerialNumber: String, onFailure:(Reason) -> (), onSuccess: (activationStatus: String) -> ()) {
|
|
|
|
guard !deviceSerialNumber.isEmpty else { onFailure(.InvalidRequest); return }
|
|
|
|
let requestURL = configuration.wolkaboutURL + "/v2/devices/\(deviceSerialNumber)/activation_status"
|
|
|
|
let onSuccessConverter = { [unowned self] (let responseData: NSData?) in
|
|
|
|
// Response data can be deserialized to String
|
|
guard let responseData = responseData,
|
|
dataString = NSString(data: responseData, encoding: NSUTF8StringEncoding) else {
|
|
onFailure(.CouldNotParseResponse)
|
|
return
|
|
}
|
|
|
|
var responseString: String = String(dataString).stringByReplacingOccurrencesOfString("\r", withString: "")
|
|
responseString = responseString.stringByReplacingOccurrencesOfString("\n", withString: "")
|
|
|
|
let status = self.getActivationStatusFromString(responseString)
|
|
status.isEmpty ? onFailure(.NoData)
|
|
: onSuccess(activationStatus: status)
|
|
}
|
|
|
|
let getDeviceStatusOperation = WolkAboutWebRequest(webApi: self, requestURL: requestURL, queryString: nil, requestData: nil, method: "GET", sendAuthorisationToken: true, isResponseDataExpected: true, isNetworkActivityIndicated: configuration.isNetworkActivityIndicated, onFailure: onFailure, onSuccess: onSuccessConverter)
|
|
requestQueue.addOperation(getDeviceStatusOperation)
|
|
}
|
|
|
|
// GET RANDOM SERIAL
|
|
func getRandomSerial(onFailure:(Reason) -> (), onSuccess: (serial: String) -> ()) {
|
|
let requestURL = configuration.wolkaboutURL + "/v3/devices/random_serial"
|
|
let queryString = "?type=HEXIWEAR"
|
|
|
|
let onSuccessConverter = { [unowned self] (let responseData: NSData?) in
|
|
|
|
// Response data can be deserialized to String
|
|
guard let responseData = responseData,
|
|
dataString = NSString(data: responseData, encoding: NSUTF8StringEncoding) else {
|
|
onFailure(.CouldNotParseResponse)
|
|
return
|
|
}
|
|
|
|
var responseString: String = String(dataString).stringByReplacingOccurrencesOfString("\r", withString: "")
|
|
responseString = responseString.stringByReplacingOccurrencesOfString("\n", withString: "")
|
|
|
|
let serialNumber = self.getSerialFromString(responseString)
|
|
serialNumber.isEmpty ? onFailure(.NoData)
|
|
: onSuccess(serial: serialNumber)
|
|
}
|
|
|
|
let getRandomSerialOperation = WolkAboutWebRequest(webApi: self, requestURL: requestURL, queryString: queryString, requestData: nil, method: "GET", sendAuthorisationToken: true, isResponseDataExpected: true, isNetworkActivityIndicated: configuration.isNetworkActivityIndicated, onFailure: onFailure, onSuccess: onSuccessConverter)
|
|
requestQueue.addOperation(getRandomSerialOperation)
|
|
}
|
|
|
|
// ACTIVATE
|
|
func activateDevice(deviceSerialNumber: String, deviceName: String, onFailure:(Reason) -> (), onSuccess: (pointId: Int, password: String) -> ()) {
|
|
|
|
guard !deviceSerialNumber.isEmpty && !deviceName.isEmpty else { onFailure(.InvalidRequest); return }
|
|
|
|
let requestURL = configuration.wolkaboutURL + "/v2/devices/\(deviceSerialNumber)"
|
|
|
|
let requestJson: [String:AnyObject] = ["name": deviceName]
|
|
guard let requestData = try? NSJSONSerialization.dataWithJSONObject(requestJson, options: NSJSONWritingOptions.PrettyPrinted) else {
|
|
onFailure(.InvalidRequest)
|
|
return
|
|
}
|
|
|
|
let onSuccessConverter = { [unowned self] (let responseData: NSData?) in
|
|
|
|
// Response data can be deserialized to String
|
|
guard let responseData = responseData,
|
|
dataString = NSString(data: responseData, encoding: NSUTF8StringEncoding) else {
|
|
onFailure(.CouldNotParseResponse)
|
|
return
|
|
}
|
|
|
|
var responseString: String = String(dataString).stringByReplacingOccurrencesOfString("\r", withString: "")
|
|
responseString = responseString.stringByReplacingOccurrencesOfString("\n", withString: "")
|
|
|
|
if let pointNumber = self.getPointIdFromString(responseString) {
|
|
let password = self.getPasswordFromString(responseString)
|
|
onSuccess(pointId: pointNumber, password: password)
|
|
}
|
|
else {
|
|
onFailure(.NoData)
|
|
}
|
|
}
|
|
|
|
let activateDeviceOperation = WolkAboutWebRequest(webApi: self, requestURL: requestURL, queryString: nil, requestData: requestData, method: "POST", sendAuthorisationToken: true, isResponseDataExpected: true, isNetworkActivityIndicated: configuration.isNetworkActivityIndicated, onFailure: onFailure, onSuccess: onSuccessConverter)
|
|
requestQueue.addOperation(activateDeviceOperation)
|
|
}
|
|
|
|
|
|
// DEACTIVATE
|
|
func deactivateDevice(deviceSerialNumber: String, onFailure:(Reason) -> (), onSuccess: () -> ()) {
|
|
guard !deviceSerialNumber.isEmpty else { onFailure(.InvalidRequest); return }
|
|
|
|
let requestURL = configuration.wolkaboutURL + "/v2/devices/\(deviceSerialNumber)"
|
|
|
|
let onSuccessConverter = { (_: NSData?) in onSuccess() }
|
|
|
|
let deactivateOperation = WolkAboutWebRequest(webApi: self, requestURL: requestURL, queryString: nil, requestData: nil, method: "DELETE", sendAuthorisationToken: true, isResponseDataExpected: false, isNetworkActivityIndicated: configuration.isNetworkActivityIndicated, onFailure: onFailure, onSuccess: onSuccessConverter)
|
|
requestQueue.addOperation(deactivateOperation)
|
|
}
|
|
|
|
|
|
// FETCH POINTS
|
|
func fetchPoints(onFailure:(Reason) -> (), onSuccess: ([Device]) -> ()) {
|
|
let requestURL = configuration.wolkaboutURL + "/v3/points"
|
|
|
|
let onSuccessConverter = { (let responseData: NSData?) in
|
|
// Response data can be deserialized to JSON
|
|
guard let responseData = responseData, jsonArrayOfDict = try? NSJSONSerialization.JSONObjectWithData(responseData, options: []) as! [[String:AnyObject]] else {
|
|
onFailure(.CouldNotParseResponse)
|
|
return
|
|
}
|
|
|
|
// Generate result from JSON
|
|
var result = [Device]()
|
|
result = jsonArrayOfDict.reduce([]) { (accum, elem) in
|
|
var accum = accum
|
|
if let item = Device.parseDeviceJSON(elem) {
|
|
accum.append(item)
|
|
}
|
|
return accum
|
|
}
|
|
onSuccess(result)
|
|
}
|
|
|
|
let fetchOperation = WolkAboutWebRequest(webApi: self, requestURL: requestURL, queryString: nil, requestData: nil, method: "GET", sendAuthorisationToken: true, isResponseDataExpected: true, isNetworkActivityIndicated: configuration.isNetworkActivityIndicated, onFailure: onFailure, onSuccess: onSuccessConverter)
|
|
requestQueue.addOperation(fetchOperation)
|
|
}
|
|
|
|
|
|
//MARK: - User account management functions
|
|
|
|
// SIGN UP
|
|
func signUp(firstName: String, lastName: String, email: String, password: String, onFailure:(Reason) -> (), onSuccess: () -> ()) {
|
|
guard !firstName.isEmpty && !lastName.isEmpty && !email.isEmpty && !password.isEmpty else { onFailure(.InvalidRequest); return }
|
|
|
|
let requestURL = configuration.wolkaboutURL + "/v2/signUp"
|
|
let requestJson: [String:AnyObject] = ["firstName": firstName, "lastName": lastName, "email": email, "password": password]
|
|
print(requestJson)
|
|
guard let requestData = try? NSJSONSerialization.dataWithJSONObject(requestJson, options: NSJSONWritingOptions.PrettyPrinted) else {
|
|
onFailure(.InvalidRequest)
|
|
return
|
|
}
|
|
|
|
let onSuccessConverter = { (_: NSData?) in onSuccess() }
|
|
|
|
let signUpOperation = WolkAboutWebRequest(webApi: self, requestURL: requestURL, queryString: nil, requestData: requestData, method: "POST", sendAuthorisationToken: false, isResponseDataExpected: false, isNetworkActivityIndicated: configuration.isNetworkActivityIndicated, onFailure: onFailure, onSuccess: onSuccessConverter)
|
|
requestQueue.addOperation(signUpOperation)
|
|
}
|
|
|
|
|
|
//SIGN IN
|
|
func signIn(username: String, password: String, onFailure:(Reason) -> (), onSuccess: () -> ()) {
|
|
guard !username.isEmpty && !password.isEmpty else { onFailure(.InvalidRequest); return }
|
|
|
|
let requestURL = configuration.wolkaboutURL + "/signIn"
|
|
let requestJson: [String:AnyObject] = ["email": username, "password": password]
|
|
guard let requestData = try? NSJSONSerialization.dataWithJSONObject(requestJson, options: NSJSONWritingOptions.PrettyPrinted) else {
|
|
onFailure(.InvalidRequest)
|
|
return
|
|
}
|
|
|
|
|
|
let onSuccessConverter = { (let responseData: NSData?) in
|
|
self.configuration.userCredentials.accessToken = ""
|
|
// Response data can be deserialized to JSON
|
|
guard let responseData = responseData, credentials = try? NSJSONSerialization.JSONObjectWithData(responseData, options: []) as! [String:String] else {
|
|
onFailure(.CouldNotParseResponse)
|
|
return
|
|
}
|
|
|
|
self.configuration.userCredentials.storeCredentials(credentials)
|
|
onSuccess()
|
|
}
|
|
|
|
let signInOperation = WolkAboutWebRequest(webApi: self, requestURL: requestURL, queryString: nil, requestData: requestData, method: "POST", sendAuthorisationToken:false, isResponseDataExpected: true, isNetworkActivityIndicated: configuration.isNetworkActivityIndicated, onFailure: onFailure, onSuccess: onSuccessConverter)
|
|
requestQueue.addOperation(signInOperation)
|
|
}
|
|
|
|
// RESET PASSWORD
|
|
func resetPassword(userEmail: String, onFailure:(Reason) -> (), onSuccess: () -> ()) {
|
|
guard !userEmail.isEmpty else { onFailure(.InvalidRequest); return }
|
|
|
|
let requestURL = configuration.wolkaboutURL + "/reset-password"
|
|
let requestJson: [String:AnyObject] = ["email": userEmail]
|
|
guard let requestData = try? NSJSONSerialization.dataWithJSONObject(requestJson, options: NSJSONWritingOptions.PrettyPrinted) else {
|
|
onFailure(.InvalidRequest)
|
|
return
|
|
}
|
|
|
|
|
|
let onSuccessConverter = { (_: NSData?) in onSuccess() }
|
|
|
|
let verifyOperation = WolkAboutWebRequest(webApi: self, requestURL: requestURL, queryString: nil, requestData: requestData, method: "POST", sendAuthorisationToken: false, isResponseDataExpected: false, isNetworkActivityIndicated: configuration.isNetworkActivityIndicated, onFailure: onFailure, onSuccess: onSuccessConverter)
|
|
requestQueue.addOperation(verifyOperation)
|
|
}
|
|
|
|
// CHANGE PASSWORD
|
|
func changePassword(userEmail: String, oldPassword: String, newPassword: String, onFailure:(Reason) -> (), onSuccess: () -> ()) {
|
|
guard !oldPassword.isEmpty && !newPassword.isEmpty && !userEmail.isEmpty else { onFailure(.InvalidRequest); return }
|
|
|
|
let requestURL = configuration.wolkaboutURL + "/change-password"
|
|
let requestJson: [String:AnyObject] = ["email": userEmail, "oldPassword": oldPassword, "newPassword": newPassword]
|
|
guard let requestData = try? NSJSONSerialization.dataWithJSONObject(requestJson, options: NSJSONWritingOptions.PrettyPrinted) else {
|
|
onFailure(.InvalidRequest)
|
|
return
|
|
}
|
|
|
|
|
|
let onSuccessConverter = { (_: NSData?) in onSuccess() }
|
|
|
|
let verifyOperation = WolkAboutWebRequest(webApi: self, requestURL: requestURL, queryString: nil, requestData: requestData, method: "POST", sendAuthorisationToken: true, isResponseDataExpected: false, isNetworkActivityIndicated: configuration.isNetworkActivityIndicated, onFailure: onFailure, onSuccess: onSuccessConverter)
|
|
requestQueue.addOperation(verifyOperation)
|
|
}
|
|
|
|
}
|
|
|
|
//MARK: - Private functions
|
|
extension WebAPI {
|
|
|
|
//MARK:- WolkAboutWebRequest
|
|
private class WolkAboutWebRequest: NSOperation {
|
|
private unowned let webApi: WebAPI
|
|
private let requestURL: String
|
|
private let queryString: String?
|
|
private let requestData: NSData?
|
|
private let method: String
|
|
private let sendAuthorisationToken: Bool
|
|
private let isResponseDataExpected: Bool
|
|
private let isNetworkActivityIndicated: Bool
|
|
private let failure: (Reason) -> ()
|
|
private let success: (NSData?) -> ()
|
|
|
|
// Request/response vars
|
|
private let requestURLPath: String
|
|
private var request: NSMutableURLRequest!
|
|
private var failureReason: Reason?
|
|
private var result: NSData?
|
|
|
|
// Async task management
|
|
private var responseSemaphore: NSCondition!
|
|
private var responseReceived = false
|
|
|
|
init (webApi: WebAPI, requestURL: String, queryString: String?, requestData: NSData?, method: String, sendAuthorisationToken: Bool, isResponseDataExpected: Bool, isNetworkActivityIndicated: Bool, onFailure:(Reason) -> (), onSuccess: (NSData?) -> ()) {
|
|
self.webApi = webApi
|
|
self.requestURL = requestURL
|
|
self.queryString = queryString
|
|
self.requestData = requestData
|
|
self.method = method
|
|
self.sendAuthorisationToken = sendAuthorisationToken
|
|
self.isResponseDataExpected = isResponseDataExpected
|
|
self.isNetworkActivityIndicated = isNetworkActivityIndicated
|
|
self.failure = onFailure
|
|
self.success = onSuccess
|
|
|
|
self.requestURLPath = requestURL + (queryString ?? "")
|
|
}
|
|
|
|
override func main() {
|
|
|
|
// Bounce if cancelled
|
|
guard !cancelled else {
|
|
failure(Reason.Cancelled)
|
|
return
|
|
}
|
|
|
|
// Bounce if access token expired
|
|
if sendAuthorisationToken {
|
|
guard webApi.checkAccessToken() else {
|
|
failure(.AccessTokenExpired)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Bounce if cancelled
|
|
guard !cancelled else {
|
|
failure(Reason.Cancelled)
|
|
return
|
|
}
|
|
request = webApi.createWebAPIRequest(requestURLPath, method: method, jsonData: requestData, sendAuthorisationToken: sendAuthorisationToken)
|
|
|
|
// Bounce if cancelled
|
|
guard !cancelled else {
|
|
failure(Reason.Cancelled)
|
|
return
|
|
}
|
|
responseSemaphore = NSCondition()
|
|
|
|
if isNetworkActivityIndicated { setNetworkActivityIndicatorVisible(true) }
|
|
|
|
let task = webApi.configuration.session.dataTaskWithRequest(request) { data, response, error in
|
|
|
|
// Bounce if cancelled
|
|
guard !self.cancelled else {
|
|
self.failureReason = .Cancelled
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// There is no error
|
|
guard error == nil else {
|
|
self.failureReason = .Other(error!)
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// Bounce if cancelled
|
|
guard !self.cancelled else {
|
|
self.failureReason = .Cancelled
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// Response is valid
|
|
guard let httpResponse = response as? NSHTTPURLResponse else {
|
|
self.failureReason = .NoData
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// Bounce if cancelled
|
|
guard !self.cancelled else {
|
|
self.failureReason = .Cancelled
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// Response status code is successful
|
|
guard httpResponse.statusCode == self.webApi.HTTP_OK else {
|
|
self.failureReason = .NoSuccessStatusCode(statusCode: httpResponse.statusCode)
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// Bounce if cancelled
|
|
guard !self.cancelled else {
|
|
self.failureReason = .Cancelled
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// Is response data expected (i.e. exit if only statusCode is needed)
|
|
guard self.isResponseDataExpected else {
|
|
self.failureReason = nil
|
|
self.result = nil
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// Bounce if cancelled
|
|
guard !self.cancelled else {
|
|
self.failureReason = .Cancelled
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// There is response data
|
|
guard let responseData = data else {
|
|
self.failureReason = .NoData
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// Bounce if cancelled
|
|
guard !self.cancelled else {
|
|
self.failureReason = .Cancelled
|
|
self.signalResponseReceived()
|
|
return
|
|
}
|
|
|
|
// signal that data task is done
|
|
self.result = responseData
|
|
self.signalResponseReceived()
|
|
|
|
}
|
|
task.resume()
|
|
|
|
// wait for async data task to end
|
|
responseSemaphore.lock()
|
|
while !responseReceived {
|
|
responseSemaphore.wait()
|
|
}
|
|
responseSemaphore.unlock()
|
|
|
|
if isNetworkActivityIndicated { setNetworkActivityIndicatorVisible(false) }
|
|
|
|
// Callback success or failure
|
|
if let reason = failureReason {
|
|
failure(reason)
|
|
}
|
|
else {
|
|
success(result)
|
|
}
|
|
}
|
|
|
|
private func signalResponseReceived() {
|
|
// signal that data task is done
|
|
responseSemaphore.lock()
|
|
responseReceived = true
|
|
responseSemaphore.signal()
|
|
responseSemaphore.unlock()
|
|
}
|
|
}
|
|
|
|
// Create REQUEST
|
|
private func createWebAPIRequest(requestURLPath: String, method: String = "GET", jsonData: NSData? = nil, sendAuthorisationToken: Bool = true) -> NSMutableURLRequest {
|
|
let requestURLPathEscaped = requestURLPath.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
|
|
|
|
let requestURL: NSURL = NSURL(string: requestURLPathEscaped!)!
|
|
let request = NSMutableURLRequest(URL: requestURL)
|
|
request.HTTPMethod = method
|
|
|
|
if let data = jsonData {
|
|
let postDataLengthString = "\(data.length)"
|
|
request.setValue(postDataLengthString, forHTTPHeaderField: "Content-Length")
|
|
request.HTTPBody = data
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
}
|
|
|
|
if let accessToken = configuration.userCredentials.accessToken where sendAuthorisationToken {
|
|
request.setValue(accessToken, forHTTPHeaderField: "Authorization")
|
|
}
|
|
|
|
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
|
|
print(request)
|
|
return request
|
|
}
|
|
|
|
private func makeQueryString(dict: [String:String]) -> String {
|
|
var keyValuePairs = ""
|
|
for (k, v) in dict {
|
|
keyValuePairs += "\(k)=\(v)&"
|
|
}
|
|
return keyValuePairs == "" ? "" : "?\(keyValuePairs[keyValuePairs.startIndex..<keyValuePairs.endIndex.predecessor()])"
|
|
}
|
|
|
|
private func checkAccessToken() -> Bool {
|
|
|
|
if !(configuration.userCredentials.accessTokenExpires != nil && configuration.userCredentials.accessTokenExpires!.timeIntervalSinceNow < configuration.refreshTokenTimeout) {
|
|
return true
|
|
}
|
|
|
|
let responseSemaphore: NSCondition = NSCondition()
|
|
var responseReceived = false
|
|
var accessTokenOk = false
|
|
|
|
if configuration.userCredentials.accessTokenExpires != nil && configuration.userCredentials.accessTokenExpires!.timeIntervalSinceNow < configuration.refreshTokenTimeout {
|
|
let content = String("{\"refreshToken\":\"\(configuration.userCredentials.refreshToken!)\"}");
|
|
guard let jsonData = content.dataUsingEncoding(NSUTF8StringEncoding) else {
|
|
configuration.userCredentials.clearCredentials()
|
|
return false
|
|
}
|
|
|
|
if configuration.isNetworkActivityIndicated { setNetworkActivityIndicatorVisible(true) }
|
|
|
|
let refreshRequest = createWebAPIRequest(configuration.wolkaboutURL + "/refreshToken", jsonData: jsonData, method: "POST")
|
|
configuration.session.dataTaskWithRequest(refreshRequest) { [unowned self] (let data, let response, let error) in
|
|
// Response is valid
|
|
guard let httpResponse = response as? NSHTTPURLResponse else {
|
|
self.configuration.userCredentials.clearCredentials()
|
|
|
|
// signal that data task is done
|
|
responseSemaphore.lock()
|
|
responseReceived = true
|
|
responseSemaphore.signal()
|
|
responseSemaphore.unlock()
|
|
return
|
|
}
|
|
|
|
// Response status code is successful
|
|
guard httpResponse.statusCode == self.HTTP_OK else {
|
|
self.configuration.userCredentials.clearCredentials()
|
|
|
|
// signal that data task is done
|
|
responseSemaphore.lock()
|
|
responseReceived = true
|
|
responseSemaphore.signal()
|
|
responseSemaphore.unlock()
|
|
return
|
|
}
|
|
|
|
// There is response data
|
|
guard let responseData = data else {
|
|
self.configuration.userCredentials.clearCredentials()
|
|
|
|
// signal that data task is done
|
|
responseSemaphore.lock()
|
|
responseReceived = true
|
|
responseSemaphore.signal()
|
|
responseSemaphore.unlock()
|
|
return
|
|
}
|
|
|
|
guard let credentials = try? NSJSONSerialization.JSONObjectWithData(responseData, options: [.MutableContainers]) as! [String:String] else {
|
|
self.configuration.userCredentials.clearCredentials()
|
|
|
|
// signal that data task is done
|
|
responseSemaphore.lock()
|
|
responseReceived = true
|
|
responseSemaphore.signal()
|
|
responseSemaphore.unlock()
|
|
return
|
|
}
|
|
|
|
|
|
self.configuration.userCredentials.storeCredentials(credentials)
|
|
accessTokenOk = true
|
|
|
|
// signal that data task is done
|
|
responseSemaphore.lock()
|
|
responseReceived = true
|
|
responseSemaphore.signal()
|
|
responseSemaphore.unlock()
|
|
|
|
}.resume()
|
|
|
|
// wait for async data task to end
|
|
responseSemaphore.lock()
|
|
while !responseReceived {
|
|
responseSemaphore.wait()
|
|
}
|
|
responseSemaphore.unlock()
|
|
|
|
if configuration.isNetworkActivityIndicated { setNetworkActivityIndicatorVisible(false) }
|
|
|
|
}
|
|
return accessTokenOk
|
|
}
|
|
|
|
private func getActivationStatusFromString(jsonString: String) -> String {
|
|
return getStringFromJSON(jsonString, key: "activationStatus")
|
|
}
|
|
|
|
private func getSerialFromString(jsonString: String) -> String {
|
|
return getStringFromJSON(jsonString, key: "serial")
|
|
}
|
|
|
|
private func getPointIdFromString(jsonString: String) -> Int? {
|
|
return getIntFromJSON(jsonString, key: "pointId")
|
|
}
|
|
|
|
private func getPasswordFromString(jsonString: String) -> String {
|
|
return getStringFromJSON(jsonString, key: "password")
|
|
}
|
|
|
|
private func getStringFromJSON(jsonString: String, key: String) -> String {
|
|
do {
|
|
if let data = jsonString.dataUsingEncoding(NSUTF8StringEncoding),
|
|
authenticationResponse = try NSJSONSerialization.JSONObjectWithData(data, options: [.MutableContainers]) as? NSDictionary,
|
|
stringValue = authenticationResponse[key] as? String
|
|
{
|
|
return stringValue
|
|
}
|
|
}
|
|
catch {
|
|
return ""
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
private func getIntFromJSON(jsonString: String, key: String) -> Int? {
|
|
do {
|
|
if let data = jsonString.dataUsingEncoding(NSUTF8StringEncoding),
|
|
authenticationResponse = try NSJSONSerialization.JSONObjectWithData(data, options: [.MutableContainers]) as? NSDictionary,
|
|
intValue = authenticationResponse[key] as? Int
|
|
{
|
|
return intValue
|
|
}
|
|
|
|
}
|
|
catch {
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|