Nick Harris

Archive for the ‘Learning Swift’ Category

Bitcoin Exchange Rates – A Swift 2.0 Sample App

with 3 comments

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

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

Super Euchre Swift

with 3 comments

Back when the first iPhoneOS SDK was announced I decided I wanted to learn how to build apps. I had been playing a shareware Euchre game on my Windows PC since college and thought it would be a fun exercise to learn Objective C while writing my own Euchre game for iPhone.

For those who don’t know, Euchre is the card game of choice for all bored lifeguards, wait staff, high school and college students in the Great Lakes region. Its the regional pass time. Its also very popular in Canada and Australia.

Pocket Euchre 1.0 went live in the app store in January of 2009. It was never a big money maker but for the first few years it averaged almost $1000/month. Since then its name has changed to Super Euchre, it’s become completely Apple Ad supported and its earnings have dwindled down to just over $100/month. Its no longer the best app I’ve ever shipped, but it still serves as a way for me to learn and use changes in iOS along with showing off just how bad of a UX/Designer I am – all while making a little beer money for the effort.

Being that Euchre has been my little side project and learning tool, I’ve decided to take another stab at a re-write of it, this time completely in Swift 2.0. My plan is to eventually ship another version of Super Euchre that has Game Center game play as well as a Mac version. Will I ever get there? I really hope so, but knowing what the real return on investment would be at this point I don’t know if my grand scheme will ever come true. But I can still use it as a way to learn Swift by applying it to problems I solved previously in ObjC so I can hopefully see the strengths of Swift instead of just re-writing the same logic with a new syntax.

Learning Swift

I’ve been enjoying Brent’s Swift Diary posts, though to be honest they were mostly over my head as I had not really gotten my hands dirty with Swift. I’m a week out from taking a break from contracting work for September and besides getting some long overdue house work done, I plan to spend a lot of time learning Swift 2.0. I started this weekend. So I think I’ll give the Learning Swift blog series a shot as well.

There’s a couple reasons to do this. First I want to think through the decisions I’ve been making. For me the best way to reflect on decisions is to write out what reasoning I had at the time. Secondly it gives anyone who reads this a chance to tell me their ideas on what they would have done instead.

I’ve added a category to my blog called Learning Swift. We’ll see how it works out.

Written by Nick Harris

August 24, 2015 at 1:35 am

Posted in Learning Swift