Update on Xcode 8.3

Xcode 9 is getting up their in beta numbers, so it's time to look back on 8.3.1 and 8.3.2 to see if Apple managed to fix the issues plaguing Xcode I wrote about for 8.3.0.   Answer:  no they didn't. Every release since 8.3.0 was just as awful as 8.3.0.  Xcode 9 brings with it some really cool features and a polished and improved UI, none of which matters at all if we have to sit on our backsides for 20 minutes to build an app across Swift and Objective-C, or cmd-clicking a symbol works about 12% of the time, or deleting derived data is still a part of our workflow.  

Xcode 9 is on track to yet again fail to put up table stakes, while hoping to impress with frills and polish (wireless debugging?).  It's absolutely the iTunes of IDEs.  

Xcode 8.3

Update: as of 8.3.3 the crashing issue is mercifully fixed, but all the other issues still remain.

Xcode 8.3 is the worst release since I began using Xcode to build apps for my iPhone 3G almost a decade ago.   Every version of Xcode has had its irritants (6 had a lot more than most), but 8.3 is so problematic that I cannot believe it was ever released.  Xcode makes using the excellent Swift language a frustrating, stressful, and unproductive experience.

Xcode has always been a bit flaky, but its reliability drops off a cliff with any project that has a sizeable number of Objective-C and Swift files.  In my personal projects I have few problems with Xcode, because they're all in pure Swift and don't have many dependencies.  But working on a decent sized project with hundreds of classes split between Obj-C and Swift is an utter nightmare.  It's such a shame: Swift is an delight to write in, while using Xcode is frequently a miserable experience.

Here are the things I find I’m dealing with on a daily basis (and not one of them has been fixed in 8.3.1):

The dreadful compile times. 

 

I am literally writing this blog post to give me something to do while Xcode compiles.  My last build took almost 10 minutes.  That wouldn’t be so bad if it restrained itself to full builds when they were needed, but Xcode will decide to do a full build out of nowhere, even when you’ve changed just a single line.  This turnaround time is unacceptably long.  You hit build, you wait for 30 seconds.  You switch to your browser and read an article.  You come back and it’s still building.  You switch back and continue reading.  You eventually remember you were programming something and switch back to Xcode.   You've completely lost your train of thought.  You spend another ten minutes figuring out what you were doing.  Over the course of a day this costs hours and hours of productive time.   

There are tools to highlight exactly what code is taking so long.  All those tools tell me is that, rather than a handful of glaring issues which I can fix and see 10x improvement, my compile times are being drawn out simply because of the sheer amount of lines of Swift that the compiler is glacially churning through - death by a thousand null coalescing operators.  

This is only one item on the list, but it's the worst.   Take the keyboard smashing frustration of this and multiply it by the following:

Crashes on auto-complete.

On opening a new .swift file and entering pretty much anything, there is a decent chance Xcode will decide to crash.  And crash, and crash, and crash.  Once it has decided to do this, nothing will stop it from crashing on that same line after you open it up again and begin typing.  It's so bad sometimes you literally have to write your code behind comments and then uncomment it, or write it in another text editor and copy it across.  This happens all the time, except when it doesn’t.  It’s crashed so many times for me I could have used the crash reporter to write War and Peace to Apple’s software QA team one word at a time, assuming they have such a team.

Autocomplete/suggest not working anyway, despite trying so hard it brings down Xcode.

After crashing, Xcode then does this:

 

No autocomplete for you until this has finished.  And it might not ever finish, at least not on timescales that humans are attuned to.

Type half a class name, and stare at the blinking cursor.  This is your life now.  At the end of your vigil, you might get suggestions, you might not.  You may no longer code at the speed of thought.  It’s like hillwalking with a very slow partner; constantly having to stop and wait for them and wonder if they’ve actually fallen to their death and will never show up.  And then realising that only they knew the way.

Misc.

CMD click on a method - “MyClass.doThatThing()” - and be confused for several seconds after you’re dropped off in “ACompletelyUnrelatedClass.doThatThing()”. 

Typing anything with the Quick Help sidebar open and it’ll refresh itself on every keystroke, interrupting the UI.  It’s what coding over SSH to the moon would feel like.

Failing with no errors:  During build, the red error icon in the status bar will show up.  Then it'll go away.  Then it'll come up again at the end of the build.  You take a look at the Build Time navigation panel.  Nothing.  No error.  You have to build again, and try to catch the error appearing in the navigator during the ten minute compile process.

An app deleted while running will absolutely devastate Xcode.  It will grieve the loss of that process by keeping it running in spirit.  Click the stop menu and it’s ghost is there, unable to be excised.  You must do the only humane thing and also kill Xcode because its attachment is too strong.  You cannot quit Xcode normally and you cannot restart your Mac because even the OS cannot console Xcode.  The only thing is to put a pillow over its face and whisper ‘shhh… it’ll all be ok when 8.4 comes out…I hope”.

I know the tools team at Apple is working really hard to integrate Swift into the Xcode toolchain

Forge workout app

I've been developing an iPhone app since September 2014, almost a year now, for tracking workouts.  It might seem like another health app in a genre already thoroughly crowded, but this app has a singular focus:  to make entering sets (reps, times etc.) as quick and painless as possible.  Calorie tracking is left to apps like My Fitness Pal.  'Propping' workout mates and bragging your one-rep-max is left to Fitocracy.  This app is the little notebook and pencil you take to the gym in your back pocket, augmented.

Design & Development

The core of the app is the workouts screen, which presents the user with the details of a workout, and a horizontal display of dots along the top representing workouts in history.  The user can swipe horizontally on the workout detail, or on the dots themselves, to page back to previous workouts.

This element allows the user to quickly go back to see a previous workout while they are still entering their current one;  this was the number one feature I wanted in a workout app, because it's one of the main reasons people carry a little book and pencil into the gym with them - to see what they did yesterday/last week.

Another of the centrepieces of the app is the timer and stopwatch.  The three lines of the stopwatch represent seconds, minutes and hours elapsed.  These elements are implemented with a CALayer subclass which automatically animates its start and end angles when they are changed.

The countdown timer has been designed primarily as a 'set' timer.  It has a single countdown path, with 4 preset buttons which can be set to any value by long pressing.  The user can immediately count down from, say, 1 minute by tapping the 1:00 preset.

The preset time button surrounding the timer are custom drawn with any given text.

The preset time button surrounding the timer are custom drawn with any given text.

Graphing

One of the core UI elements of the app is graphs.  There are graphs behind the title of each exercise in a workout to subtly inform the user of their progress.  There is a similar graph on the home screen for weight.  The weight screen itself has a scrollable graph which re-samples its data as the data points scroll in and out of the viewable area, with variable a goal range.

This was all done with CALayers and masking.  Create one layer for your actual graph, one for your goal (with the exact same path as your actual graph path), and a third rectangle path to mask your goal path.

 self.goalLayer.path = self.actualGraphPath;
 self.goalMaskLayer.path = [self rectangleGoalMaskPath].CGPath;
 self.goalLayer.mask = self.goalMaskLayer;

Theming

I wanted the app to provide theme options for the user.  To this end, every single icon and control in the app is drawn in Core Graphics rather than an image, allowing arbitrary tint colour. The actual themes themselves are represented by a Theme object, subclassed from NSManagedObject and persisted in CoreData.  A Theme object holds a number of UIColor objects representing colours of various classes of components in the UI; nav bar background/foreground, main background/foreground, text colour, control colour, graphing background/foreground/goal, workout history dots etc.  When a theme is chosen by the user, a notification is posted; view controllers inherit from a base view controller, which responds to this notification and calls a method to update the appearance of the view controllers view hierarchy.

To give the use an idea of what theme they are selecting, I use the concept of 'outfits' - (an admittedly cheesy metaphor which fits with the premiss of the app), these are little round icons which represent the theme.  These are shown in a grid for the user to peruse, and are drawn in CoreGraphics rather than pre-rendered so that I can create themes on the fly using the app's built-in theme creator, and have them upload to my server for users to enjoy.

Backend/Syncing

I use my old iPhone at the gym, which doesn't have cellular access anymore.   I wanted to be able to track a workout on my non-cellular phone at the gym and when I get home have it sync to my main phone.

I decided to go with Azure Mobile Services for the backend.  I experimented with Facebook's Parse but came to feel (perhaps irrationally) that I don't trust Facebook's support for this framework to the extent that I'll make Parse the basis for all my persisted objects.  In my company we run some of our services on Azure, and it is generally well endorsed by those who implement those services.  

The way the Mobile Services works is that you can query and insert into tables directly from your Objective-C / Swift code with convenience methods built in to the Mobile Services SDK.  This is fine for simple non-relational data (like saving Theme object representations for example).  For more complex data, like saving a workout with its related exercises and metrics, there is the ability to script your own API.  

You can do all of this through the Azure web UI, but this quickly gets painful.  Set up source control and you can edit and push your scripts locally.  This brings me to the one thing which makes working with Azure a bit of a pain for a serious service - there is no simple way to set up a mirror of your environment for staging purposes.  You basically have to create an entire second service and copy across all your tables and scripts.  Once that's done, you can then have two git remotes (staging/production) for your two environments, but as far as I see there is no way to mirror your database schema to your second service.

Status of Forge

It's the beginning of November now, over one year since I started developing Forge in my free time, and I'm happy to say the app has been submitted and is waiting for review on the AppStore.

Effective iOS App Architecture

Most iOS projects are in a bad way when it comes to software design.  I have seen (and had to work on) 5000 line view controllers.  I have seen business logic of a main view controller be split out into categories on that view controller.  I'm not the only one who has opened an AppDelegate.m and had my eyes begin to water. 

I've been using a pattern for a while now which I have found great with both new projects and nightmare legacy codebases.   It is quite similar to VIPER but a bit easier to introduce to existing projects.  It's also easier to grasp just by looking at a project's files; code should be bus-proof (the developers of a project get hit by a bus, and that project gets dumped on you), and I feel that VIPER is overly complicated with bizarre terminology (what are all classes if not 'interactors'?).

If you'd rather see a concrete example of this pattern than read a description of it, then take a look at this barebones example app.

Problems in typical iOS apps to overcome

  1. UIViewControllers are huge, because:

  2. UIViewControllers contain business logic.  Business logic being code which defines what to show the user - like a formatted string based on the price property of some object, or code to determine whether to show a postage label based on multiple properties of a product object.

  3. UIViewControllers contain the delegate and datasource methods for a UITableView or UICollectionView.  Those methods consist of sprawling conditionals determining what to show for a given index path.

A Solution

  1. Each UIViewController has a corresponding view model.

  2. View controllers have an array of section managers, with one section manager for each section of the table/collection view.

  3. View models talk to data providers, which in turn talk to Coredata for persistence, and API clients for getting data from the network.

Here's what the setup should look like:

Role of each class:

API Client:

Calls an API (or logical group of APIs) and parses the response into digestible NSDictionary objects.  This is where you might want to clean up or standardise some crappy API response data for use in your data providers.

Data Provider:

Will fetch data from API client objects and parse them into actual objects and, optionally, deal with persistence (such as Coredata).  A data provider may call on multiple API clients to provide data for some logic portion of your app's functionality.

View Model:  

Contains code which processes and calculates what needs to be shown in your view controller.   It does not contain code which process how to show that data.    View models may be reused between view controllers.

Your object model will be referenced in the view model, and not in your view controller.  Say you have a Product class that represents your company's product.  Instead of having an array of products in your view controller,  you keep that array internally in your view model.  You then have methods on your view model which return data from those products.  Let's say you have a label on your view which will show the price of a product, with or without tax depending on the type of the product.  You might do all this in your view controller.  But this is business logic, which you might reuse somewhere else.  So put it in your view model.   Then have a method on your view model - like priceTextForItemAtIndex That method will do the heavy lifting of determining what text to show for a given product.

If the data your are showing in your view controller is huge (you have 15 sections, with tons of data being presented) you can have sub view models referenced as properties on your view model.  Have a whole section for a product's shop information?  Make another view model just for shop data, and reference it as a property of your main view model.  This also make reuse easier.

View Controller:

Your view controller will be relatively minimal and will serve as the responder to section manager delegate methods, and be the hub for navigation.  It will have an array of Section Managers - one for each section of your table/collection view.

It will conform to UICollectionViewDatasource and UICollectionViewDelegate (or UITableView equivalent).  But instead of implementing those methods and populating cells, calculating cell heights etc., it will pass that functionality through to the appropriate section manager.

The entirety of your cellForItemAtIndexPath will look something like this:

if let managers = self.sectionManagers {

        let sectionManager = managers[indexPath.section]
        return sectionManager.cell(forRow: indexPath.row)

}

Section Manager:

Section managers will be objects which implement methods for:

  1. Returning number of rows.
  2. Returning height for a given row.
  3. Returning a UICollection/TableViewCell (or subclass).  
  4. Responding to tapped cells or other actions inside cells.

A view controller and its section manager will share the same view model.  

There are two options to handle navigation.  One is to have the view controller pass didSelectItemAtIndex through to the section managers, and each section manager will interpret what that tap means, and fire a delegate method on the view controller such as

-(void)sectionManager:(SectionManager *)sectionManager didSelectDoSomthingWithItemAtIndex:(NSInteger)index

and then the view controller would handle the navigation associated with that action.

The other is to have the view controller just respond directly to didSelectItemAtIndex rather than passing it through to section manager.  

The first option is a little more convoluted but it keeps the interpretation of an action in the logical place - the section manager.

Implementing in older projects.

Take it one view controller at a time.  This pattern doesn't need to take over your entire project at once. 

Rather than trying to refactor your existing view controllers in site, create a new one along side your old one.  

  1. If your API fetching/processing takes place in your view controller (you poor soul) then first thing is to write any API client(s) for the APIs you need to hit.  Then create any data providers required for your view controller.
  2. Create your view controller, view model(s), section managers for replacing an existing view controller.
  3. Go through your old view controller with a fine-tooth comb and scrape out any business logic and re-implement it in your view model as logically as possible.  Remember to keep your object model(s) inside your view model and have a method or property for sharing that object's data to the outside.
  4. Take this opportunity to redo your VC's UI with Autolayout if it isn't already.  Flesh out your section managers one section at a time until you have recreated your UI.

Little by little you'll replace all that crappy code with something that is a pleasure to work on.  An advantage of having everything logically separated is that once you set up your scaffolding, other team members can drop in when they have time and contribute easily. 

Why bother

There are a whole bunch of advantages to using this approach. Yes, you will end up with lots of files. But so what?  When you are hunting a crasher in a legacy code base, and you're scrolling through a 5000 line view controller you'll wish that functionality was chopped up in to some semblance of logical pieces, no matter how numerous.

Here are the main advantages I've found with this approach:

  1. View controllers are smaller, and are basically just hubs for responding to delegate methods from the view model and section mangers, and handling navigation.
  2. It's very easy to find the code you're looking for by navigating to the appropriate section manager.
  3. Table/collection view datasource/delegate methods on your view controllers are not massive conditional nightmares.  This is good for your emotional wellbeing.
  4. You can reorder your sections simply by changing the order of your section managers in the array on your view controller.
  5. Adding new features to your view controller no longer means adding complexity.  Your view controller is not going to be any more complex by adding another section.  You will need to add code to your view model, but as mentioned before, you can separate your view model into logical pieces with one main view model and several sub view models as properties on the main one. 
  6. View models can be reused.  
  7. View models, by their nature, lend themselves to be unit testing without you having to keep unit tests in mind while you implement features.
  8. If you have a list-detail type app with edit/save functionality, you can have your model contain your editing state.  Have properties (declared internally in your .m) representing your object's properties.  Those get edited and changed in your UI.  When you save, you copy them to your object and save its context.  If you cancel, you do nothing.

An actual real-world example

I have created a bare-bones app with a couple of view controllers for you to play around with - try it out and see what you think.  You may have ideas on how to improve this pattern please feel free to share it.

Exposing functionality in Swift.

In Objective-C we have our header files to expose the functionality of our classes.  There we lay out the methods and properties that we allow the consumer of the class to use.  Even though we can technically call methods which are not exposed in the header, the header acts as a contract between classes and makes development easier.

In Swift we no longer have header files.  We have a single .swift file.  We can modify access to our classes members with private, internal and public, but having a good clear indication of what you have exposed to external classes is not easy when you have a dozen methods, and a handful of properties with getters and setters. 

So how to have those exposed methods clearly visible on a cursory look into that .swift file?  The way I'm starting to do it is with protocols.

protocol carProtocol {

    var speed : Float { get } //Readonly

    var color : UIColor { get set//Read/write

    func accelerate()

    func decelerate()

}

 

class car : carProtocol {

    var speed : Float;

    var color : UIColor;

    init () {

        self.speed = 1.0

        self.color = UIColor.redColor()

    }

    func accelerate() {  }

    func decelerate() {  }

}

And then to have a car instance as a property on your class, you declare the property as being an object which conforms to carProtocol, and instantiate the object as a car object.

class clientThing {

    let myCar : carProtocol

    init () {

        self.myCar = car()

    }

}

So now to see what you have exposed to other classes, you just glance at the protocol declaration in your class's file.