diff --git a/BluetoothExplorer.xcodeproj/project.pbxproj b/BluetoothExplorer.xcodeproj/project.pbxproj index c586124..6fef7f2 100644 --- a/BluetoothExplorer.xcodeproj/project.pbxproj +++ b/BluetoothExplorer.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 35335A2B20E16D4F006ABD4D /* DateTimeCharacteristic.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 35335A2320E16D4F006ABD4D /* DateTimeCharacteristic.storyboard */; }; 35335A2D20E16D74006ABD4D /* DateTimeCharacteristicViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35335A2C20E16D74006ABD4D /* DateTimeCharacteristicViewController.swift */; }; + 353557C720E28CB700543440 /* InstantiableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 353557C620E28CB700543440 /* InstantiableViewController.swift */; }; + 353557D020E28CD200543440 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 353557CF20E28CD200543440 /* String.swift */; }; 6E13E8CC20D9C7270007111B /* AdvertisementDataManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E13E8CB20D9C7270007111B /* AdvertisementDataManagedObject.swift */; }; 6E13E8E320D9D8290007111B /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E13E8E220D9D8290007111B /* TableViewController.swift */; }; 6E353A4620D7FDB200E94B73 /* CoreDataEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E353A4520D7FDB200E94B73 /* CoreDataEncodable.swift */; }; @@ -168,6 +170,8 @@ /* Begin PBXFileReference section */ 35335A2320E16D4F006ABD4D /* DateTimeCharacteristic.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DateTimeCharacteristic.storyboard; sourceTree = ""; }; 35335A2C20E16D74006ABD4D /* DateTimeCharacteristicViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeCharacteristicViewController.swift; sourceTree = ""; }; + 353557C620E28CB700543440 /* InstantiableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantiableViewController.swift; sourceTree = ""; }; + 353557CF20E28CD200543440 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 6E13E8CB20D9C7270007111B /* AdvertisementDataManagedObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvertisementDataManagedObject.swift; sourceTree = ""; }; 6E13E8E220D9D8290007111B /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; 6E353A4520D7FDB200E94B73 /* CoreDataEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataEncodable.swift; sourceTree = ""; }; @@ -253,6 +257,7 @@ children = ( 6E8C68EC20DA030900232169 /* ActivityIndicatorViewController.swift */, 6EDE00D620DB60D9002E951A /* CharacteristicViewController.swift */, + 353557C620E28CB700543440 /* InstantiableViewController.swift */, ); path = Protocols; sourceTree = ""; @@ -327,6 +332,7 @@ isa = PBXGroup; children = ( 6EDE00C820DB0EB5002E951A /* Data.swift */, + 353557CF20E28CD200543440 /* String.swift */, ); path = Extensions; sourceTree = ""; @@ -600,6 +606,7 @@ 6EFD557E20D83B8F003B75A1 /* ServiceManagedObject.swift in Sources */, 6EDE00C920DB0EB5002E951A /* Data.swift in Sources */, 6EFB3E2520D3E5A600364CA0 /* DeviceStore.swift in Sources */, + 353557C720E28CB700543440 /* InstantiableViewController.swift in Sources */, 6ECCCC7320E091070054663C /* FDGapLayoutGuide.m in Sources */, 6E8C68EA20DA02DA00232169 /* UIAlertAction.swift in Sources */, 6EC0D9C720DE076A005FB128 /* Appearance.swift in Sources */, @@ -611,6 +618,7 @@ 6E8C68F020DA04D500232169 /* Async.swift in Sources */, 6E353A4620D7FDB200E94B73 /* CoreDataEncodable.swift in Sources */, 6EDE00D220DB47FD002E951A /* CustomButton.swift in Sources */, + 353557D020E28CD200543440 /* String.swift in Sources */, 6EFB3E9120D3EF6C00364CA0 /* CoreDataExtensions.swift in Sources */, 6EFB3E9B20D40F2000364CA0 /* PeripheralModel.swift in Sources */, 6E8C68ED20DA030A00232169 /* ActivityIndicatorViewController.swift in Sources */, diff --git a/BluetoothExplorer/BatteryLevelCharacteristic.storyboard b/BluetoothExplorer/BatteryLevelCharacteristic.storyboard index 907bf58..f21338a 100644 --- a/BluetoothExplorer/BatteryLevelCharacteristic.storyboard +++ b/BluetoothExplorer/BatteryLevelCharacteristic.storyboard @@ -12,7 +12,7 @@ - + diff --git a/BluetoothExplorer/Controller/GATT Characteristic Editors/BatteryLevelCharacteristicViewController.swift b/BluetoothExplorer/Controller/GATT Characteristic Editors/BatteryLevelCharacteristicViewController.swift index fdd701e..1e3c857 100644 --- a/BluetoothExplorer/Controller/GATT Characteristic Editors/BatteryLevelCharacteristicViewController.swift +++ b/BluetoothExplorer/Controller/GATT Characteristic Editors/BatteryLevelCharacteristicViewController.swift @@ -12,7 +12,7 @@ import CoreData import Bluetooth import GATT -final class BatteryLevelCharacteristicViewController: UIViewController { +final class BatteryLevelCharacteristicViewController: UIViewController, CharacteristicViewController, InstantiableViewController { // MARK: - IB Outlets @@ -67,17 +67,3 @@ final class BatteryLevelCharacteristicViewController: UIViewController { textLabel.text = value.description } } - -// MARK: - CharacteristicViewController - -extension BatteryLevelCharacteristicViewController: CharacteristicViewController { - - static func fromStoryboard() -> BatteryLevelCharacteristicViewController { - - let storyboard = UIStoryboard(name: "BatteryLevelCharacteristic", bundle: .main) - - let viewController = storyboard.instantiateInitialViewController() as! BatteryLevelCharacteristicViewController - - return viewController - } -} diff --git a/BluetoothExplorer/Controller/GATT Characteristic Editors/DateTimeCharacteristicViewController.swift b/BluetoothExplorer/Controller/GATT Characteristic Editors/DateTimeCharacteristicViewController.swift index ee28c19..5d61ee8 100644 --- a/BluetoothExplorer/Controller/GATT Characteristic Editors/DateTimeCharacteristicViewController.swift +++ b/BluetoothExplorer/Controller/GATT Characteristic Editors/DateTimeCharacteristicViewController.swift @@ -12,7 +12,7 @@ import CoreData import Bluetooth import GATT -final class DateTimeCharacteristicViewController: UIViewController { +final class DateTimeCharacteristicViewController: UIViewController, CharacteristicViewController, InstantiableViewController { // MARK: - IB Outlets @@ -26,35 +26,48 @@ final class DateTimeCharacteristicViewController: UIViewController { var valueDidChange: ((GATTDateTime) -> ())? - var minimumDate: Date? { + private lazy var minimumDate: Date = { var dateComponents = DateComponents() dateComponents.year = Int(GATTDateTime.Year.min.rawValue) dateComponents.timeZone = TimeZone(identifier: "UTC") - return Calendar.current.date(from: dateComponents) - } + guard let minimumDate = calendar.date(from: dateComponents) + else { fatalError("Couldn't create minimumDate") } + + return minimumDate + }() - var maximumDate: Date? { + private lazy var maximumDate: Date = { var dateComponents = DateComponents() dateComponents.year = Int(GATTDateTime.Year.max.rawValue) dateComponents.timeZone = TimeZone(identifier: "UTC") - return Calendar.current.date(from: dateComponents) - } + guard let maximumDate = calendar.date(from: dateComponents) + else { fatalError("Couldn't create maximumDate") } + + return maximumDate + }() + + private lazy var calendar: Calendar = { + + return Calendar(identifier: .gregorian) + }() + + private lazy var timeZone: TimeZone = { + + guard let timezone = TimeZone(identifier: "UTC") + else { fatalError("Couldn't create timezone") } + + return timezone + }() // MARK: - Loading override func viewDidLoad() { super.viewDidLoad() - guard let minimumDate = minimumDate - else { assertionFailure("Couldn't create minimumDate"); return } - - guard let maximumDate = maximumDate - else { assertionFailure("Couldn't create maximumDate"); return } - datePicker.minimumDate = minimumDate datePicker.maximumDate = maximumDate @@ -65,17 +78,10 @@ final class DateTimeCharacteristicViewController: UIViewController { @IBAction func datePickerChanged(_ sender: Any) { - let strDate = CustomDateFormatter.default.string(from: datePicker.date) - dateTimeLabel.text = strDate - - let dateComponents = Calendar.current.dateComponents(in: TimeZone(identifier: "UTC")!, from: datePicker.date) + let dateString = CustomDateFormatter.default.string(from: datePicker.date) + dateTimeLabel.text = dateString - let datetime = GATTDateTime(dateComponents: dateComponents) - - guard let value = datetime - else { assertionFailure("Couldn't create GATTDateTime from components"); return } - - self.value = value + self.value = GATTDateTime(date: datePicker.date) valueDidChange?(value) } @@ -90,26 +96,13 @@ final class DateTimeCharacteristicViewController: UIViewController { func updateDatePicker() { - if let date = value.dateComponents.date { - dateTimeLabel.text = CustomDateFormatter.default.string(from: date) - datePicker.date = date - } - } - -} - -// MARK: - CharacteristicViewController - -extension DateTimeCharacteristicViewController: CharacteristicViewController { - - static func fromStoryboard() -> DateTimeCharacteristicViewController { - - let storyboard = UIStoryboard(name: "DateTimeCharacteristic", bundle: .main) + guard let date = value.dateComponents.date + else { return } - let viewController = storyboard.instantiateInitialViewController() as! DateTimeCharacteristicViewController - - return viewController + dateTimeLabel.text = CustomDateFormatter.default.string(from: date) + datePicker.date = date } + } extension DateTimeCharacteristicViewController { diff --git a/BluetoothExplorer/Controller/PeripheralCharacteristicDetailViewController.swift b/BluetoothExplorer/Controller/PeripheralCharacteristicDetailViewController.swift index ca4a200..1285e1b 100644 --- a/BluetoothExplorer/Controller/PeripheralCharacteristicDetailViewController.swift +++ b/BluetoothExplorer/Controller/PeripheralCharacteristicDetailViewController.swift @@ -141,9 +141,9 @@ final class PeripheralCharacteristicDetailViewController: UITableViewController let canWrite = characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) - func load (_ type: T.Type) -> T { + func load (_ type: T.Type) -> T { - let viewController = T.fromStoryboard() + let viewController: T = .instantiate() if let data = self.value, let value = T.CharacteristicValue(data: data) { diff --git a/BluetoothExplorer/Controller/Protocols/CharacteristicViewController.swift b/BluetoothExplorer/Controller/Protocols/CharacteristicViewController.swift index f9face4..abf78bb 100644 --- a/BluetoothExplorer/Controller/Protocols/CharacteristicViewController.swift +++ b/BluetoothExplorer/Controller/Protocols/CharacteristicViewController.swift @@ -15,9 +15,6 @@ protocol CharacteristicViewController: class { /// The GATT Characteristic type this view controller can edit. associatedtype CharacteristicValue: GATTCharacteristic - /// Initialize and load from storyboard. - static func fromStoryboard() -> Self - var view: UIView! { get } /// The current value. diff --git a/BluetoothExplorer/Controller/Protocols/InstantiableViewController.swift b/BluetoothExplorer/Controller/Protocols/InstantiableViewController.swift new file mode 100644 index 0000000..b1eea90 --- /dev/null +++ b/BluetoothExplorer/Controller/Protocols/InstantiableViewController.swift @@ -0,0 +1,32 @@ +// +// InstantiableViewController.swift +// BluetoothExplorer +// +// Created by Carlos Duclos on 6/26/18. +// Copyright © 2018 PureSwift. All rights reserved. +// + +import UIKit + +protocol InstantiableViewController { + + static func instantiate() -> T +} + +extension InstantiableViewController where Self: UIViewController { + + static func instantiate() -> T { + guard let storyboardName = String(describing: self).text(before: "ViewController") else { + fatalError("The controller name is not standard.") + } + + let storyboard = UIStoryboard(name: storyboardName, bundle: Bundle(for: self)) + let identifier = String(describing: T.self) + guard let viewController = storyboard.instantiateViewController(withIdentifier: identifier) as? T else { + fatalError("The storyboard identifier does not exist.") + } + + return viewController + } + +} diff --git a/BluetoothExplorer/DateTimeCharacteristic.storyboard b/BluetoothExplorer/DateTimeCharacteristic.storyboard index e1f4f04..977a959 100644 --- a/BluetoothExplorer/DateTimeCharacteristic.storyboard +++ b/BluetoothExplorer/DateTimeCharacteristic.storyboard @@ -12,7 +12,7 @@ - + diff --git a/BluetoothExplorer/Extensions/String.swift b/BluetoothExplorer/Extensions/String.swift new file mode 100644 index 0000000..9eaed1c --- /dev/null +++ b/BluetoothExplorer/Extensions/String.swift @@ -0,0 +1,17 @@ +// +// String.swift +// BluetoothExplorer +// +// Created by Carlos Duclos on 6/26/18. +// Copyright © 2018 PureSwift. All rights reserved. +// + +import Foundation + +public extension String { + + func text(before text: String) -> String? { + guard let range = self.range(of: text) else { return nil } + return String(self[self.startIndex..