Nick Harris

Suntracker

leave a comment »

Icon 120

Introducing Suntracker!

Suntracker is a simple app that uses Core Location to determine a users location then calculates sunrise and sunset times along with total daylight for the location based on the NOAA Sunrise/Sunset Calculator. Suntracker shows you a graph of 3 months, 6 months or a years worth of daylight time and the ability to drag you finger across the graph to see individual days. The today extension shows the days sunrise and sunset times along with how many hours and minutes the day has of sun light. Suntracker will also notify you each day when the sun rises and sets – even on your Apple Watch!

Built using iOS 9 and Swift 2, Suntracker is designed for use on iPhone 4S through the iPad Pro. Much attention has been paid toward battery usage as well as accessibility. It supports large and bold fonts on all devices as well as multitasking on all iPads.

Screenshots:

4S

4SPortrait    4SDayDrag

4SLandscape

6S Plus

6SPlusPortrait    6SPlusLandscape

6SPlusLandscapeSettings

iPad Air 2

IPadAirMultitaskingTwoThird

IPadMultitaskingHalf

IPadMultitaskingThird

Today Extension and Apple Watch

6SPlusToday   

AppleWatch 

Availability

Suntracker is only available on BitBucket for developers.

Do not release this app or its assets as your own. The code and icon are my copyright. If you’re interested in building an app like this I would encourage you to use EDSunriseSet.

Idea

Suntracker started as an idea a few years ago. I had spent months writing my book and wanted an idea to play with to get me back into everyday coding. It was the beginning of February at the time and I was interested in the sunrise/sunset times of each day as the calendar moved from Winter to Spring. I found the NOAA Javascript calculator and decided to port it to Objective-C. I had enough code to make the calculations based on Core Location but I never built out the UI.

Fast forward 2 years to this past February and again I was in search of a side project. Translating Javascript to Swift was interesting to me but I also wanted to take some time to do something more with Autolayout on multiple devices along with dynamic text. I ended up learning a lot more about Core Location along the way.

Code Highlights

Core Location

I spent a lot of time getting my head around all of the different ways to use Core Location. I ended up with an approach that asks for location data even in the background but still supported using location only while in-app. With iOS 8 you have the opportunity to supply strings explaining why the app needs location information using the NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription keys in your info.plist. You can choose which you prompt the user for, but you only get one shot at it. I opted for always on but the user can change that in the Settings app if they wish:

4S Settings

When allowed background updates I used startMonitoringSignificantLocationChanges. The documentation for Core Location seems lacking to me but it felt like I could get the best experience while using the least amount of battery this way. I really wanted to use differed location updates but that only works by using the GPS radio with the best accuracy. This app doesn’t need GPS. Wifi or local cell tower triangulation are fine which is what I get with startMonitoringSignificantLocationChanges. I spent a few days with different devices driving around town while leaving others at home and everything worked as expected.

When only allowed in-app location access the code calls requestLocation. The system gives you one location then stops location monitoring. For an app like this its fine but the delay on launch to get a location is a bit annoying.

The Today Extension was more difficult. I couldn’t find a way to use CLLocationManagerDelegate in any way so it just uses the location property on the manager.

Here’s the function that gets called anytime the app is launched or comes to the foreground:

func handleLocationManagerStatus(status: CLAuthorizationStatus) {
        
    switch status {
    case .NotDetermined:
        // need to ask for location permissions
        coreLocationManager?.requestAlwaysAuthorization()
            
    case .AuthorizedAlways:
        // register for notifications only when location permissions have been granted
        UIApplication.sharedApplication().registerUserNotificationSettings(UIUserNotificationSettings(forTypes:[ .Sound, .Alert], categories: nil))
            
        // the app doesn't need a precise location so no need for GPS which will impact battery use
        // instead use significant location changes
        coreLocationManager?.startMonitoringSignificantLocationChanges()
            
    case .AuthorizedWhenInUse:
        // register for notifications only when location permissions have been granted
        UIApplication.sharedApplication().registerUserNotificationSettings(UIUserNotificationSettings(forTypes:[ .Sound, .Alert], categories: nil))
            
        // if we're only allowed in-app locations theres no need to get location updates as its highly unlikely the user will
        // be moving great enough distances to alter sunrise/sunset times. Instead just request the current location
        coreLocationManager?.requestLocation()
            
    case .Denied:
        // we need to be sure the view is loaded so the alert can be presented
        if let sunTrackerViewController = sunTrackerViewController where sunTrackerViewController.isViewLoaded() {
            sunTrackerViewController.displayLocationPermissionsAlert()
        }
            
    case .Restricted:
        // we need to be sure the view is loaded so the alert can be presented
        if let sunTrackerViewController = sunTrackerViewController where sunTrackerViewController.isViewLoaded() {
            sunTrackerViewController.displayLocationPermissionsRestrictedAlert()
        }
    }
}

Tuples

This is another app based primarily on NSOperations. After years of messy block to block implementations, I can’t imagine not breaking functionality into operations. The push toward functional programming aids in that thought. What I like about Suntracker is the one operation that calculates sunrise/sunset and other auxiliary operation groups that use it to complete the app.

I wrote about using tuples to pass data into operations and getting results out using operationInput and operationOutput properties. I really like the approach and plan on continuing that pattern. For example:

class CalculateSunriseSunsetOperation: NSOperation {
    
    // MARK: Public Properties
    var operationInput: (coordinate: CLLocationCoordinate2D?, date: NSDate?)
    var operationResult: (error: NSError?, sunriseTimeInMinutes: Double?, sunsetTimeInMinutes: Double?)
 
    . . . 

Just yesterday I saw a post about tuples being lightweight / limited scope structs. I think that’s a fair comparison.

Constants

I had been using enums for constants such as user default keys. I like that since it groups like values together but I was using rawValue everywhere which wasn’t ideal. For Suntracker I decided to use a struct instead. Here are my user default keys:

struct SuntrackerUserDefaultsConstants {
    static let UserDefaultsSuiteName = "group.cliftongarage.suntracker"
    static let SunriseTimeUserDefaultsKey = "SunriseTimeUserDefaultsKey"
    static let SunsetTimeUserDefaultsKey = "SunsetTimeUserDefaultsKey"
    static let TotalDaylightUserDefaultsKey = "TotalDaylightUserDefaultsKey"
    static let DaysUntilDSTSwitchUserDefaultsKey = "DaysUntilDSTSwitchUserDefaultsKey"
    static let LastLocalityUserDefaultsKey = "LastLocalityUserDefaultsKey"
    static let LastDayCalculatedUserDefaultsKey = "LastDayCalculatedUserDefaultsKey"
    
    static let SunriseNotificationsUserDefaultsKey = "SunriseNotificationsUserDefaultsKey"
    static let SunsetNotificationsUserDefaultsKey = "SunsetNotificationsUserDefaultsKey"
    
    static let LastLocationFetchUserDefaultsKey = "LastLocationFetchUserDefaultsKey"
    static let LastStringsOperationUserDefaultsKey = "LastStringsOperationUserDefaultsKey"
    
    static let SelectedTrendChartUserDefaultKey = "SelectedTrendChartUserDefaultKey"
}

They’re still grouped but there’s no need for rawValue everywhere in code to use them. Much better!

Today Extension

Suntracker also has a Today Extension.

I found developing for the Today view incredibly frustrating. I’d like to see data on how many users enable a today widget to determine if the struggle is worth the effort. As an iOS user I can’t imagine it is. I’ve seen some say they use the Today view often but personally I don’t.

What frustrated me the most was the odd auto layout constraints as well as the inability many times to connect the debugger. It was hard to tell if I could rely on the widgetPerfromUpdateWithCompletionHandler to get called every time the user viewed the Today panel. I found some saying to call code to update the view in viewWillAppear as well so that’s what I ended up doing.

Shared NSUserDefaults

Setting up shared NSUserDefaults was fun. Just make sure your user defaults suite name is exactly the same as your app group identifier.

Dynamic Text

Dynamic text was more difficult to support then I would like. Not because its all that hard but designing a user interface that would react well was difficult. Layouts that looked fine at regular size look horrible at large and bold. In the end I decided that calculated values were more important then simple labels so I went with shrinking the simple labels in order to keep the calculated values large. For instance, here’s a 4S in landscape with the largest text set to bold:

4SBoldBigText

Conclusion

This app was a lot of fun to finish. I spent a good portion of time working with Core Location and battery usage in order to get the best user experience but I think that time was well worth it. Also spending more time playing with auto layout in different size classes using different accessibility settings was great. I can’t imagine being a developer with a single test device though. I wrote this app using my 4S, 5, 6, 6S Plus, iPad Mini, iPad Air and iPad Pro. I needed all of them in order to find issues with device speed and well as layout differences.

All in all I’m just happy Spring is here! 

Code is available on BitBucket.

Written by Nick Harris

March 9, 2016 at 10:53 pm

Posted in Uncategorized

Denver

leave a comment »

Denver was named the #1 place to live in the United States today by U.S. News & World Report. I’ve lived here for almost 15 years now and I can attest that its very true. The people are great, the natural beauty is breathtaking and the job market is amazingly robust. Even during the big bust of 2008 when the company I worked for had to layoff a good percentage of employees, my friends that were let go had offers from other great companies in short order. 

I spent a good portion of last year deciding if I wanted to stay in Denver or move to somewhere new. I visited a bunch of cities – Raleigh NC, Washington DC, San Francisco CA, Seattle WA, Chicago IL, Madison WI. I loved them all and had a really hard time deciding which I liked over all the others. But in the end I decided the place I really wanted to be was here in Denver. 

I live 5 miles from Red Rocks. I have friends here from high school and college who are basically family after 20+ years. I can take a lunch break and drive up the canyons and switchbacks to views like this and be back before afternoon meetings.

IMG 2196

I’m about to start a new job search. After 5 years of remote work I’m particularly interested in working in an office again – maybe even a startup again – though remote work isn’t out of the question. What is out of the question is leaving Denver even if that means missing the opportunity to work at some pretty amazing companies. The only thing that could really draw me away from Denver is to be closer to my family in Ohio.

It wasn’t an easy decision to come to but its one that I have no doubts about. Denver is that awesome.

Written by Nick Harris

March 3, 2016 at 6:05 am

Posted in Uncategorized

The Making of an Icon (by a developer with Flying Meat’s Acorn)

leave a comment »

I spent a year working for the Ohio University College of Visual Communication. I still love that Ohio U gave me the opportunity to make some money while letting me audit classes outside of my Major (Computer Science) and Minor (English).

I learned a ton about Adobe Photoshop that year.

—-

I‘ve been working on a side project called “Suntracker”. Its a simple app that tracks sunrise / sunset times and how much sunlight there is per day based on Core Location. 

Last weekend I download Flying Meats Acorn 5.3 to make an icon. I’ve been an Acorn user for years so I was pretty excited to see the new version.

My review: Fantastic.

Here’s the icon I created in an evening:

Icon 180

CbttyXQUUAA8MEb jpg large

—-

I’m by no means a graphic designer, nor would I ever hire myself out as one… but all of the personal apps that I’ve published on the app store have icons I created. I’m familiar with the process and how many icon sizes you need to make.

This one is my favorite.

I like how it looks on my iPhone 4S, 5, 6, 6S Plus, iPad Pro and Watch.

Its actually a really simple image. A circle shape with a yellow > orange gradient on top of a light blue > midnight blue gradient background.

The genius of Acorn is how I can scale it any way to fulfill all the sizes needed in todays iOS development environment.

Acorn wasn’t always that way. This is the first icon I’ve ever created with it for iOS. In the past I’ve used vector based image editors – particularly Gimp which is powerful but not fun to use.

—-

Now I’ll admit that Gus Mueller is a friend. A few years ago, as I was introducing him to new friends at WWDC, my friend Rebecca asked me if he was one of my heroes. I paused for a second then answered with an easy and simple “yep”.

—-

Thanks Gus for Acorn! Keep up the amazing work!

Written by Nick Harris

February 26, 2016 at 7:35 am

Posted in Uncategorized

Swift Array with a Default Value

with 2 comments

I discovered this nifty little Swift array initializer tonight:

Screen Shot 2016 02 17 at 10 45 11 PM

Sweet! Set me up with a bunch of default objects!

Screen Shot 2016 02 17 at 10 48 29 PM

What the… Ugh.

Screen Shot 2016 02 17 at 10 49 09 PM

I get why but I really wanted 10 different NSObjects. 

Oh well.

Written by Nick Harris

February 18, 2016 at 5:56 am

Posted in Uncategorized

Swift vs. JavaScript

leave a comment »

Every year around this time I start watching the sunrise and sunset times. Two years ago I even spent a few days looking into the math that powers the NOAA sunrise/sunset calculator. It was before Swift but I spent a little time porting the JavaScript to Objective-C. I had it somewhat working but never finished it off. It was more an exercise for myself to port JavaScript to Objective-C.

I hated JavaScript at the time. I still prefer other languages to JavaScript. I like strongly typed languages with powerful compilers. Its just my preference though. I like the color green more then red too.

In 2003 I wrote a backend service in JavaScript that used classic ASP pages and XMLHTTPRequests to communicate with a Java Applet client as well as Delphi written Windows Services to control teleconferences. It ended up being my intro to C# and eventually Azure. Since then I’ve written some throw-away Node.JS web services including a Passport project and talk I gave about a year ago.

Those experiences really opened my eyes to the power of JavaScript. Tonight I spent a few hours porting the same NOAA JavaScript code to Swift.

A recurring theme I’ve heard in the developer community at large is that knowing JavaScript means you can now write iOS apps in Swift.

HA HA HA HA HA HA HA HA HA HA HA HA HA HA

I know all of my long time iOS developer colleagues know that this train of thought is ridiculous. 

The syntax between Swift and JavaScript is similar. Higher lever functions like map and filter are in both. Beyond that I fail to see the comparison.

I’m not going to dive into the differences. This post is more of a wake up call to anyone looking to hire an iOS dev and thinking that JavaScript experience = Swift.

It doesn’t.
At all.

If you are hiring – show them my NSOperation / NSOperationBlock retain cycle post and ask them to solve it. 

Written by Nick Harris

February 16, 2016 at 8:06 am

Posted in Uncategorized

UIAlertController + NSOperation

with one comment

I’ve been investigating NSOperation and dependency chaining lately including my posts about syncing data with CloudKit. While working on that code I had two things come up that I didn’t get around to investigating:

1. How can I pass data back after an operation completes in a more consistent way
2. How can I allow user input within an operation

I put together a little project tonight that I used to explore these issues. I like it so I thought I’d share it.

Sample App

This code is available on BitBucket

Its so much easier to explain ideas within the context of a sample app. This app is super simple:

– Tap the button
– Get prompted for what operation you want to run
– View the results

The code creates and executes an operation chain when you tap the button. The operations are executed or canceled depending on which option is chosen. The console documents everything that is happening and when. Simple.

The operation choice is encapsulated in an enum:

enum OperationChoice {
    case Unknown
    case OperationOne
    case OperationTwo
    case OperationThree
    
    func actionSheetOptionTitle() -> String {
        switch self {
        case .Unknown: return "Unknown"
        case .OperationOne: return "Operation One"
        case .OperationTwo: return "Operation Two"
        case .OperationThree: return "Operation Three"

        }

    }

}

Operation Return Values 

I really dislike how my operations in my CloudKit proof of concept return data. Its done with multiple properties on the operation class. For another developer to use those operations they would need to know more details of the operation then they really need to. Moving forward I think I’ll use a tuple instead:

var operationResult: (error: NSError?, operationChoice: OperationChoice)

 

I’ve used this approach in some other Swift class operations but I like how it fits with operations. The consumer of the operation knows the one property they need to interact with on completion. It’s much simpler then lines of documentation about what public properties will have the changes. It also reflects a more functional approach to programming with monad type behavior.

User Interaction in an NSOperation

Every NSOperation I’ve ever created has used main() to execute instead of start(). To be honest, the responsibilities of overriding functions and KVO calls using start() to be a good citizen made me avoid it.

That is until I read a post about combining NSURLSession and NSOperation by Marcus Zarra. Its a great approach that I plan on using. It also demystified some of my apprehensions with overriding the life-cycle of an NSOperation.

I had tried using UIAlertController’s in a main() override. The problem is that the operation reaches the end of main() before the user has a chance to respond, therefore releasing the operation before any dependencies could use the input. Controlling the life-cycle of the operation means that I can keep the operation executing until the user interaction is registered and other operations in the chain can then react appropriately on completion.

If you haven’t read Marcus’s post yet please do so now. I’ll wait…

Cool. Now you know that Swift is a little weird with overriding private properties which is essential to this approach with NSOperations. I also tried using the ivars directly and also found it didn’t work. I need this code in multiple operations. I initially tried using a protocol but ran into more issues about overriding private properties. I think the only solution in this situation is to subclass:

class ConcurrentOperation: NSOperation {
    
    override init() {
        
        override_executing = false
        override_finished = false
        
        super.init()
    }
    
    func completeOperation() {
        executing = false
        finished = true
    }
 
    private var override_executing : Bool
    override var executing : Bool {
        get { return override_executing }
        set {
            willChangeValueForKey("isExecuting")
            override_executing = newValue
            didChangeValueForKey("isExecuting")
        }
    }
    
    private var override_finished : Bool
    override var finished : Bool {
        get { return override_finished }
        set {
            willChangeValueForKey("isFinished")
            override_finished = newValue
            didChangeValueForKey("isFinished")
        }
    }
}

 

Sample Operation Code

With a way to keep my operation in memory until the user interaction is complete, I could create an operation that displays an Action Sheet to choose an operation and operations that use an Alert to display which option was chosen (I’ll only show OperationOne as the other two are identical):

class PromptUserOperation: ConcurrentOperation {
    
    var operationResult: (error: NSError?, operationChoice: OperationChoice)
    
    override init() {
        
        operationResult = (error: nil, operationChoice: .Unknown)
        
        super.init()
    }
    
    override func start() {
 
        print("PromptUserOperation.start()")
        promptUser()
    }
    
    func promptUser() {
        
        let alertController = UIAlertController(title: "Which operation should be performed?", message: nil, preferredStyle: .ActionSheet)
        
        alertController.addAction(createOperationAlertAction(OperationChoice.OperationOne))
        alertController.addAction(createOperationAlertAction(OperationChoice.OperationTwo))
        alertController.addAction(createOperationAlertAction(OperationChoice.OperationThree))
        
        dispatch_async(dispatch_get_main_queue(),{
            if let appDelegate = UIApplication.sharedApplication().delegate,
                let appWindow = appDelegate.window!,
                let rootViewController = appWindow.rootViewController {
                    rootViewController.presentViewController(alertController, animated: true, completion: nil)
            }
        })
    }
    
    func createOperationAlertAction(operationChoice: OperationChoice) -> UIAlertAction {
        
        return UIAlertAction(title: operationChoice.actionSheetOptionTitle(), style: .Default) {
            [unowned self]
            action in
            
            self.userSelection(operationChoice)
        }
    }
 
    func userSelection(selectedOperation: OperationChoice) {
        print("PromptUserOperation.userSelection( \(selectedOperation.actionSheetOptionTitle()) )")
        
        operationResult = (error: nil, operationChoice: selectedOperation)
        completeOperation()
    }
    
    deinit {
        print("PromptUserOperation.deinit")
    }
}
 
class OperationOne: ConcurrentOperation {
 
    override func start() {
        
        if cancelled {
            print("OperationOne - CANCELLED")
            completeOperation()
            return
        }
        
        print("OperationOne.start()")
        promptUser()
    }
    
    func promptUser() {
        
        let alertController = UIAlertController(title: "You chose Operation One!", message: nil, preferredStyle: .Alert)
        
        let alertAction = UIAlertAction(title: "Yay!", style: .Default) {
            [unowned self]
            action in
            
            self.completeOperation()
        }
        alertController.addAction(alertAction)
            
        dispatch_async(dispatch_get_main_queue(),{
            if let appDelegate = UIApplication.sharedApplication().delegate,
                let appWindow = appDelegate.window!,
                let rootViewController = appWindow.rootViewController {
                    rootViewController.presentViewController(alertController, animated: true, completion: nil)
            }
        })
    }
    
    deinit {
        print("OperationOne.deinit")
    }
}
 
@IBAction func runOperations() {
        
    // create an operation queue
    let operationQueue = NSOperationQueue()
        
    // create the operations
    let promptUserOperation = PromptUserOperation()
    let operationOne = OperationOne()
    let operationTwo = OperationTwo()
    let operationThree = OperationThree()
       
    // create the process operation
    let processChoiceOperation = NSBlockOperation {
        [unowned promptUserOperation, unowned operationOne, unowned operationTwo, unowned operationThree] in
            
        print("processChoiceOperation")
            
        let promptUserOperationResult = promptUserOperation.operationResult
            
        if let error = promptUserOperationResult.error {
            print("Error while prompting the user: \(error)")
        }
        else {
            switch promptUserOperationResult.operationChoice {
            case OperationChoice.OperationOne:
                operationTwo.cancel()
                operationThree.cancel()
            case OperationChoice.OperationTwo:
                operationOne.cancel()
                operationThree.cancel()
            case OperationChoice.OperationThree:
                operationOne.cancel()
                operationTwo.cancel()
            default:
                fatalError("Unknown operation choice: \(promptUserOperationResult.operationChoice)")
            }
        }
    }
        
    // set the dependencies
    processChoiceOperation.addDependency(promptUserOperation)
    operationOne.addDependency(processChoiceOperation)
    operationTwo.addDependency(processChoiceOperation)
    operationThree.addDependency(processChoiceOperation)
        
    // queue the operations
    operationQueue.addOperation(promptUserOperation)
    operationQueue.addOperation(processChoiceOperation)
    operationQueue.addOperation(operationOne)
    operationQueue.addOperation(operationTwo)
    operationQueue.addOperation(operationThree)
}

 

And here’s what the console looks like after the user interacts with the app state in the screen shot:

initial_launch

PromptUserOperation.start()


prompt_choice

 

PromptUserOperation.userSelection( Operation One )

processChoiceOperation

OperationThree – CANCELLED

OperationThree.deinit

OperationOne.start()

OperationTwo – CANCELLED

OperationTwo.deinit


show_choice

 

OperationOne.deinit

PromptUserOperation.deinit

 

I love NSOperation Chaining even more now!

Written by Nick Harris

February 10, 2016 at 6:34 am

Posted in Uncategorized

CloudKit + Core Data + NSOperations – Conclusions

with one comment

This is post four of four about CloudKit syncing with Core Data. The original post is here. Sample code is here. You’ll need to enable CloudKit and other entitlements in your own environment to get it to work correctly.

Conclusions

The previous posts includes a ton of code. You can see all of it by getting the project from BitBucket. In this post I’ll highlight what I liked and what I didn’t like. I learned a ton while writing it and would again urge you to heed my warning in the first post about it being unpolished and incomplete. 

CloudKit

CloudKit is incredibly powerful. Once I started really diving in I could tell it was thoughtfully created to support almost any type of app someone would want to create. Getting started was relatively easy and the more I learned I think the architectural decisions about how to get the most out of it became easier. Having pre-made NSOperations that handle all of the networking is very nice, even though I found myself subclassing them. Subscriptions are also incredibly powerful for getting any kind of push notifications and background fetching an app might need.

What I don’t like is that it is so generically built – its a strength and a weakness. From my time playing around and researching I didn’t find any real good best practices guidance which made all of the different ways of utilizing it harder to figure out (and I still think I’m wrong with some) as well as possible edge cases I never thought of.

You’re also betting a lot of usability of your app on your users having CloudKit enabled and having enough storage for your app to function appropriately. Using error codes you can build around that and appropriately inform your users of the problem, but they’re more then likely going to blame you instead of Apple. A casual user who paid $10, $20 maybe even $50 for a productivity app but was then prompted to pony up more money to Apple to make it work is a support nightmare.

I also don’t like that its unknown how Apple will handle changes they need to make to keep CloudKit going forward while handling backward compatibility. Its completely reasonable to think that the next version of CloudKit could deprecate all of the NSOperatoins I’m using in favor of new ones that handle problems they may be having server side. It may not be a big deal to refactor, depending how big the changes are, but that’s still a risk that is out of my hands.

The last thing that concerns me is how I would transfer a users private data to another backend system should I ever need to. The device would have to somehow do this at the great expense of the user experience. Imagine running an app for the first time after it updated in the background and it tells you it needs an hour of processing and all of your bandwidth to transfer the data somewhere else. This exact problem came up as my time on Glassboard was ending. The pricing of Azure SQL Server instances was dropping and the code we were using to support Table storage was beginning to show cracks. We didn’t do this but we could have spun up our new SQL instance and a background instance to slowly move our data from Tables to SQL. Our server code could make the process much more seamless to our users then what I would envision with CloudKit.

That being said, for me personally I would prefer a custom backend. I’m probably more biased then most iOS or Mac developers though since I’ve built a few on my own and worked on a handful of others throughout my career. I’ve built them in Java, Node.JS, C# and even straight JavaScript and classic asp pages years ago. I actually miss working on them. Performance tuning a database and server instances is more interesting to me then tweaking auto layout constraints for a day to get an animation to look nice. Not that I don’t love when a app looks sleek when I show it off, but I’d prefer showing off response time improvements more.

For my side project though? I’ll go with CloudKit. I’m building it for myself so I can easily keep up with any changes without worrying about dealing with the support emails.

NSOperation Chaining

I’ve said it before and I’ll say it again. I LOVE NSOperations! Once I found the pattern for passing data I love chaining them even more. CloudKit’s dependencies between zones and records as well as fetching before modifying make NSOperation dependencies close to a no brainer. I also like how I can separate concerns, encapsulate better and have reusable blocks of code I can chain together in many different ways.

It reminds me of one of my last final exams in college where we needed to make a data transformation diagram and detailed writeup showing how we would process a big chuck of data into its many pieces. I wish I could remember what the actual problem we had to solve was but after taking all 2 hours on the final to write it up I promptly empty my brain of it. Someone on twitter mentioned that the approach also resembles monads in functional programming. Just judging from the wikipedia page I’d have to agree but I haven’t explored functional programming very much yet.

Swift

Frankly my love of Swift just keeps growing. The proof of concept uses protocols and enums heavily. I haven’t taken the time to figure out how I would have accomplished the same things in Objective-C but I’m certain I would like my Swift code better. With swift though I learned:

– I love power of Swift Sets. Having intersect and subtract methods is fantastic. 

– Swift higher order functions are also great. I use flatMap a ton in this code. I probably could have used filter and reduce more as well.

– Finding out I could do inline predicates to find the index of an object was a very happy surprise:

// do we have this record locally? We need to know so we can remove it from the changedCloudKitManagedObjects
if let index = changedCloudKitManagedObjects.indexOf( { $0.recordName == deletedServerRecordID.recordName } ) {
    changedCloudKitManagedObjects.removeAtIndex(index)
}

 

– Casting in Swift is powerful in “if let” statements but even better in flatMap. 

– Guard statements continue to be good for me as well though I found I used them much less in this code then my first attempts at Swift.

– I think Swift is much more readable then Objective-C at this point.

– I still hate retain cycles. Seriously. I hate them. Capture lists are nice and all but I hate the fact that I still have to think about memory management.

Series Conclusion

I hope these posts are helpful. Don’t use them as a blueprint for how to use CloudKit but rather a long winded explanation of one persons adventures. Any feedback would be greatly appreciated!

Written by Nick Harris

February 9, 2016 at 6:54 am

Posted in Uncategorized

Follow

Get every new post delivered to your Inbox.