Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Painless Migration to Swift 1.2: Sets and Touches and Rock and Roll

DZone's Guide to

Painless Migration to Swift 1.2: Sets and Touches and Rock and Roll

· Java Zone
Free Resource

Learn how to troubleshoot and diagnose some of the most common performance issues in Java today. Brought to you in partnership with AppDynamics.

My initial tests with the early Xcode 6.3 betas were a bit worrying: I ended up with countless compiler errors that couldn’t be easily resolved, the scariest being related to an apparent change in the precedence of the ternary ‘?’ operator. However, the final release has made migrating my existing Swift projects to the 1.2 syntax an absolute breeze. Pretty much every change has been related to the new as! casting syntax, but there have been other issues that couldn’t be automatically resolved.
This post is a a brief summary of those issues and the fixes. It certainly isn’t exhaustive of every issue, but I thought I’d publish the list in the hope it may assist others if they experience problems migrating their code.
Almost all of my projects have now been successfully migrated to Swift 1.2 and they’re all available in  my GitHub repository here.
Sets and Touches
Overridden touchesBegan, touchesMoved and touchesEnded requires that touches is typed as Set (wasNSSet):
    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)

This argument can then be cast as a set of UITouch using optional binding:

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
    {
        if let touches = touches as? Set<UITouch>
        {
            // touches is Set
        }
    }

It’s worth noting that Swift sets don’t allow access to items via a subscript whereas NSSet does. So, pre-1.2 code that looked like this:

    let touch: UITouch = touches.allObjects[0] as UITouch

…now needs to access the first item:

    let touch: UITouch = touches.first as! UITouch // assuming touches is still Set

…or, if you need to access each touch via subscripts, you can cast the set to an array:

    let touchesAsArray = Array(touches)

…or, if you're feeling particularly extravagent, you could even write an extension to Set to support subscripting:

    extension Set
    {
        subscript(i: Int) -> Element
        {
            return Array(self)[i]
        }
    }

The use of Swift set for touches makes managing the current touches elsewhere in code quite easy. Consider my GPU particles demonstration, where I want to interrogate the current touches in a step() method which is outside of theUIResponder functions. I define a variable, currentTouches, which is a set of UITouch:

    var currentTouches = Set<UITouch>()

…and then in touchesBegan and touchesEnded I add to and remove from currentTouches using the union andsubtract methods:

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
    {
        if let touches = touches as? Set<UITouch>
        {
            currentTouches = currentTouches.union(touches)
        }
    }

    override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent)
    {
        if let touches = touches as? Set<UITouch>
        {
            currentTouches = currentTouches.subtract(touches)
        }
    }
You can read more about sets in Swift 1.2 in my  Let's Talk About Sets post from earlier this year.
Because  UITouch is a class rather than a struct and, therefore, passed by reference, I don’t need to update my array intouchesMoved:  currentTouches always contains the latest locations and this code, invoked at every update of my particle system, successfully positions gravity wells based on touch points:
        let currentTouchesArray = Array(currentTouches)
        
        for var i:Int = 0; i < currentTouchesArray.count; i++
        {
            particleLab.setGravityWellProperties(gravityWellIndex: i,
                normalisedPositionX: Float(currentTouchesArray[i].locationInView(view).x / view.frame.width) ,
                normalisedPositionY: Float(currentTouchesArray[i].locationInView(view).y / view.frame.height),
                mass: 10,
                spin: Float(currentTouchesArray[i].majorRadius / 5))
        }
Miscellaneous Migration Issues
NSPersistentStoreCoordinator addPersistentStoreWithType() method requires that the optionsargument is explicitly cast as [NSObject : AnyObject]:
    if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options as [NSObject : AnyObject], error: &error)

NSError constructor requires that the userInfo argument is explicitly cast as [NSObject : AnyObject]:

    error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict as [NSObject : AnyObject])

Misspelt imports cause compiler error:

    import UIKIt // fails
    import UIKit // compiles
The CGImageCreate() method’s arguments are now all Int rather than UInt.
The posix_memalign() method’s arguments are now all Int rather than UInt.
Using NSString to format a string and concatenating with a string literal requires that the NSString is explicitly case as a String.
    println("Fluid Solve time:" + (NSString(format: "%.4f", CFAbsoluteTimeGetCurrent() - startTime) as String));

Finally, the the rock and roll comes in! Migrating to Swift 1.2 gives you the much nicer optional binding syntax, clearer casting with as! and smaller, faster binaries. 


Understand the needs and benefits around implementing the right monitoring solution for a growing containerized market. Brought to you in partnership with AppDynamics.

Topics:

Published at DZone with permission of Simon Gladman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}