Conversion to Swift3

This commit is contained in:
Charles Magahern
2017-05-13 00:40:03 -07:00
parent 687b45c73a
commit 091af0844a
16 changed files with 353 additions and 325 deletions
@@ -10,15 +10,15 @@ import UIKit
class MainViewController: UIViewController, SwitchesViewControllerDelegate
{
private var _server: WemoServer
private var _visualizationController: VisualizationViewController = VisualizationViewController()
private var _switchesController: SwitchesViewController = SwitchesViewController()
private var _headerView: HeaderView = HeaderView()
private var _updateDevices: Bool = false
fileprivate var _server: WemoServer
fileprivate var _visualizationController: VisualizationViewController = VisualizationViewController()
fileprivate var _switchesController: SwitchesViewController = SwitchesViewController()
fileprivate var _headerView: HeaderView = HeaderView()
fileprivate var _updateDevices: Bool = false
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
{
let url = NSURL(string: "http://midna.xionsf.com:5000")
let url = URL(string: "http://midna.xionsf.com:5000")
_server = WemoServer(url!)
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
@@ -35,7 +35,7 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
{
super.viewDidLoad()
self.view.backgroundColor = UIColor.blackColor()
self.view.backgroundColor = UIColor.black
self.addChildViewController(_visualizationController)
self.view.addSubview(_visualizationController.view)
@@ -46,7 +46,7 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
self.view.addSubview(_headerView)
_updateConnectivityStatus(.Disconnected)
_updateConnectivityStatus(.disconnected)
}
override func viewDidLayoutSubviews()
@@ -64,7 +64,7 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
)
let bodyBounds = CGRect(
x: 0.0,
y: CGRectGetMaxY(headerBounds),
y: headerBounds.maxY,
width: bounds.size.width,
height: bounds.size.height - headerBounds.size.height
)
@@ -81,8 +81,8 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
var switchesOriginX: CGFloat = 0.0
var switchesWidth: CGFloat = 0.0
if (horizontalSizeClass == .Regular) {
switchesOriginX = CGRectGetMaxX(visualizationFrame)
if (horizontalSizeClass == .regular) {
switchesOriginX = visualizationFrame.maxX
switchesWidth = bodyBounds.size.width - visualizationFrame.size.width
} else {
switchesOriginX = 0.0
@@ -98,32 +98,32 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
_switchesController.view.frame = switchesControllerFrame
}
override func viewDidAppear(animated: Bool)
override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
_headerView.xionLogoView.beginAnimating()
if (!_server.connected) {
_updateConnectivityStatus(.Connecting)
_server.connect { (error: NSError?) -> Void in
_updateConnectivityStatus(.connecting)
_server.connect { (error: Error?) -> Void in
if (error == nil) {
self._reloadDevices()
self._startUpdatingDevices()
} else {
self._updateConnectivityStatus(.Error)
self._updateConnectivityStatus(.error)
}
}
}
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
{
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
super.viewWillTransition(to: size, with: coordinator)
_updateSizeClassPresentation()
}
override func prefersStatusBarHidden() -> Bool
override var prefersStatusBarHidden : Bool
{
return true
}
@@ -141,12 +141,12 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
// MARK: SwitchesViewControllerDelegate
func switchesViewControllerDidToggleDevices(controller: SwitchesViewController, devices: [WemoDevice])
func switchesViewControllerDidToggleDevices(_ controller: SwitchesViewController, devices: [WemoDevice])
{
_updateVisualization(true)
for device in devices {
_server.toggleDevice(device, state: device.state, completion: { (error: NSError?) -> Void in })
_server.toggleDevice(device, state: device.state, completion: { (error: Error?) -> Void in })
}
}
@@ -155,18 +155,18 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
internal func _updateSizeClassPresentation()
{
let horizontalSizeClass = self.traitCollection.horizontalSizeClass
if (horizontalSizeClass == .Regular) {
_visualizationController.view.hidden = false
if (horizontalSizeClass == .regular) {
_visualizationController.view.isHidden = false
} else {
_visualizationController.view.hidden = true
_visualizationController.view.isHidden = true
}
}
internal func _updateVisualization(animated: Bool)
internal func _updateVisualization(_ animated: Bool)
{
var activatedDevicesCount = 0
for device in self.devices {
if (device.state == .On) {
if (device.state == .on) {
activatedDevicesCount += 1
}
}
@@ -177,9 +177,9 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
}
}
internal func _updateConnectivityStatus(status: ConnectionStatus)
internal func _updateConnectivityStatus(_ status: ConnectionStatus)
{
dispatch_async(dispatch_get_main_queue()) { () -> Void in
DispatchQueue.main.async { () -> Void in
self._headerView.connectionStatusView.connectivityStatus = status
self._headerView.setNeedsLayout()
self._visualizationController.connectionStatus = status
@@ -188,14 +188,14 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
internal func _reloadDevices()
{
_server.fetchDevices({ (devices: [WemoDevice], error: NSError?) -> Void in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
_server.fetchDevices({ (devices: [WemoDevice], error: Error?) -> Void in
DispatchQueue.main.async { () -> Void in
if (error == nil) {
self.devices = devices
self._updateConnectivityStatus(.Connected)
self._updateConnectivityStatus(.connected)
} else {
self.devices = []
self._updateConnectivityStatus(.Error)
self._updateConnectivityStatus(.error)
}
}
})
@@ -205,8 +205,8 @@ class MainViewController: UIViewController, SwitchesViewControllerDelegate
{
_updateDevices = true
let interval = dispatch_time(DISPATCH_TIME_NOW, Int64(10 * Double(NSEC_PER_SEC)))
dispatch_after(interval, dispatch_get_main_queue()) { () -> Void in
let interval = DispatchTime.now() + Double(Int64(10 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: interval) { () -> Void in
if (self._updateDevices) {
self._reloadDevices()
self._startUpdatingDevices()
@@ -12,12 +12,12 @@ import UIKit
protocol SwitchesViewControllerDelegate: class
{
func switchesViewControllerDidToggleDevices(controller: SwitchesViewController, devices: [WemoDevice])
func switchesViewControllerDidToggleDevices(_ controller: SwitchesViewController, devices: [WemoDevice])
}
extension SwitchesViewControllerDelegate
{
func switchesViewControllerDidToggleDevices(controller: SwitchesViewController, devices: [WemoDevice]) {}
func switchesViewControllerDidToggleDevices(_ controller: SwitchesViewController, devices: [WemoDevice]) {}
}
class SwitchesViewController: UIViewController,
@@ -25,25 +25,25 @@ class SwitchesViewController: UIViewController,
UICollectionViewDelegateFlowLayout
{
weak var delegate: SwitchesViewControllerDelegate?
private var _collectionView: UICollectionView = UICollectionView(frame: CGRectZero,
fileprivate var _collectionView: UICollectionView = UICollectionView(frame: CGRect.zero,
collectionViewLayout: UICollectionViewFlowLayout())
private var _currentDevicesHash: Int = 0
fileprivate var _currentDevicesHash: Int = 0
static private let collectionViewDeviceSwitchCellReuseIdentifier = "DeviceSwitchReuseID"
static private let collectionViewActionCellReuseIdentifier = "ActionCellReuseID"
static private let collectionViewCellsSpacing: CGFloat = 5.0
static fileprivate let collectionViewDeviceSwitchCellReuseIdentifier = "DeviceSwitchReuseID"
static fileprivate let collectionViewActionCellReuseIdentifier = "ActionCellReuseID"
static fileprivate let collectionViewCellsSpacing: CGFloat = 5.0
private enum ActionCell: Int
fileprivate enum ActionCell: Int
{
case AllOn
case AllOff
case allOn
case allOff
func name() -> String
{
switch self {
case .AllOn:
case .allOn:
return "All On"
case .AllOff:
case .allOff:
return "All Off"
}
}
@@ -62,15 +62,15 @@ class SwitchesViewController: UIViewController,
let deviceCellReuseID = SwitchesViewController.collectionViewDeviceSwitchCellReuseIdentifier
let actionCellReuseID = SwitchesViewController.collectionViewActionCellReuseIdentifier
let layout = _collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.scrollDirection = .Vertical
layout.scrollDirection = .vertical
layout.minimumInteritemSpacing = SwitchesViewController.collectionViewCellsSpacing
layout.minimumLineSpacing = SwitchesViewController.collectionViewCellsSpacing
_collectionView.backgroundColor = UIColor.blackColor()
_collectionView.backgroundColor = UIColor.black
_collectionView.delegate = self
_collectionView.dataSource = self
_collectionView.registerClass(WemoDeviceCellView.self, forCellWithReuseIdentifier: deviceCellReuseID)
_collectionView.registerClass(WemoActionCellView.self, forCellWithReuseIdentifier: actionCellReuseID)
_collectionView.register(WemoDeviceCellView.self, forCellWithReuseIdentifier: deviceCellReuseID)
_collectionView.register(WemoActionCellView.self, forCellWithReuseIdentifier: actionCellReuseID)
self.view.addSubview(_collectionView)
self.devices = []
@@ -91,36 +91,36 @@ class SwitchesViewController: UIViewController,
didSet
{
// sort devices by name
self.devices.sortInPlace({ (d1: WemoDevice, d2: WemoDevice) -> Bool in
return (d1.name.compare(d2.name) == .OrderedAscending)
self.devices.sort(by: { (d1: WemoDevice, d2: WemoDevice) -> Bool in
return (d1.name.compare(d2.name) == .orderedAscending)
})
let hash = self.devices.reduce(0, combine: {$0 ^ $1.hashValue})
let hash = self.devices.reduce(0, {$0 ^ $1.hashValue})
if (hash != _currentDevicesHash) {
let previousSet = NSOrderedSet(array: oldValue)
let newSet = NSOrderedSet(array: self.devices)
var insertedIndexPaths: [NSIndexPath] = []
var updatedIndexPaths: [NSIndexPath] = []
var deletedIndexPaths: [NSIndexPath] = []
var insertedIndexPaths: [IndexPath] = []
var updatedIndexPaths: [IndexPath] = []
var deletedIndexPaths: [IndexPath] = []
// if we have devices now and we didn't before, or vice versa,
// we need to update the action cells
if ((oldValue.count == 0 && self.devices.count != 0) || (self.devices.count == 0 && oldValue.count != 0)) {
for actionCellIdx in 0 ..< ActionCell.count {
let actionCellIndexPath = NSIndexPath(forItem: actionCellIdx, inSection: 0)
let actionCellIndexPath = IndexPath(item: actionCellIdx, section: 0)
updatedIndexPaths.append(actionCellIndexPath)
}
}
// find deletes and updates
for (idx, device) in previousSet.enumerate() {
for (idx, device) in previousSet.enumerated() {
let itemIndex = idx + ActionCell.count
let curIndexPath = NSIndexPath(forItem: itemIndex, inSection: 0)
let curIndexPath = IndexPath(item: itemIndex, section: 0)
if (!newSet.containsObject(device)) {
if (!newSet.contains(device)) {
deletedIndexPaths.append(curIndexPath)
} else if (idx < newSet.count) {
let deviceInNewSet = newSet.objectAtIndex(idx) as! WemoDevice
let deviceInNewSet = newSet.object(at: idx) as! WemoDevice
if (deviceInNewSet != (device as! WemoDevice)) {
updatedIndexPaths.append(curIndexPath)
}
@@ -128,19 +128,19 @@ class SwitchesViewController: UIViewController,
}
// find insertions
for (idx, device) in newSet.enumerate() {
if (!previousSet.containsObject(device)) {
for (idx, device) in newSet.enumerated() {
if (!previousSet.contains(device)) {
let itemIndex = idx + ActionCell.count
let insertedIndexPath = NSIndexPath(forItem: itemIndex, inSection: 0)
let insertedIndexPath = IndexPath(item: itemIndex, section: 0)
insertedIndexPaths.append(insertedIndexPath)
}
}
UIView.performWithoutAnimation { () -> Void in
self._collectionView.performBatchUpdates({ () -> Void in
self._collectionView.deleteItemsAtIndexPaths(deletedIndexPaths)
self._collectionView.reloadItemsAtIndexPaths(updatedIndexPaths)
self._collectionView.insertItemsAtIndexPaths(insertedIndexPaths)
self._collectionView.deleteItems(at: deletedIndexPaths)
self._collectionView.reloadItems(at: updatedIndexPaths)
self._collectionView.insertItems(at: insertedIndexPaths)
}, completion: nil)
}
@@ -151,46 +151,47 @@ class SwitchesViewController: UIViewController,
// MARK: UICollectionView
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return self.devices.count + ActionCell.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
if (indexPath.item < ActionCell.count) {
let reuseID = SwitchesViewController.collectionViewActionCellReuseIdentifier
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseID, forIndexPath: indexPath) as! WemoActionCellView
cell.textLabel.text = ActionCell(rawValue: indexPath.item)?.name().uppercaseString
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseID, for: indexPath) as! WemoActionCellView
cell.textLabel.text = ActionCell(rawValue: indexPath.item)?.name().uppercased()
cell.enabled = (self.devices.count > 0)
return cell
} else {
let reuseID = SwitchesViewController.collectionViewDeviceSwitchCellReuseIdentifier
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseID, forIndexPath: indexPath) as! WemoDeviceCellView
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseID, for: indexPath) as! WemoDeviceCellView
let device = _deviceAtIndexPath(indexPath)
cell.deviceName = device.name
cell.toggled = (device.state == .On)
cell.toggled = (device.state == .on)
cell.ordinal = indexPath.item - ActionCell.count + 1
return cell
}
}
func collectionView(collectionView: UICollectionView,
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
sizeForItemAt indexPath: IndexPath) -> CGSize
{
let spacing = SwitchesViewController.collectionViewCellsSpacing
let bounds = collectionView.bounds
var cellsPerRow: CGFloat = 0.0
switch (self.traitCollection.horizontalSizeClass) {
case .Regular, .Compact where (bounds.size.width >= 400.0):
case .regular where (bounds.size.width >= 400.0),
.compact where (bounds.size.width >= 400.0):
cellsPerRow = 3.0
break
case .Compact:
case .compact:
cellsPerRow = 2.0
default:
cellsPerRow = 2.0
@@ -204,18 +205,18 @@ class SwitchesViewController: UIViewController,
}
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
if (indexPath.item < ActionCell.count) {
let tappedActionCell = ActionCell(rawValue: indexPath.item)
var currentDelay: NSTimeInterval = 0.0
var currentDelay: TimeInterval = 0.0
for i in ActionCell.count ..< collectionView.numberOfItemsInSection(indexPath.section) {
if let cell = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: i, inSection: indexPath.section)) as? WemoDeviceCellView {
let animOptions = UIViewAnimationOptions([.AllowUserInteraction])
for i in ActionCell.count ..< collectionView.numberOfItems(inSection: indexPath.section) {
if let cell = collectionView.cellForItem(at: IndexPath(item: i, section: indexPath.section)) as? WemoDeviceCellView {
let animOptions = UIViewAnimationOptions([.allowUserInteraction])
UIView.animateWithDuration(0.3, delay: currentDelay, options: animOptions, animations: {
cell.toggled = (tappedActionCell == .AllOn)
UIView.animate(withDuration: 0.3, delay: currentDelay, options: animOptions, animations: {
cell.toggled = (tappedActionCell == .allOn)
}, completion: nil)
currentDelay += 0.05
@@ -223,22 +224,22 @@ class SwitchesViewController: UIViewController,
}
for device in self.devices {
device.state = (tappedActionCell == .AllOn ? .On : .Off)
device.state = (tappedActionCell == .allOn ? .on : .off)
}
self.delegate?.switchesViewControllerDidToggleDevices(self, devices: self.devices)
} else {
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! WemoDeviceCellView
let cell = collectionView.cellForItem(at: indexPath) as! WemoDeviceCellView
cell.toggled = !cell.toggled
let device = _deviceAtIndexPath(indexPath)
device.state = (cell.toggled ? .On : .Off)
device.state = (cell.toggled ? .on : .off)
self.delegate?.switchesViewControllerDidToggleDevices(self, devices: [device])
}
}
func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool
func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool
{
if (indexPath.item < ActionCell.count) {
return (self.devices.count > 0)
@@ -247,7 +248,7 @@ class SwitchesViewController: UIViewController,
}
}
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool
{
if (indexPath.item < ActionCell.count) {
return (self.devices.count > 0)
@@ -258,7 +259,7 @@ class SwitchesViewController: UIViewController,
// MARK: Internal
internal func _deviceAtIndexPath(indexPath: NSIndexPath) -> WemoDevice
internal func _deviceAtIndexPath(_ indexPath: IndexPath) -> WemoDevice
{
let deviceIdx = indexPath.item - ActionCell.count
let device = self.devices[deviceIdx]
@@ -11,31 +11,32 @@ import Foundation
import GLKit
import SceneKit
import UIKit
import SceneKit
let π = CGFloat(M_PI)
let π = CGFloat(Double.pi)
class VisualizationViewController: UIViewController
{
private var _scene: SCNScene = SCNScene()
private var _sceneView: SCNView?
private var _cameraNode: SCNNode = SCNNode()
private var _lightNode: SCNNode = SCNNode()
private var _cubletsNode: SCNNode = SCNNode()
private var _cublets: [SCNNode] = []
private var _percentActivated: Float = 0.0
fileprivate var _scene: SCNScene = SCNScene()
fileprivate var _sceneView: SCNView?
fileprivate var _cameraNode: SCNNode = SCNNode()
fileprivate var _lightNode: SCNNode = SCNNode()
fileprivate var _cubletsNode: SCNNode = SCNNode()
fileprivate var _cublets: [SCNNode] = []
fileprivate var _percentActivated: Float = 0.0
static private let cubletsDimensions = 5
static private let cubletsSize = 1.0
static private let cubletsSpacing = 2.0
static private let rotationAnimationKey = "RotationAnimation"
static fileprivate let cubletsDimensions = 5
static fileprivate let cubletsSize = 1.0
static fileprivate let cubletsSpacing = 2.0
static fileprivate let rotationAnimationKey = "RotationAnimation"
// MARK: Overrides
override func loadView()
{
let opts = [SCNPreferredRenderingAPIKey : SCNRenderingAPI.OpenGLES2.rawValue]
let view = SCNView(frame: UIScreen.mainScreen().bounds, options: opts)
view.backgroundColor = UIColor.blackColor()
let opts = [SCNView.Option.preferredRenderingAPI.rawValue : SCNRenderingAPI.openGLES2]
let view = SCNView(frame: UIScreen.main.bounds, options: opts)
view.backgroundColor = UIColor.black
view.scene = _scene
view.allowsCameraControl = false
@@ -55,13 +56,13 @@ class VisualizationViewController: UIViewController
_beginModelResetTimer()
}
override func viewDidAppear(animated: Bool)
override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
_sceneView?.play(nil)
}
override func viewDidDisappear(animated: Bool)
override func viewDidDisappear(_ animated: Bool)
{
super.viewDidDisappear(animated)
_sceneView?.stop(nil)
@@ -69,17 +70,17 @@ class VisualizationViewController: UIViewController
// MARK: API
var connectionStatus: ConnectionStatus = .Disconnected
var connectionStatus: ConnectionStatus = .disconnected
{
didSet
{
switch (self.connectionStatus) {
case .Disconnected, .Connecting, .Error:
_cubletsNode.paused = true
case .disconnected, .connecting, .error:
_cubletsNode.isPaused = true
_lightNode.light?.color = UIColor(white: 0.3, alpha: 1.0)
case .Connected:
_cubletsNode.paused = false
_lightNode.light?.color = UIColor.whiteColor()
case .connected:
_cubletsNode.isPaused = false
_lightNode.light?.color = UIColor.white
}
}
}
@@ -97,7 +98,7 @@ class VisualizationViewController: UIViewController
}
}
func setPercentActivated(percentage: Float, animated: Bool)
func setPercentActivated(_ percentage: Float, animated: Bool)
{
let cubletsCount = _cublets.count
let cubletsToActivate = UInt(percentage * Float(cubletsCount))
@@ -124,8 +125,8 @@ class VisualizationViewController: UIViewController
let rotCoeff = CGFloat(arc4random() % 2 == 0 ? -1.0 : 1.0)
let rotAngle = CGFloat(rotCoeff * π / 4.0)
let rotAction = SCNAction.rotateByAngle(rotAngle, aroundAxis: rotAxis, duration: 0.8)
rotAction.timingMode = .Linear
let rotAction = SCNAction.rotate(by: rotAngle, around: rotAxis, duration: 0.8)
rotAction.timingMode = .linear
rotAction.timingFunction = { (t: Float) -> Float in
return min(((log10(4.0 * (t + 0.03)) + 1.0) / 1.5), 1.0)
}
@@ -134,7 +135,7 @@ class VisualizationViewController: UIViewController
// smooth transition
let longTermAnimKey = VisualizationViewController.rotationAnimationKey
let newLongTermRotAction = _createLongTermRotationAnimation((rotCoeff * 2.0 * π), rotAxis)
_cubletsNode.removeActionForKey(longTermAnimKey)
_cubletsNode.removeAction(forKey: longTermAnimKey)
let actionSeq = SCNAction.sequence([rotAction, newLongTermRotAction])
_cubletsNode.runAction(actionSeq, forKey: longTermAnimKey)
@@ -166,8 +167,8 @@ class VisualizationViewController: UIViewController
internal func _setupLights()
{
let light = SCNLight()
light.type = SCNLightTypeOmni
light.color = UIColor.whiteColor()
light.type = SCNLight.LightType.omni
light.color = UIColor.white
_lightNode = SCNNode()
_lightNode.light = light
@@ -178,7 +179,7 @@ class VisualizationViewController: UIViewController
internal func _setupModel()
{
_cubletsNode.enumerateChildNodesUsingBlock { $0.0.removeFromParentNode() }
_cubletsNode.enumerateChildNodes { $0.0.removeFromParentNode() }
_cublets.removeAll()
let sz = Float(VisualizationViewController.cubletsSize)
@@ -191,7 +192,7 @@ class VisualizationViewController: UIViewController
// setup material and geometry. each node needs its own material for the activation effect.
let geom = SCNBox(width: CGFloat(sz), height: CGFloat(sz), length: CGFloat(sz), chamferRadius: 0.0)
let material = SCNMaterial()
material.diffuse.contents = UIColor.whiteColor()
material.diffuse.contents = UIColor.white
material.transparency = 0.75
// generate nodes for each cublet
@@ -216,7 +217,7 @@ class VisualizationViewController: UIViewController
}
_cubletsNode.position = SCNVector3Zero
if (_cubletsNode.parentNode == nil) {
if (_cubletsNode.parent == nil) {
_scene.rootNode.addChildNode(_cubletsNode)
}
}
@@ -232,8 +233,8 @@ class VisualizationViewController: UIViewController
internal func _setupEffects()
{
let techniqueURL = NSBundle.mainBundle().URLForResource("CubletsTechnique", withExtension: "plist")
let techniqueDict = NSDictionary(contentsOfURL: techniqueURL!) as! [String : AnyObject]
let techniqueURL = Bundle.main.url(forResource: "CubletsTechnique", withExtension: "plist")
let techniqueDict = NSDictionary(contentsOf: techniqueURL!) as! [String : AnyObject]
let technique = SCNTechnique(dictionary: techniqueDict)
_sceneView?.technique = technique
@@ -244,8 +245,8 @@ class VisualizationViewController: UIViewController
/* since this visualization is running all the time, trigonometric functions begin
malfunctioning at very large numbers. just reload the model every 24 hours so we
don't have to see it */
let reloadModelTime = dispatch_time(DISPATCH_TIME_NOW, Int64(60 * 60 * 24 * NSEC_PER_SEC))
dispatch_after(reloadModelTime, dispatch_get_main_queue()) { [weak self] in
let reloadModelTime = DispatchTime.now() + Double(Int64(60 * 60 * 24 * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: reloadModelTime) { [weak self] in
if let strongSelf = self {
strongSelf._cubletsNode.removeFromParentNode()
strongSelf._cubletsNode = SCNNode()
@@ -263,25 +264,25 @@ class VisualizationViewController: UIViewController
}
}
internal func _setCubletActivated(cublet: SCNNode, activated: Bool)
internal func _setCubletActivated(_ cublet: SCNNode, activated: Bool)
{
let material = cublet.geometry?.firstMaterial
material?.diffuse.contents = (activated ? UIColor.redColor() : UIColor.whiteColor())
material?.diffuse.contents = (activated ? UIColor.red : UIColor.white)
}
internal func _setAnimationSpeed(speed: CGFloat)
internal func _setAnimationSpeed(_ speed: CGFloat)
{
let key = VisualizationViewController.rotationAnimationKey
if let action = _cubletsNode.actionForKey(key) {
_cubletsNode.removeActionForKey(key)
if let action = _cubletsNode.action(forKey: key) {
_cubletsNode.removeAction(forKey: key)
action.speed = speed
_cubletsNode.runAction(action, forKey: key)
}
}
internal func _createLongTermRotationAnimation(rotAngle: CGFloat, _ rotAxis: SCNVector3) -> SCNAction
internal func _createLongTermRotationAnimation(_ rotAngle: CGFloat, _ rotAxis: SCNVector3) -> SCNAction
{
return SCNAction.repeatActionForever(SCNAction.rotateByAngle(rotAngle, aroundAxis: rotAxis, duration: 40.0))
return SCNAction.repeatForever(SCNAction.rotate(by: rotAngle, around: rotAxis, duration: 40.0))
}
}
+56 -54
View File
@@ -10,40 +10,46 @@ import Foundation
enum ConnectionStatus
{
case Disconnected
case Connecting
case Connected
case Error
case disconnected
case connecting
case connected
case error
}
enum ConnectionError : Error
{
case unknown
case serverUnavailable
}
class WemoServer
{
private(set) var baseURL: NSURL
private(set) var connected: Bool = false
fileprivate(set) var baseURL: URL
fileprivate(set) var connected: Bool = false
private var _urlSession: NSURLSession
private var _errorStream: StandardErrorOutputStream = StandardErrorOutputStream()
private var _operationQueue: NSOperationQueue = NSOperationQueue()
fileprivate var _urlSession: URLSession
fileprivate var _errorStream: StandardErrorOutputStream = StandardErrorOutputStream()
fileprivate var _operationQueue: OperationQueue = OperationQueue()
init(_ url: NSURL)
init(_ url: URL)
{
self.baseURL = url
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
_urlSession = NSURLSession(configuration: config)
let config = URLSessionConfiguration.default
_urlSession = URLSession(configuration: config)
_operationQueue.maxConcurrentOperationCount = 1
}
func connect(completion: (NSError?) -> Void)
func connect(_ completion: @escaping (Error?) -> Void)
{
if (!self.connected) {
let op = ConnectOperation(baseURL: self.baseURL, session: _urlSession)
weak var weakOp = op
op.completionBlock = {
guard let strongOp = weakOp else { completion(nil) ; return }
if (strongOp.error != nil) {
self._logError("Error connecting to server", error: strongOp.error!)
if let error = strongOp.error {
self._logError("Error connecting to server", error: error)
} else {
self.connected = true
}
@@ -56,68 +62,68 @@ class WemoServer
}
}
func disconnect(completion: (NSError?) -> Void)
func disconnect(_ completion: (Error?) -> Void)
{
self.connected = false
completion(nil)
}
func fetchDevices(completion: ([WemoDevice], NSError?) -> Void)
func fetchDevices(_ completion: @escaping ([WemoDevice], Error?) -> Void)
{
if (self.connected) {
let op = FetchDevicesOperation(baseURL: self.baseURL, session: _urlSession)
weak var weakOp = op
op.completionBlock = {
guard let strongOp = weakOp else { completion([], nil) ; return }
if (strongOp.error != nil) {
self._logError("Error fetching devices", error: strongOp.error!)
if let error = strongOp.error {
self._logError("Error fetching devices", error: error)
}
completion(strongOp.devices, strongOp.error)
}
_operationQueue.addOperation(op)
} else {
let err = NSError.xionError(.ConnectionError)
let err = ConnectionError.serverUnavailable
completion([], err)
}
}
func toggleDevice(device: WemoDevice, state: WemoDevice.State, completion: (NSError?) -> Void)
func toggleDevice(_ device: WemoDevice, state: WemoDevice.State, completion: @escaping (Error?) -> Void)
{
if (self.connected) {
let op = ToggleDeviceOperation(baseURL: self.baseURL, session: _urlSession, device: device, state: state)
weak var weakOp = op
op.completionBlock = {
guard let strongOp = weakOp else { completion(nil) ; return }
if (strongOp.error != nil) {
self._logError("Error toggling device", error: strongOp.error!)
if let error = strongOp.error {
self._logError("Error toggling device", error: error)
}
completion(strongOp.error)
}
_operationQueue.addOperation(op)
} else {
let err = NSError.xionError(.ConnectionError)
let err = ConnectionError.serverUnavailable
completion(err)
}
}
// MARK: Internal
internal func _logError(description: String, error: NSError)
internal func _logError(_ description: String, error: Error)
{
print("ERROR: \(description) \(error)", toStream: &_errorStream)
print("ERROR: \(description) \(error)", to: &_errorStream)
}
}
internal class WemoOperation : NSOperation
internal class WemoOperation : Operation
{
var baseURL: NSURL
var session: NSURLSession
var baseURL: URL
var session: URLSession
internal(set) var error: NSError?
internal(set) var error: Error?
init(baseURL: NSURL, session: NSURLSession)
init(baseURL: URL, session: URLSession)
{
self.baseURL = baseURL
self.session = session
@@ -129,16 +135,14 @@ internal class ConnectOperation : WemoOperation
override func main()
{
let semaphore = Semaphore(value: 0)
let url = self.baseURL.URLByAppendingPathComponent("api/environment")
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
let url = self.baseURL.appendingPathComponent("api/environment")
var request = URLRequest(url: url)
request.httpMethod = "POST"
let task = self.session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
if (error != nil) {
self.error = NSError.xionError(.ConnectionError, underlying: error!)
}
let task = self.session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in
self.error = error
semaphore.signal()
}
})
task.resume()
semaphore.wait()
}
@@ -151,24 +155,24 @@ internal class FetchDevicesOperation : WemoOperation
override func main()
{
let semaphore = Semaphore(value: 0)
let url = self.baseURL.URLByAppendingPathComponent("api/environment")
let task = self.session.dataTaskWithURL(url) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
let url = self.baseURL.appendingPathComponent("api/environment")
let task = self.session.dataTask(with: url, completionHandler: { (data: Data?, response: URLResponse?, error: NSError?) -> Void in
if (data != nil) {
self.devices = self._parseDevices(data!)
} else {
self.error = NSError.xionError(.ConnectionError, underlying: error)
self.error = NSError.xionError(.connectionError, underlying: error)
}
semaphore.signal()
}
} as! (Data?, URLResponse?, Error?) -> Void)
task.resume()
semaphore.wait()
}
internal func _parseDevices(data: NSData) -> [WemoDevice]
internal func _parseDevices(_ data: Data) -> [WemoDevice]
{
var devices: [WemoDevice] = []
if let responseDict = (try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as? NSDictionary) {
if let responseDict = (try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as? NSDictionary) {
for responseObj in (responseDict?.allValues)! {
if let responseDict = responseObj as? NSDictionary {
let device = WemoDevice(responseDict)
@@ -186,7 +190,7 @@ internal class ToggleDeviceOperation : WemoOperation
var device: WemoDevice
var state: WemoDevice.State
init(baseURL: NSURL, session: NSURLSession, device: WemoDevice, state: WemoDevice.State)
init(baseURL: URL, session: URLSession, device: WemoDevice, state: WemoDevice.State)
{
self.device = device
self.state = state
@@ -196,15 +200,13 @@ internal class ToggleDeviceOperation : WemoOperation
override func main()
{
let semaphore = Semaphore(value: 0)
let stateArg = (self.state == .On ? "on" : "off")
let url = self.baseURL.URLByAppendingPathComponent("api/device/\(self.device.name)").URLByAppendingRequestParameters(["state" : stateArg])
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
let stateArg = (self.state == .on ? "on" : "off")
let url = self.baseURL.appendingPathComponent("api/device/\(self.device.name)").URLByAppendingRequestParameters(["state" : stateArg])
var request = URLRequest(url: url!)
request.httpMethod = "POST"
let task = self.session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
if (error != nil) {
self.error = NSError.xionError(.ConnectionError, underlying: error)
}
let task = self.session.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
self.error = error
semaphore.signal()
}
task.resume()