Carthage/Checkouts/sdk-ble-swift/Source/Firmware/XYFirmwareLoader.swift
//
// XYFirmwareLoader.swift
// Pods-XyBleSdk_Example
//
// Created by Darren Sutherland on 11/27/18.
//
import Foundation
public class XYFirmwareLoader {
public class func locateDocumentsFirmware() -> [URL] {
// Get the document directory url
guard let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return [] }
var firmwareUrls = [URL]()
do {
// Get the directory contents urls (including subfolders urls)
let directoryContents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil, options: [])
firmwareUrls = directoryContents.filter { $0.pathExtension == "img" || $0.pathExtension == "bin" }
} catch {
print(error.localizedDescription)
}
return firmwareUrls
}
public class func locateBundleFirmware(for fileName: String, type: String) -> URL? {
let path: String? = Bundle.main.path(forResource: fileName, ofType: type)
return URL.init(fileURLWithPath: path!)
}
public class func getFirmwareData(from url: URL) -> Data? {
return try? Data(contentsOf: url)
}
public class func getFirmwareData(for path: String, success: @escaping (Data?) -> Void, error: @escaping (Error?) -> Void, progress: @escaping (Float) -> Void) {
guard let loader = XYFirmwareRemoteLoader(path: path) else {
error(XYBluetoothError.unableToUpdateFirmware)
return
}
loader.download(success: success, error: error, progress: progress)
}
}
// MARK: Wraps the remote version JSON data
public struct XYRemoteVersionData: Decodable {
public var version: String, path: String
}
public struct XYRemoteVersionData2: Decodable {
public struct SentinelX: Decodable {
public var version: String, path: String, type: String, bank: Int
}
public struct Firmware: Decodable {
public var version: String, path: String, type: String, priority: Int, bank: Int
}
public struct Xy4: Decodable {
public var firmware: [Firmware]
}
public var sentinelX: SentinelX
public var xy4: Xy4
}
// MARK: Fetches the version JSON and the path to the firmware
public class XYFirmwareRemoteVersionLoader {
public init?(family: XYDeviceFamily) {
guard family.id == XY4BluetoothDevice.id else { return nil }
}
public func get(from path: String = "https://s3.amazonaws.com/xyfirmware.xyo.network/sentinelx/version.json") -> XYRemoteVersionData? {
guard
let url = URL(string: path),
let versionData = self.loadJson(from: url) else {
return nil
}
return versionData
}
private func loadJson(from url: URL) -> XYRemoteVersionData? {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(XYRemoteVersionData.self, from: data)
return jsonData
} catch {}
return nil
}
}
// MARK: Fetches the version JSON and the path to the firmware
public class XYSentinelFirmwareRemoteVersionLoader {
public class func get(from path: String) -> XYRemoteVersionData2? {
guard
let url = URL(string: path),
let versionData = self.loadJson(from: url) else {
return nil
}
return versionData
}
private class func loadJson(from url: URL) -> XYRemoteVersionData2? {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(XYRemoteVersionData2.self, from: data)
return jsonData
} catch {}
return nil
}
}
// MARK: Used for grabbing the binary firmware from a remote server, allows for background download
internal class XYFirmwareRemoteLoader: NSObject, URLSessionDownloadDelegate {
private let url: URL
private var backgroundSession: URLSession?
private var isComplete: Bool = false
private var
success: ((Data?) -> Void)?,
error: ((Error?) -> Void)?,
progress: ((Float) -> Void)?
init?(path: String) {
guard let url = URL(string: path) else { return nil }
self.url = url
super.init()
}
func download(success: @escaping (Data?) -> Void, error: @escaping (Error?) -> Void, progress: @escaping (Float) -> Void) {
self.success = success
self.error = error
self.progress = progress
let sessionConfig = URLSessionConfiguration.background(withIdentifier: self.url.absoluteString)
self.backgroundSession = Foundation.URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
guard let task = self.backgroundSession?.downloadTask(with: self.url) else {
self.backgroundSession?.finishTasksAndInvalidate()
self.error?(XYBluetoothError.unableToUpdateFirmware)
return
}
task.resume()
}
// Download is done
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
let data = try? Data(contentsOf: location)
self.backgroundSession?.finishTasksAndInvalidate()
self.isComplete = true
self.success?(data)
}
// Download is in progress
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
self.progress?(progress)
}
// Something bad happened
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if !isComplete {
self.backgroundSession?.finishTasksAndInvalidate()
self.error?(error)
}
}
}