Nick Harris

Twitter

leave a comment »

Brent Simmons – Not Back on Twitter:

When the new Star Wars movie came out, I decided to take a break from Twitter so I could avoid spoilers. I mostly kept away (but for a few small hits).

I finally saw the movie (which I enjoyed) this past weekend, and so I came back to Twitter.

Now, a few hours later, I’m off Twitter again. I didn’t like being back.

Twitter became a daily part of my routine as soon as I joined. I was still working on NewsGator Inbox (which was an RSS Outlook add-in) and had a few conversations with another developer who built a Twitter Outlook add-in. I had every blog post and Twitter post all in Outlook. It was pretty seamless and I enjoyed it.

While we were building and using Glassboard extensively a few years ago, I rarely opened Twitter. If we were doing a PR push or a conference was coming up I’d be on, but for the most part I ignored it. I started checking in daily again when I wrote a book to help publicize it and answer any questions that would come up.

Over the last year though, I’ve seen my usage increase while the benefits have decreased.

I found myself evaluating my professional worth based on who and how many people followed me. All the while knowing that some of the best developers I’ve ever worked with either don’t have accounts or rarely use them.

I also found it a way to easily stick my foot in my mouth and say something that I regretted. Basically forgetting this classic xkcd comic about the internet.

I knew Brent’s tweet in December was more then it seemed (work with a guy for 8 years and you start to read things). The post today summed up many of the same thoughts I’ve had and why I also took a break from Twitter in December. After a quick week back on Twitter after the New Year, I took it a step further and deactivated my account two weeks ago.

Brent talks about being calmer and happier. I was too. He also talks about the trade-off of not having a Twitter account. Its very close to the conclusion I had last week when I re-activated mine. I’m guessing its not a coincidence that Manton Reece wrote about the same trade-off today as well. (when Brent wrote about Manton being a litter-mate the comment was to me and some mutual friends while I was in Seattle)

I’m not sure yet just how far back I’ll be cutting down on Twitter but I do plan to publish links to blog posts or interesting things I find while coding. That part of Twitter has been very valuable to me.

But largely ignoring the Twitter Noise Machine – particularly when my timeline becomes the Twitter Hate Machine – is going to be good for me.

I plan on using that time well this year and for years to come.

Written by Nick Harris

January 20, 2016 at 5:58 am

Posted in Uncategorized

NFL vs the NHL – What the NFL Should Learn

leave a comment »

This article sums up some of the conversations I’ve had this past NFL Wild Card Weekend with friends.

One was about the NHL and fighting.

I was watching the 2004 Avalanche / Canucks game live when Todd Bertuzzi sucker punched Steve Moore and basically ended his professional career. Part of the reason I was watching the game is that it was hyped as a revenge game for a hit Moore had against a star on the Canucks a few weeks earlier. It ended in criminal charges against Bertuzzi after one of the ugliest sports play I’ve ever seen.

Am I saying local authorities should start investigating dirty plays in the NFL?

No.

But I do think the NFL should come down heavy on both teams. And by that I mean Pittsburgh losing coaches for the remainder of the playoffs (Shazier should probably be suspended for the next game too) and Burfict (Bengals player at least for now) for a quarter of the 2016 season.

I completely despise fighting in NHL games anymore. It has no place. The game is great enough with the shear athleticism of those who play it.

I was 8th row for the Avs game Friday. It was more or less a playoff game in that a win put the Avs in a tie for the last playoff spot with a tie breaker over Nashville. Sure, there are 40 more games and months of regular season games ahead, but for a team seemingly out of playoff talk just weeks ago it was huge.

At one point Nashville put in an “enforcer” who tried to pick at fight with one of the Avs first linemen. The Avs player literally laughed in his face and the game continued.

That’s why there’s no fighting in the playoffs or big games. Its only for in-season fans and ticket sales but I wish it was gone completely. Its been on the downturn for years now.

What the NFL allowed Saturday night both in the lead up to, and the execution of that game is a great example of why they should follow the NHL. Send a strong message to everyone that games like that will not be tolerated. And also quit publicizing games in the way that game was sold to fans.

Another friend shared this article. It reminded me that the first person to try and shake Tom Brady’s hand in last years Super Bowl was Richard Sherman.

You can find Sherman’s mouth and competitiveness annoying but he’s the kind of athlete I want to support.

*** I’m a Packers stock owner – GO PACK GO!!!

**** I’m also an Avs fan – Whoop Whoop!!

 IMG 1993  1

Written by Nick Harris

January 11, 2016 at 5:13 am

Posted in Uncategorized

Swift + UIPickerView + Enums

leave a comment »

I needed a way to power a UIPickerView with an ordered set of options that had different selection values. Its a pretty common thing in HTML and I’ve done it before in Objective-C but I didn’t see any great examples of how to do it in Swift. I really like how this works and shared it on Twitter but thought it deserved a blog post as well.

Feel free to ask me about it. Or if you have suggestions to improve it.

 

enum RecurrenceOptions: Int, CustomStringConvertible {
    case Unknown = 0
    case Daily = 1
    case Weekly = 2
    case Monthly = 3
    case Bimonthly = 4
    case Quarterly = 5
    case Yearly = 6
    case Biannual = 7
    case Biennial = 8
    
    static var count: Int { return RecurrenceOptions.Biennial.rawValue + 1 }
    
    var description: String {
        switch self {
        case .Unknown: return ""
        case .Daily: return "Every Day"
        case .Weekly   : return "Weekly"
        case .Monthly  : return "Every Month"
        case .Bimonthly : return "Twice a Month"
        case .Quarterly : return "Every Three Months"
        case .Yearly : return "Every Year"
        case .Biannual : return "Twice a Year"
        case .Biennial : return "Every Other Year"
        }
    }
    
    var value: Int {
        switch self {
        case .Unknown: return 0
        case .Daily: return 1
        case .Weekly   : return 7
        case .Monthly  : return 30
        case .Bimonthly : return 60
        case .Quarterly : return 90
        case .Yearly : return 360
        case .Biannual : return 180
        case .Biennial : return 720
        }
    }
    
    init?(value: Int) {
        switch value {
        case 0 : self = .Unknown
        case 1 : self = .Daily
        case 7 : self = .Weekly
        case 30 : self = .Monthly
        case 60 : self = .Bimonthly
        case 90 : self = .Quarterly
        case 360 : self = .Yearly
        case 180 : self = .Biannual
        case 720 : self = .Biennial
        default : return nil
        }
    }
}
 
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
 
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        
        recurrenceLabel.text = ""
        if let recurrence = myCoreDataObject?.recurrance,
           let recurrenceOption = RecurrenceOptions.init(value: recurrence.integerValue) {
            recurrenceLabel.text = recurrenceOption.description
            recurrencePickerView.selectRow(recurrenceOption.rawValue, inComponent: 0, animated: false)
        }
    }
 
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return RecurrenceOptions.count
    }
    
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        guard let recurrenceOption = RecurrenceOptions(rawValue: row) else {
            fatalError("Unknown RecurrenceOption")
        }
        
        return choreRecurrenceOption.description
    }
    
    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        guard let recurrenceOption = RecurrenceOptions(rawValue: row) else {
            fatalError(“Unknown RecurrenceOption")
        }
        
        print(“RecurrenceOption.description: \(recurrenceOption.description) | RecurrenceOption.value: \(recurrenceOption.value)")
    }
}

Written by Nick Harris

December 7, 2015 at 9:08 pm

Posted in Uncategorized

Bitcoin Exchange Rates – A Swift 2.0 Sample App

leave a comment »

Introduction

A few days ago I read this post about Massive View Controllers by Marcus Zarra. Marcus and I have been friends for years. I’ve probably learned more about iOS development from him and my friend and old co-worker Brent then anyone else.

The post talks about using NSOperation subclasses for networking. This struck a chord with me since I love NSOperation for networking myself, particularly after spending the last 1.5 years working on a project that used a facade pattern approach. I believe that pattern is popular in Android but I had never used it in iOS and wasn’t particularly fond of it.

I decided I would write a quick blog post on how to use NSOperation to call a web service in Swift. As I got started I realized that another pattern that I had learned and used quite successfully in past projects from Marcus around Core Data (updated and described nicely in this post) was also missing in my last project. Instead it used a third party framework that seemed to cause more confusion and crashes then the simple approaches I had used successfully in the past.

So instead of just a blog post, I ended up writing a sample app to teach myself more Swift 2.0 that combines both the NSOperation networking pattern and Core Data stack in Marcus’s posts. 

Bitcoin Exchange Rate App

Bitcoin Exchange Rate is a very simple app that fetches the current exchange rate for Bitcoin in Dollars, Pounds and Euros then displays them. Its powered by an open web service from CoinDesk. Its a simple idea that demonstrates not only NSOperation and Core Data but also Storyboards, Size Classes and Adaptive Layout that works on all iOS 8 and up devices including multitasking on iPad in iOS 9. It’s written completely in Swift 2.0.

The whole project is less then 600 lines of code.

You can find the repository here on BitBucket.

DISCLAIMER
I am in no way an expert on any of the topics this demo app uses nor is this demo app polished. The code and this post is meant to show how I approached the app and implemented it’s core functionality while learning Swift 2.0. I’m sure there are mistakes since I’ve been coding in Swift for all of one month now. I’d love to hear any feedback either in the comments, on twitter or if you want you can email me.

Web Service 

In order to demonstrate a web service driven app I obviously needed a web service to connect to. I’ve been a bit fascinated with Bitcoin lately and after a quick search I found a nice web service API from CoinDesk. It uses HTTPS so I can use it out of the box with iOS 9 and the JSON is fairly simple:

{

bpi:
{
EUR:
{
code:
EUR,
description:
Euro,
rate:
208.2519,
rate_float:
208.2519,
symbol:
€

},

GBP:
{
code:
GBP,
description:
British Pound Sterling,
rate:
153.4957,
rate_float:
153.4957,
symbol:
£

},

USD:
{
code:
USD,
description:
United States Dollar,
rate:
233.2992,
rate_float:
233.2992,
symbol:
$

}

},

disclaimer:
This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org,
time:
{
updated:
Sep 28, 2015 01:23:00 UTC,
updatedISO:
2015-09-28T01:23:00+00:00,
updateduk:
Sep 28, 2015 at 02:23 BST

}

}

Adaptive Layout

I tend to be a “paint the canvas” type of developer before diving into the data layer of an app. There’s no point in parsing and saving data that’s never used. With the response from my web service in hand, I created my one scene in my main storyboard using wAny hAny:

Screen Shot 2015 09 27 at 7 39 00 PM

Its probably easier to pick through the view by downloading the project yourself, but here’s a quick overview:

– a UIView anchored to the top that has the headline, disclaimer text and last updated label
– a UITableView with a prototype cell that lists the name of the currency, its symbol and current bitcoin exchange rate
– a UIToolbar with a single “Refresh” UIBarButtonItem

One of the tricks I’ve learned with Adaptive Layout is setting font sizes big then using autoshrink with either a minimum font size or minimum font scale. For the large “Bitcoin Exchange Rates” header I went with minimum font size of 12. 

Screen Shot 2015 09 27 at 7 53 26 PM

I’ll fully admit that the simple layout of this app is not very eye pleasing but it did make getting it usable on any iOS 8 supported iPhone or iPad incredibly easy. I didn’t have to change anything for full screen iPad support though I probably could have. For iPad multitasking all I needed to do was set a smaller font for the disclaimer text on the wCompact hRegular size class:

Screen Shot 2015 09 27 at 8 09 57 PM

As you can see in the previous screen shot, I also needed to adjust the font for the wCompact hCompact size class which is used for iPhone 6/6S and under in landscape (iPhone 6/6S+ uses wCompact hRegular but I didn’t optimize the UI for those devices). I also changed the layout so that the header view is off to the side while the table view takes up the rest of the screen:

Screen Shot 2015 09 27 at 8 14 22 PM

One other simple trick I used was two views as a divider line for either iPhone in landscape on the side or all other orientations at the bottom of the header view. For the wCompact hCompact size class I set the vertical separator lines width to 1 and the horizontal lines height to 0. For all other size classes those values are swapped. Pretty nifty that those constrains can be set per size class in IB.

Preview mode for different device sizes and orientations is incredibly handy when working on your UI:

Screen Shot 2015 09 27 at 8 22 13 PM

That was pretty much it for Adaptive Layout. It took me a few hours to get right but the result is a pretty simple storyboard with only a few size class differences. I particularly like how easy this makes multitasking!

Screen Shot 2015 09 27 at 8 31 42 PM

Screen Shot 2015 09 27 at 8 31 59 PM

(A major oversight I realized while proofreading is that this does not support Dynamic Type at all – perhaps in a future update)

Core Data and the DataController

I am one of the weird iOS devs that likes Core Data. If you’re doing consulting work I believe it makes sense to know and use Core Data instead of something like Realm (though I do like Realm too). The reason is that Core Data is an Apple technology stack so you’re pretty much guaranteed that it will be updated and/or deprecated as time moves on and needs of the system change. There are third party frameworks you can use with Core Data but I believe its best to write the code yourself. Its not that difficult to implement.

I recommend starting with Marcus’s post I mentioned in the introduction

In my past projects I’ve used the boilerplate Core Data code Xcode will throw into your AppDelegate. I’ve also partitioned a good portion of the actual working code into its own singleton manager class. It worked well but the idea of using dependency injection instead of a singleton looked better to me. I have another side project app that uses the DataController approach with a tabbed layout that injects the DataController into each. It was easy so I went with that approach again.

The complete Swift code for my DataController:

import CoreData
 
typealias networkOperationResult = (success: Bool, errorMessage: String?) -> Void
 
enum DataControllerUserDefaultKeys: String {
    case lastUpdatedKey = "BitcoinExchangeLastUpdatedKey"
}
 
class DataController {
    
    var managedObjectContext: NSManagedObjectContext
    internal var privateObjectContext: NSManagedObjectContext
    
    let networkQueue = NSOperationQueue()
    
    init(closure:()->()) {
        
        guard let modelURL = NSBundle.mainBundle().URLForResource("BitcoinExchangeRateDataModel", withExtension: "momd"),
              let managedObjectModel = NSManagedObjectModel.init(contentsOfURL: modelURL)
        else {
            fatalError("DataController - COULD NOT INIT MANAGED OBJECT MODEL")
        }
        
        let coordinator = NSPersistentStoreCoordinator.init(managedObjectModel: managedObjectModel)
            
        self.managedObjectContext = NSManagedObjectContext.init(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType)
        self.privateObjectContext = NSManagedObjectContext.init(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
            
        self.privateObjectContext.persistentStoreCoordinator = coordinator
        self.managedObjectContext.parentContext = self.privateObjectContext
        
        dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.rawValue), 0)) {
            
            let options = [
                NSMigratePersistentStoresAutomaticallyOption: true,
                NSInferMappingModelAutomaticallyOption: true,
                NSSQLitePragmasOption: ["journal_mode": "DELETE"]
            ]
            
            let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last
            let storeURL = NSURL.init(string: "bitcoinExchangeRate.sqlite", relativeToURL: documentsURL)
            
            do {
                try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options)
                
                dispatch_async(dispatch_get_main_queue()) {
                    closure()
                }
            }
            catch let error as NSError {
                fatalError("DataController - COULD NOT INIT SQLITE STORE: \(error.localizedDescription)")
            }
        }
    }
    
    func save() {
        
        if self.privateObjectContext.hasChanges || self.managedObjectContext.hasChanges {
            
            self.managedObjectContext.performBlockAndWait {
                
                do {
                    try self.managedObjectContext.save()
                                        
                    self.privateObjectContext.performBlock {
                        
                        do {
                            try self.privateObjectContext.save()
                        }
                        catch let error as NSError {
                            fatalError("DataController - SAVE PRIVATEOBJECTCONTEXT FAILED: \(error.localizedDescription)")
                        }
                    }
                }
                catch let error as NSError {
                    fatalError("DataController - SAVE MANAGEDOBJECTCONTEXT FAILED: \(error.localizedDescription)")
                }
            }
        }
    }
    
    func fetchExchangeRateData(completion: networkOperationResult) {
        
        let fetchExchangeRateDataOperation = FetchExchangeRateDataOperation(dataController: self, completion: completion)
        self.networkQueue.addOperation(fetchExchangeRateDataOperation)
    }
}

The class differs slightly from Marcus’s post in that it does not inherit from NSObject. I didn’t see the need for anything in NSObject so I didn’t use it. Perhaps I’m missing something lower level but this worked for me.

The init is the workhorse. It takes a closure as a parameter that gets called after the boilerplate Core Data initialization happens. I think Marcus covers this well so I won’t dwell too much on its implementation other then to note the use of fatalError instead of NSAssert. I also like the use of guard to make sure the model exists in the app bundle.

The reason for the closure after a successful init is to inject the DataController into the apps view controller(s) from the AppDelegate:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
 
    var window: UIWindow?
    var dataController: DataController?
 
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
        self.dataController = DataController.init() {
            
            if let bitcoinExchangeRateViewController = self.window?.rootViewController as? BitcoinExchangeRateViewController {
                bitcoinExchangeRateViewController.configureWithDataController(self.dataController!)
            }
        }
        
        return true
    }
}

My BitcoinExchangeRateViewController can then set itself up knowing full well that the Core Data layer is initiated and ready to use. I’ll show that in the Updating the UI section below.

Fetching Data with NSOperation

 In the code for my DataController you’ll see the fetchExchangeRateData function:

func fetchExchangeRateData(completion: networkOperationResult) {
        
    let fetchExchangeRateDataOperation = FetchExchangeRateDataOperation(dataController: self, completion: completion)
    self.networkQueue.addOperation(fetchExchangeRateDataOperation)
}

FetchExchangeRateDataOperation is my NSOperation subclass that could easily be initiated by the view controller that wants the data instead of within the DataController. I’ve done that in the past and used execute to start the operation. What I like about this approach though is that the DataController owns the NSOperationQueue for working background networking tasks. It also means that my NSOperation is going to be called on a background thread instead of the main thread. I get both background threading and a simpler dependency graph in a few easy to read lines of code.

Next lets take a look at the NSOperation subclass itself:

import UIKit
 
class FetchExchangeRateDataOperation: NSOperation {
    
    let currencyDataUrlString = "https://api.coindesk.com/v1/bpi/currentprice.json"
    var dataController: DataController
    var completion: networkOperationResult
    
    init(dataController: DataController, completion: networkOperationResult) {
        self.dataController = dataController
        self.completion = completion
        super.init()
    }
    
    override func start() {
        
        if let currencyExchangeRateURL = NSURL(string: currencyDataUrlString) {
            
            let session = NSURLSession.sharedSession()
            let request = NSMutableURLRequest(URL: currencyExchangeRateURL)
         
            let task = session.dataTaskWithRequest(request, completionHandler: {
                [unowned self]
                data, response, error -> Void in
                
                if let safeError = error {
                    self.completion(success: false, errorMessage: safeError.localizedDescription)
                    return
                }
                
                guard let safeData = data else {
                    self.completion(success: false, errorMessage: "No data was recieved")
                    return
                }
                
                do {
                    
                    if let json = try NSJSONSerialization.JSONObjectWithData(safeData, options: .MutableLeaves) as? NSDictionary
                    {
                        let bitcoinExchangeRateJSON = try BitcoinCurrentPriceJSON(json: json)
                        
                        NSUserDefaults.standardUserDefaults().setObject(bitcoinExchangeRateJSON.lastUpdate, forKey: DataControllerUserDefaultKeys.lastUpdatedKey.rawValue)
                        
                        for currencyExchangeData in bitcoinExchangeRateJSON.currencies! {
                            ExchangeCurrency.saveCurrencyExchangeData(currencyExchangeData, managedObjectContext: self.dataController.managedObjectContext)
                        }
                        
                        self.dataController.save()
                        self.completion(success: true, errorMessage: nil)
                    }
                    
                    
                }
                catch JSONParseError.MissingRequiredField(let fieldName) {
                    self.completion(success: false, errorMessage: "Response is missing an expected field: \(fieldName)")
                }
                catch let error as NSError {
                    self.completion(success: false, errorMessage: "Parsing the response failed: \(error.localizedDescription)")
                }
                
            })
            
            task.resume()
        }
    }
}

I’ve become a big fan of NSURLSession and its counterparts. Networking back in the bad old days prior to its introduction were ugly and very error prone. My apps powered through it but I’ll admit that the first time I saw AFNetworking in action I couldn’t wait to use it. In fact I use to tout it as the one third party framework I would never argue against using. I don’t feel that way anymore with AlamoFire. The tools we have in the foundation are more then enough for most developers now a day.

Side Track – Third Party Code
You may have caught on in this post or knowing me that I’m not a fan of third party code. That doesn’t mean I’m against open source code. I think sharing code openly is amazing. You can learn so much from reading other code and seeing how problems were approached. I just caution anyone depending on third party code to know what you’re “buying” in the trade-off for not writing it yourself. Check out this post and think about it when debating third party frameworks. 

Lets look at what this code does. First it makes sure the system can convert the web service URL string to an actual NSURL used by NSURLSession:

if let currencyExchangeRateURL = NSURL(string: currencyDataUrlString)

Next it gets the default NSURLSession and creates an NSURLRequest

let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: currencyExchangeRateURL)

The creation of the data task is next.

let task = session.dataTaskWithRequest(request, completionHandler: {...

By using dataTaskWithResult we know we’re making a GET HTTP call. The rest is just a completion handler which gets called after the system makes the networking call for us.

It has a signature that returns the data, the NSURLResponse object if we want it, along with any generated NSError’s. You might also notice that it declares self in the closure as unowned. I’ll be honest in that I don’t have the best knowledge of how ARC, weak, strong and unowned work in Swift yet. I can tell you that this code doesn’t crash and has no memory leaks.

The closure first looks for any reported errors. If there is an error it calls the supplied closure with success: false and the errorMessage set to the NSError.localizedDescription.

if let safeError = error {
    self.completion(success: false, errorMessage: safeError.localizedDescription)
    return
}

Next it uses a guard statement to make sure there is actual data to parse:

guard let safeData = data else {
    self.completion(success: false, errorMessage: "No data was recieved")
    return
}

Guard vs. let if has been my biggest struggle with Swift so far. I think they both have their place but I wonder if I’m utilizing them to their maximum potential.

The rest of the closure uses a do/catch statement.

Long, long ago I wrote about exceptions in Objective-C vs what I was doing with platform code I was writing for Glassboard in C#. I even had a special explanation in my book about how exceptions and error handling in Objective-C were completely different then any other language. Swift 2.0 makes this so much better.

The first try attempts to parse the NSData into a generic JSON object using NSJSONSerialization.JSONObjectWithData. If that succeeds it then uses my own BitcoinCurrentPriceRateJSON struct to parse it down into usable objects. Either of these calls can throw exceptions which can then be properly assessed and passed back up to the UI layer using the networkOperationResult closure. If all goes well then everything gets parsed and saved to Core Data which in turn lets the UI know there’s new data to display.

The reason I really like do/catch is that is much more in line with languages I’ve used in the past. Objective-C and the very clear mandate to never use NSException in the iOS developer community was very strong. But it made handling error conditions in code clunky. Swift and all its type safety and error security is much different. I believe it will make creating apps that handle unexpected situations much easier then in Objective-C.

Parsing the JSON

JSON parsing in Swift is odd. JSON is not strongly typed by definition. Swift however is. This makes for a bit of a stand off.

When I first got to the part of parsing the JSON from the CoinDesk web service I initially threw my hands in the air and decided that Swift sucks for JSON while simultaneously searching for a third party framework to do the hard work for me. After about 2 hours though I gave up on any third party code yet again (see my note above) and settled into writing my own parser. 

I did like what I saw in SwiftyJSON and even Argo. But the idea of adding more lines of code to my project through a third party framework then actual lines of JSON I needed to parse seemed up-side-down. How hard could a Swift JSON parser be when I know exactly what the JSON looks like.

Turns out its not hard at all:

import UIKit
 
enum JSONParseError: ErrorType {
    case MissingRequiredField(fieldName: String)
}
 
struct CurrencyExchangeData {
    var code: String?
    var name: String?
    var rate: String?
    var symbol: String?
}
 
struct BitcoinCurrentPriceJSON {
    
    enum BitcoinCurrentPriceJSONKeys: String {
        case time = "time"
        case updated = "updated"
        case bpi = "bpi"
        case code = "code"
        case description = "description"
        case rate = "rate"
        case symbol = "symbol"
    }
    
    var lastUpdate: String?
    var currencies: [CurrencyExchangeData]?
    
    init(json: NSDictionary) throws {
        self.currencies = []
        try parseExchangeRateJSON(json)
    }
    
    internal mutating func parseExchangeRateJSON(json: NSDictionary) throws -> BitcoinCurrentPriceJSON {
        
        guard let timeDictionary = json[BitcoinCurrentPriceJSONKeys.time.rawValue] as? NSDictionary,
              let timeStamp = timeDictionary[BitcoinCurrentPriceJSONKeys.updated.rawValue] as? String else
        {
            throw JSONParseError.MissingRequiredField(fieldName: BitcoinCurrentPriceJSONKeys.updated.rawValue)
        }
        
        self.lastUpdate = timeStamp
        
        guard let bpiDictionary = json[BitcoinCurrentPriceJSONKeys.bpi.rawValue] as? NSDictionary,
              let currencies = bpiDictionary.allValues as? [NSDictionary] else
        {
            throw JSONParseError.MissingRequiredField(fieldName: BitcoinCurrentPriceJSONKeys.bpi.rawValue)
        }
        
        for currencyDictionary in currencies {
            let currencyExchangeData = try parseCurrencyDictionary(currencyDictionary as NSDictionary)
            self.currencies?.append(currencyExchangeData)
        }
        
        return self
    }
    
    internal func parseCurrencyDictionary(currencyDictionary: NSDictionary) throws -> CurrencyExchangeData {
        
        guard let currencyCode = currencyDictionary[BitcoinCurrentPriceJSONKeys.code.rawValue] as? String else
        {
            throw JSONParseError.MissingRequiredField(fieldName: BitcoinCurrentPriceJSONKeys.code.rawValue)
        }
        
        guard let currencyDescription = currencyDictionary[BitcoinCurrentPriceJSONKeys.description.rawValue] as? String else
        {
            throw JSONParseError.MissingRequiredField(fieldName: BitcoinCurrentPriceJSONKeys.description.rawValue)
        }
        
        guard let currencyRate = currencyDictionary[BitcoinCurrentPriceJSONKeys.rate.rawValue] as? String else
        {
            throw JSONParseError.MissingRequiredField(fieldName: BitcoinCurrentPriceJSONKeys.rate.rawValue)
        }
        
        guard let currencySymbol = currencyDictionary[BitcoinCurrentPriceJSONKeys.symbol.rawValue] as? String else
        {
            throw JSONParseError.MissingRequiredField(fieldName: BitcoinCurrentPriceJSONKeys.symbol.rawValue)
        }
        
        var currencyExchangeData = CurrencyExchangeData()
        currencyExchangeData.code = currencyCode
        currencyExchangeData.name = currencyDescription
        currencyExchangeData.rate = currencyRate
        currencyExchangeData.symbol = decodeSymbol(currencySymbol)
        
        return currencyExchangeData
    }
    
    internal func decodeSymbol(encodedSymbolString: String) -> String {
        let encodedData = encodedSymbolString.dataUsingEncoding(NSUTF8StringEncoding)!
        let attributedOptions : [String: AnyObject] = [
            NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
            NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding
        ]
        
        do {
            let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
            return attributedString.string
            
        }
        catch {
            return ""
        }
    }
}

What I like about this code is that its not only strongly typed but its also strongly structured. I love that JSON is open type-wise, but when it comes down to consuming data offered in JSON you kind of need to know what you’re getting and how to get to it. The third party frameworks mentioned earlier can do this for you but again at what cost? If my JSON response changes, I can be pretty certain my data layer needs updating as well.

This code also gave me the chance to group the JSON keys into their own String enumeration and use rawValue in the code. I ended up making that change after fixing a misspelling in one of the throws but not in the guard statement its self. Its the old problem of magical strings but solved in a Swift-type way.

One last note on this code – the decodeSymbol function could easily have been written as an extension to String. Perhaps I’ll do that in a future update.

Updating the UI

After my NSOperation parses the JSON into a BitcoinCurrentPriceJSON object it uses that to parse and save the information to Core Data. There’s only one Core Data NSManagedObject in the project called ExchangeCurrency. Here’s the ExchangeCurrency+CoreDataProperties:

import Foundation
import CoreData
 
extensionExchangeCurrency {
 
    @NSManaged var code: String?
    @NSManaged var name: String?
    @NSManaged var rate: String?
    @NSManaged var symbol: String?
 
}

I then added two functions to the ExchangeCurrency.swift file. In the past I would have done this using Mogenerator but you can do this now in Xcode without any helpers. I think this code is fairly straight forward. The internal function gets the correct object from the context if it can find one or it creates a new one. The public function uses the found or created object and sets the correct properties.

import Foundation
import CoreData
 
@objc(ExchangeCurrency)
 
class ExchangeCurrency: NSManagedObject {
 
    class func saveCurrencyExchangeData(currencyExchangeData: CurrencyExchangeData, managedObjectContext: NSManagedObjectContext) {
        
        guard let exchangeCurrency = getExchangeCurrencyObject(currencyExchangeData.code!, managedObjectContext: managedObjectContext) else {
            fatalError("ExchangeCurrency.saveCurrencyExchangeData - NO EXCHANGECURRENCY OBJECT")
        }
        
        exchangeCurrency.code = currencyExchangeData.code
        exchangeCurrency.name = currencyExchangeData.name
        exchangeCurrency.rate = currencyExchangeData.rate
        exchangeCurrency.symbol = currencyExchangeData.symbol
    }
 
    internal class func getExchangeCurrencyObject(code: String, managedObjectContext: NSManagedObjectContext) -> ExchangeCurrency? {
        
        let fetchRequest = NSFetchRequest(entityName: "ExchangeCurrency")
        fetchRequest.predicate = NSPredicate(format: "code = %@", code)
        
        do {
            guard let fetchResults = try managedObjectContext.executeFetchRequest(fetchRequest) as? [ExchangeCurrency] else {
                fatalError("ExchangeCurrency.getExchangeCurrencyObject - NO FETCH RESULTS")
            }
            
            if fetchResults.count == 1 {
                return fetchResults.last
            }
            else if fetchResults.count == 0 {
                
                guard let exchangeCurrency = NSEntityDescription.insertNewObjectForEntityForName("ExchangeCurrency", inManagedObjectContext: managedObjectContext) as? ExchangeCurrency else {
                    fatalError("ExchangeCurrency.getExchangeCurrencyObject - COULD NOT CREATE EXCHANGECURRENCY OBJECT")
                }
                
                return exchangeCurrency
            }
            else {
                fatalError("ExchangeCurrency.getExchangeCurrencyObject - MULTIPLE FETCH RESULTS FOR CODE")
            }
        }
        catch let error as NSError {
            fatalError("ExchangeCurrency.getExchangeCurrencyObject - ERROR FETCHING FROM CORE DATA: \(error.localizedDescription)")
        }
        
        return nil
    }
}

Once all the data is parsed into NSManagaedObjectContext the FetchExchangeRateDataOperation calls save on the DataController. It should be noted that I’m doing the Core Data work on the context meant for the main thread. In this small of an app it works fine. If I were doing something more involved I would probably have created a child context from the main thread context and used that in the FetchExchangeRateDataOperation. There really isn’t much difference code wise in the NSOperation. The only big difference would be in the DataController and how the save merges things back into the main thread context.

Finally lets take a look at the BitcoinExchangeRateViewController:

import UIKit
import CoreData
 
class BitcoinExchangeRateViewController: UIViewController, UITableViewDataSource, NSFetchedResultsControllerDelegate {
    
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var lastUpdatedLabel: UILabel!
    @IBOutlet weak var refreshButton: UIBarButtonItem!
    var dataController: DataController?
    var fetchedResultsController: NSFetchedResultsController?
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        
        setLastUpdatedLabel()
    }
    
    @IBAction func updateExchangeRateData() {
        fetchExchangeRateData()
    }
    
    func configureWithDataController(dataController: DataController) {
        
        self.dataController = dataController
        
        let currencyFetchRequest = NSFetchRequest(entityName: "ExchangeCurrency")
        let sortDescriptor = NSSortDescriptor(key: "code", ascending: false)
        
        currencyFetchRequest.sortDescriptors = [sortDescriptor]
        
        if let managedObjectContext = self.dataController?.managedObjectContext {
            self.fetchedResultsController = NSFetchedResultsController(
                fetchRequest: currencyFetchRequest,
                managedObjectContext: managedObjectContext,
                sectionNameKeyPath: nil,
                cacheName: nil)
        }
        
        self.fetchedResultsController?.delegate = self
        
        do {
            tryself.fetchedResultsController?.performFetch()
        }
        catch let error as NSError {
            fatalError("BitcoinExchangeRateViewController - NSFETCHEDRESULTSCONTROLLER FETCH FAILED: \(error.localizedDescription)")
        }
        
        self.tableView.reloadData()
        fetchExchangeRateData()
    }
    
    internal func fetchExchangeRateData() {
        self.refreshButton.enabled = false
        dataController?.fetchExchangeRateData({
            (success: Bool, errorMessage: String?) in
            
            dispatch_async(dispatch_get_main_queue()) {
                self.refreshButton.enabled = true
                if success {
                    self.setLastUpdatedLabel()
                }
                else {
                    var errorMsg = ""
                    if let safeErrorMessage = errorMessage {
                        errorMsg = safeErrorMessage
                    }
                    
                    let alertController = UIAlertController(title: "Update Failed", message: errorMsg, preferredStyle: .Alert)
                    let cancelAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
                    alertController.addAction(cancelAction)
                    self.presentViewController(alertController, animated: true, completion: nil)
                }
            }
        })
    }
    
    // MARK: UITableViewDataSource
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        
        return 1
    }
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        if let currencyCount = fetchedResultsController?.fetchedObjects?.count {
            return currencyCount
        }
        
        return 0
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        let currencyCell: CurrencyTableViewCell = tableView.dequeueReusableCellWithIdentifier(CurrencyTableViewCell.ReuseIdentifier) as! CurrencyTableViewCell
        
        if let exchangeCurrency = self.fetchedResultsController?.objectAtIndexPath(indexPath) as? ExchangeCurrency {
                currencyCell.configureCell(exchangeCurrency)
        }
        
        return currencyCell
    }
    
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        
        return 60.0
    }
 
    // MARK: NSFetchedResultsControllerDelegate
    func controllerWillChangeContent(controller: NSFetchedResultsController) {
        self.tableView.beginUpdates()
    }
    
    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
        switch type {
        case .Insert:
            guard let newIndexPath = newIndexPath else {
                fatalError("BitcoinExchangeRateViewController - CONTROLLERDIDCHANGEOBJECT CALLED WITH NIL INDEXPATH")
            }
            tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: UITableViewRowAnimation.Fade)
            
        case .Update:
            guard let indexPath = indexPath else {
                fatalError("BitcoinExchangeRateViewController - CONTROLLERDIDCHANGEOBJECT CALLED WITH NIL INDEXPATH")
            }
            guard let currencyCell = tableView.cellForRowAtIndexPath(indexPath) as? CurrencyTableViewCell else {
                fatalError("BitcoinExchangeRateViewController - COULD NOT GET CELL FOR INDEX PATH")
            }
            guard let exchangeCurrency = anObject as? ExchangeCurrency else {
                fatalError("BitcoinExchangeRateViewController - CONTROLLERDIDCHANGEOBJECT CALLED WITH ANOBJECT NOT AN EXCHANGECURRENCY OBJECT")
            }
            currencyCell.configureCell(exchangeCurrency)
            
        case .Delete:
            break// this tableview does not enable deleting cells
            
        case .Move:
            break// this tableview does not enable moving cells
        }
    }
    
    func controllerDidChangeContent(controller: NSFetchedResultsController) {
        self.tableView.endUpdates()
    }
    
    func setLastUpdatedLabel() {
        if let lastUpdatedString = NSUserDefaults.standardUserDefaults().objectForKey(DataControllerUserDefaultKeys.lastUpdatedKey.rawValue) as? String {
            lastUpdatedLabel.text = lastUpdatedString
        }
    }
}

Minus the comment header the entire view controller is ~150 lines of code which consists mostly of the UITableViewDataSource and NSFetchedResultsControllerDelegate methods. I didn’t need anything in the UITableViewDelegate so its not included at all.

The approach of using a DataController that initiates on a background thread does add just a little complexity. The view will be loaded and displayed most likely before the AppDelegate has had a chance to call configureWithDataController. That means the first calls to the UITableViewDataSource functions will not have the NSFetchedResultsController available. Swift makes this incredibly easy to handle by simply checking that the NSFetchedResultsController is ready otherwise return 0 for the number of rows (the app only has one section so that can always just return 1).

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
    if let currencyCount = fetchedResultsController?.fetchedObjects?.count {
        return currencyCount
    }
    
    return 0
}

The configureWithDataController function gets called when Core Data is ready to be used. All it really does is setup the NSFetchedResultsController:

func configureWithDataController(dataController: DataController) {
        
    self.dataController = dataController
        
    let currencyFetchRequest = NSFetchRequest(entityName: "ExchangeCurrency")
    let sortDescriptor = NSSortDescriptor(key: "code", ascending: false)
        
    currencyFetchRequest.sortDescriptors = [sortDescriptor]
        
    if let managedObjectContext = self.dataController?.managedObjectContext {
        self.fetchedResultsController = NSFetchedResultsController(
            fetchRequest: currencyFetchRequest,
            managedObjectContext: managedObjectContext,
            sectionNameKeyPath: nil,
            cacheName: nil)
    }
        
    self.fetchedResultsController?.delegate = self
        
    do {
        try self.fetchedResultsController?.performFetch()
    }
    catch let error as NSError {
        fatalError("BitcoinExchangeRateViewController - NSFETCHEDRESULTSCONTROLLER FETCH FAILED: \(error.localizedDescription)")
    }
        
    self.tableView.reloadData()
    fetchExchangeRateData()
}

The only other code of note in this class is the call to fetch the data. It uses the networkOperationResult closure to know if the data was successfully retrieved, parsed and stored or if it needs to show the user an error:

internal func fetchExchangeRateData() {
    self.refreshButton.enabled = false
    dataController?.fetchExchangeRateData({
        (success: Bool, errorMessage: String?) in
            
        dispatch_async(dispatch_get_main_queue()) {
            self.refreshButton.enabled = true
            if success {
                self.setLastUpdatedLabel()
            }
            else {
                var errorMsg = ""
                if let safeErrorMessage = errorMessage {
                    errorMsg = safeErrorMessage
                }
                    
                let alertController = UIAlertController(title: "Update Failed", message: errorMsg, preferredStyle: .Alert)
                let cancelAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
                alertController.addAction(cancelAction)
                self.presentViewController(alertController, animated: true, completion: nil)

            }

        }

    })

}

The NSFetchedResultsControllerDelegate functions are pretty standard. The code to configure the labels of the cell is contained in a UITableViewCell subclass which is something else Marcus mentions but has been a habit of mine since I started writing iOS apps:

import UIKit
 
public class CurrencyTableViewCell: UITableViewCell {
 
    class var ReuseIdentifier: String { return "CurrencyCellReuseID" }
    
    @IBOutlet var currencyNameLabel: UILabel!
    @IBOutlet var currencySymbolLabel: UILabel!
    @IBOutlet var currencyRateLabel: UILabel!
 
    func configureCell(currency: ExchangeCurrency) {
        
        currencyNameLabel.text = currency.name
        currencySymbolLabel.text = currency.symbol
        currencyRateLabel.text = currency.rate
    }
}

I couldn’t find a way to have a static string like I would typically do for a cell’s reuse identifier so I did it with a class level var. Perhaps there’s a nicer way to do that.

Conclusion

This is a pretty small and hopefully easy to understand example of how to use NSOperation for networking along with a DataController setup for Core Data. One thing I’ve heard, read and been told since Swift was introduced was to try and not write Swift like you’re writing Objective-C. I know many are very excited about functional programming in Swift but I’m not quite there yet. Perhaps this code is more Objective-C style then Swift but on the other had things like optionals, guard statements and exception handling are not things we had with Objective-C. To me its easy to see how Swift can help you write apps with a lot less bugs and better tools to handle unexpected situations. I personally think I use fatalError a bit to much in this sample but I also used NSAssert quite a bit in Objective-C.

Again, any and all feedback is appreciated! Let me know what you think!

Bitcoin Exchange Rate on BitBucket

really

Written by Nick Harris

September 30, 2015 at 4:12 am

FeedLounge

leave a comment »

It may sound crazy today, but 10 years ago there was a very healthy competitive market for RSS readers. I was working at NewsGator. My friend and founder of NewsGator, Greg Reinacker, was very much in the middle of all things RSS at the time. My second week on the job I was invited to lunch with the author of FeedDemon. A few months later a co-worker was gushing because the author of NetNewsWire was sitting in our conference room. Another year or so later another co-worker was excited that the founder of FeedLounge was in Greg’s office.

The founder of FeedLounge was Alex King. Being friends with Greg, NickB and Brent (the aforementioned devs of FeedDemon and NetNewsWire respectively) I had the great honor of meeting Alex and getting to know him over drinks and dinners through the years. Alex is one of those people that I can honestly say I have never heard a single negative thing about. He made Crowd Favorite a successful company here in Denver that I would recommend to my friends looking for new jobs.

The last time I spent with Alex was a few years ago at the house warming party for Double Encore’s new office space. There was some awkward moment where someone was joining the conversation at just the wrong time and Alex brought up an old movie quote that I wish so much I could remember now, but the joke really didn’t matter. What resonates with me is his ability to make everyone in the conversation comfortable even at the expense of his own stupid joke.

I never worked directly with Alex, nor have I ever worked on anything in the WordPress community. Instead my remembrance of him is how much he contributed to the Denver tech community.

Rest well Alex.

Written by Nick Harris

September 29, 2015 at 5:42 am

Posted in Uncategorized

Swift – UIKit Collections and Map

leave a comment »

For background please see this post about Euchre

Euchre is a four player game which means that my UI is basically the same 7 elements for each player but displayed at different positions on the view. I use basic UIKit objects for these. There are the 5 UIImageView’s that represent the 5 cards in a users hand along with a single UIImageView representing the card played as well as a UILabel for any user messages like “I deal!” or “I call Hearts!”.

Here’s what the Storyboard looks. (I’m not a designer so feel free to laugh at it. As I wrote before, I use Euchre to learn and make a few bucks off ads along the way)

Screen Shot 2015 09 14 at 12 57 51 AM

I realized a few versions ago that having helper collections for these UIKit objects makes updating the UI during game play simpler. When my game play view controller loads I create these helper collections:

@IBOutlet weak var leftCard1, leftCard2, leftCard3, leftCard4, leftCard5: UIImageView!

@IBOutlet weak var partnerCard1, partnerCard2, partnerCard3, partnerCard4, partnerCard5: UIImageView!

@IBOutlet weak var rightCard1, rightCard2, rightCard3, rightCard4, rightCard5: UIImageView!

@IBOutlet weak var userCard1, userCard2, userCard3, userCard4, userCard5: UIImageView!

@IBOutlet weak var leftCardPlayed, partnerCardPlayed, rightCardPlayed, userCardPlayed: UIImageView!

@IBOutlet weak var leftPlayerLabel, partnerPlayerLabel, rightPlayerLabel, userPlayerLabel: UILabel!

 

var leftCards: [UIImageView] = []

var partnerCards: [UIImageView] = []

var rightCards: [UIImageView] = []

var userCards: [UIImageView] = []

 

var allPlayedCards: [EuchrePlayer.PlayerPosition: UIImageView] = [:]

var allPlayerHandCards: [EuchrePlayer.PlayerPosition: [UIImageView]] = [:]

var allPlayerLabels: [EuchrePlayer.PlayerPosition: UILabel] = [:]

 

internal func createHelperCollections() {


    leftCards = [leftCard1, leftCard2, leftCard3, leftCard4, leftCard5]

    partnerCards = [partnerCard1, partnerCard2, partnerCard3, partnerCard4, partnerCard5]

    rightCards = [rightCard1, rightCard2, rightCard3, rightCard4, rightCard5]

    userCards = [userCard1, userCard2, userCard3, userCard4, userCard5]

        

    allPlayerHandCards = [

        EuchrePlayer.PlayerPosition.LeftPlayer: leftCards,

        EuchrePlayer.PlayerPosition.PartnerPlayer: partnerCards,

        EuchrePlayer.PlayerPosition.RightPlayer: rightCards,

        EuchrePlayer.PlayerPosition.UserPlayer: userCards

    ]

        

    allPlayedCards = [

        EuchrePlayer.PlayerPosition.LeftPlayer: leftCardPlayed,

        EuchrePlayer.PlayerPosition.PartnerPlayer: partnerCardPlayed,

        EuchrePlayer.PlayerPosition.RightPlayer: rightCardPlayed,

        EuchrePlayer.PlayerPosition.UserPlayer: userCardPlayed

    ]

       

    allPlayerLabels = [

        EuchrePlayer.PlayerPosition.LeftPlayer: leftPlayerLabel,

        EuchrePlayer.PlayerPosition.PartnerPlayer: partnerPlayerLabel,

        EuchrePlayer.PlayerPosition.RightPlayer: rightPlayerLabel,

        EuchrePlayer.PlayerPosition.UserPlayer: userPlayerLabel

    ]

}

 

These help me get to specific UI assets when I need to change them but during the course of a Euchre game its fairly common to hide groups of UI assets from the view. Originally I started out with functions like this:

internal func hideAllPlayerCards() {

        

    for playerCards in allPlayerHandCards.values {

        for playerCard in playerCards {

            playerCard.hidden = true

        }

    }

}

    

internal func hideAllPlayedCards() {

    for playedCard in allPlayedCards.values {

        playedCard.hidden = true

    }

}

    

internal func hideAllPlayerLabels() {

    for playedCard in allPlayerLabels.values {

        playedCard.hidden = true

    }

}

 

This works great… but…

One of the comments about Swift that’s stuck with me is my friend Nate saying he’d close any PR with a for loop (in jest of course). But now every time I write a Swift for loop I wonder if there’s a better way.

In my case I have a collection and I want to alter them all in the same way. After a bit of googling I came across this post about Swift’s hidden “each” method.

With a little casting I re-wrote my code as:

internal func hideAllPlayerCards() {

    for playerHandCards in allPlayerHandCards.values {

        _ = playerHandCards.map { imageView in imageView.hidden = true }

    }

}

    

internal func hideAllPlayedCards() {

    _ = [UIImageView](allPlayedCards.values).map { imageView in imageView.hidden = true }

}

    

internal func hideAllPlayerLabels() {

    _ = [UILabel](allPlayerLabels.values).map { label in label.hidden = true }

}

 

This looks a lot different! I’m not a fan of using _ = for map but its the only way I’ve found to get rid of the warnings about unused results. Ugly, but once I had this written I realized I could do something like this:

let hideView = {

    (view: UIView) -> () in

    view.hidden = true

}

 

internal func hideAllPlayerCards() {

    for playerHandCards in allPlayerHandCards.values {

        _ = playerHandCards.map(hideView)

    }

}

    

internal func hideAllPlayerLabels() {

    _ = [UILabel](allPlayerLabels.values).map(hideView)

}

    

internal func hideAllPlayedCards() {

    _ = [UIImageView](allPlayedCards.values).map(hideView)

}

 

That’s pretty slick! I was originally uncomfortable with with lack of verbosity in the code but once the idea of map sunk in it was a clear winner.

I bet there’s a way to get rid of the for loop with a dictionary of arrays as well but I didn’t find it tonight.

Thoughts?

Written by Nick Harris

September 14, 2015 at 7:32 am

Posted in Uncategorized

Learning Swift – The First Weekend

leave a comment »

This is part of my Learning Swift series of posts. Please see Super Euchre Swift to understand what I’m building while learning Swift.

I’ve been reading and learning about Swift since I was sitting in Moscone for WWDC 2014 and had the same “No F__king Way!” collective moment with the rest of the Apple Developer World. Ideas like let vs var, generics and even optionals and unwrapping were never hard for me to grasp. That means I think I’ve been OK at reading Swift over the last year but haven’t taken any opportunity to write some myself.

A big part of the reason I held off is Apple’s own statement that the language was going to be changing in big ways over that year. Way back when I first graduated college, there was a new language called C# along with the .Net Framework coming out of Microsoft. Though the first C# book I bought was in 2001, I didn’t ship real C# code until I was at NewsGator in 2005. The differences between the language and the compiler between .Net 1.0 and .Net 2.0 was huge. Microsoft was a fantastic partner in the transition but it was still eye opening to be sitting with a compiler engineer in Redmond trying to figure out why our code was broken.

So I waited. And what I’ve seen both from Apple at WWDC this year and the general buzz in the community has me convinced that now is the time to really jump in.

EuchreCard – Struct or Class

I started this weekend the same way I start every re-write of Euchre… the EuchreCard object. Euchre itself uses common playing cards. Luckily for me, Apple used playing cards in its Swift documentation about nested types.

The first thing that struck me about that sample code was that the BlackjackCard was a struct and not a class. This seems to be one of the fundamental decisions in Swift a developer needs to think through when adding objects to the code base.

I decided to go with struct for EuchreCard. For one, Apple used a struct for BlackjackCard. That’s usually a dead give away. The other was Greg Heo’s (@gregheo) Switching Your Brain To Swift talk at this year’s 360iDev. He had a slide (which unfortunately I don’t see in that post) about Swift foundation and what things are represented by struct vs. class. From what I recall the difference was quite stark in that there were minimal classes and many structs. That leaned me even closer to struct.

The ultimate conclusion I drew was deciding if an object should be passed by reference or by value. In Swift, structs are very much on par with classes. They can have properties, initialization functions, even their own functions. Obviously that’s very different then ObjC. Classes, however, are passed by reference, while structs are passed by value. That boils down to understanding when an object should be mutable and who can change it.

Mutability and type safety are some of the fundamental ideas that Swift is being built on. Pass by value is safer in the aspect that you can pass a value into a sub function without the caller worrying about what its value may have changed to when the sub function returns. That’s not to say mutable objects don’t have their place. They absolutely do. The approach I’ve decided to take with all things Swift is to use the safest option first then move to something thats a little more unsafe once I know WHY I need it to be. So for me that means struct first till I know I need it to be a class.

This is counter to what the Apple docs say, but I was also in the room for the very popular Protocol Oriented Programming in Swift session at this years WWDC. I need to watch that another dozen or so times to really understand it all but in the mean time I like this Stack Overflow answer on Swift Structs vs. Classes.

Suits and Associated Values

The next big decision I came across was how to represent a cards suit and rank. I’ve always used enums for these in the past and Swift didn’t change that decision. It did make it a little more interesting though.

With swift, an enum case can have an associated value. Enums in ObjeC, at least how I have always used them, are always ints. The BlackjackCard however uses a char for the suit. Not only is it a char, but its the unicode character for the typical card suits of hearts, spades, diamonds and clubs. Super clever!

But does clever mean better?

For me it doesn’t. In Euchre, the trump value of a Jack is tied not only to its suit but to the color of that suit. For instance, if trump is Clubs then the Jack of Clubs is the best card in the deck while the Jack of Spade is second. Its because both suits are black, the same way that both hearts and diamonds are red.

In my past code for Euchre I’ve relied on how I setup enums to help test if a card has the same color suit as another by using mod. This means I can do if statements that look like this:

if ((rank == .Jack) && (suit.rawValue % 2) == (trumpSuit.rawValue % 2))

If you’re not familiar with Swift, “rawValue” probably sticks out to you. When you assign an associated value to an enum case you can get its actual value by using rawValue. This allows me to treat a suit’s enum value as an Int in order to call % on it.

So though I could use a clever unicode character for a cards suit enum, I opted to stay with Int.

Calculated Properties

In my UI layer I have assets for each card that are named by using an abbreviation for the cards rank and suit. For instance, the Ace of Spades has an asset name of AS. The King of Clubs is KC while the Ten of Hearts is 10H.

My old ObjC code used calculated properties to return these to callers dealing with a EuchreCard. A calculated property is one that looks and acts like a property from the callers perspective but uses an internal method to calculate its value whenever its accessed.

Many old school ObjC programmers will tell you that calculated properties are evil and you should always use a function. They have very good arguments why that I typically agree with. Though for me, things like this asset name for a card were a perfect candidate for a calculated property since they made reading the code higher upstream easier on me though I took a small calculation hit on each access of the property.

Lazy Stored Properties

Swift solves this for me though in a very nice way with the introduction of lazy stored properties. Obviously my EuchreCard doesn’t know the name of its asset until it has its suit and rank set. I also don’t need this value right away when creating a deck of cards. I only need it when the UI first needs to display a card.

So instead of declaring a property that needs to be set on init…

(oh did I mention that Swift makes you set a value for all your properties during init? It does and its pretty awesome since it leaves nothing ambiguous up to the compiler/runtime)

…you can declare it lazy and set its value to a function that will be invoked the first time the derived value is needed. This way I can calculate its value once when its needed and have it stored for any point after. Very nice! I did the same with longName which I’ve used in past Euchre versions.

There’s nothing ground shaking or particularly valuable about my EuchreCard so why not share.

Here’s a gist of my EuchreCard struct.

First Weekend Conclusions

I wrote this down in my running Swift notes about 1 am Saturday but I like it:

“ObjC is hard to learn and really hard to learn the “right” way to use it. Swift feels different. The way it overlaps with other languages has made it very easy for me to pick up so far. I thought I would be fighting it more but instead I’m seeing much better ways to implement Euchre’s AI layer. I can see ways I could have done this in ObjC but the look of the code feels much more natural with Swift”

It was a fun weekend! Looking forward to diving in more!

Written by Nick Harris

August 24, 2015 at 3:40 am

Posted in Learning Swift

Follow

Get every new post delivered to your Inbox.