App Development

Handling Time Zones in iOS Apps

Time zones are one of those tricky programming problems. It can be difficult to understand what’s going on and maddening to debug, especially if you have customers all over the world.

The key is understanding that the Date() struct in Foundation returns an absolute timestamp in UTC. UTC is a time standard (i.e. no countries adopt it as a time zone—they would use GMT which is a time zone but, for our purposes, is identical to UTC).

Continue Reading →

Reading CSV Files Using the Scanner Class

For the third time in the last couple of years, I’ve found myself reaching for Apple’s Scanner class. Every time I use it I find myself tripping over the deceptively simple API.

This is my attempt to document it in a way that I understand for future reference.

(You’re welcome, future me.)

Continue Reading →

BIS Year-End Self Classification Report

Disclaimer: I am not a lawyer. This is not legal advice. I am not a encryption expert. If you are in doubt, do your own due diligence. I’m confident that “I read it on the internet and they said it was OK” will not be a good defence if the NSA kicks down your door and hauls you away for cyber crimes. I am trying to keep this up to date, but this stuff changes often so it’s worth double checking. If you notice anything wrong or out of date, please let me know. Thanks!

Update January 2023: According to this document on the BIS website, the reporting requirements for mass market items have been changed. My current understanding is that if your app uses any encryption that is included with iOS, or https, then you no longer need to send a report.

Yesterday, after uploading an app for beta testing, I received this message in iTunesConnect for the first time:

If you are making use of ATS or making a call to HTTPS please note that you are required to submit a year-end self classification report to the US government.

Continue Reading →

A New Adventure

Having decided that simply making a living on the App Store by whatever means necessary is not enough, I am embarking on an adventure into games, both figuratively and literally. 

My modest aim is to create something as beloved and impactful as The Secret of Monkey Island.

By myself.

Continue Reading →

MKAnnotationViews and AutoLayout

Having just developed a plist-based design framework that works in Interface Builder, I’m more interested than ever in using xibs and storyboards to design my views.

I also like using AutoLayout. There are significant advantages when it comes to things like labels that make it so much easier to deal with than manually setting frames everywhere in code. Things like Dynamic Type, accessibility, and localisation all become easier and there’s less room for error.

There are some things that do become more complicated with AutoLayout (mostly transforms) but there are well established workarounds for most of these.

I was recently designing a new app that involved using MapKit and I wanted to use AutoLayout to design a subclsss of MKAnnotationView but this isn’t entirely straightforward.

Continue Reading →

Controlling Interface Builder with Plists

I have an appearance manager that reads styles from a plist file and applies then throughout the app through the use of the appearance proxy and through notifications to various custom subclasses of the standard UIKit views and controls.

This works great and allows for of a lot of easy features like different coloured themes or dark modes. The major downside right now is that none of the changes to the plist are reflected in Storyboards or Xib files.

Continue Reading →

Fastlane

Fastlane is mostly pretty great, automating much of the drudgery around releasing apps, but there are issues with Xcode’s UI testing where the simulator will fail to launch with UI Testing Failure - Timeout waiting to launch Target Application. or UI Testing Failure - App state is still not terminated. errors, especially with a large number of UI Tests (Trail Wallet has over 35).

This means that one of the big ticket items of Fastlane, Snapshot, is unreliable with my full test suite. However, I didn’t want to give up on it because the idea of having a process that automatically takes and uploads screenshots to iTunesConnect for all devices across multiple locales based on the current version of the app was just too good to leave behind.

In the end, in order to get reliable results, I ended up duplicating my main scheme and simply removing all other UI Tests, leaving only tests specifically designed for Snapshot. It’s defiling the sanctity of my tests to have tests specifically to take screenshots, of course, but the potential time saving benefits are well worth it and, hopefully, it’s only temporary.

Here’s how I did it:

1. Click the app icon to get access to the Scheme menu, then click ‘Manage schemes…’

Screenshot showing how to access the scheme manager in Xcode

    2. Select your main app scheme, then down in the bottom left click the gear icon and click “Duplicate”

    Screenshot of the scheme options menu in Xcode

    3. Rename your scheme (e.g. <AppName>Snapshot) then click “Edit…”

    4. Click on `Test` in the left hand list, then click the disclosure indicator next to your UI Tests target and deselect everything except your snapshot tests.

    Screenshot showing how to edit the Test target of the new scheme in Xcode

    5. Finally, (assuming Fastlane is set up in your application folder) edit your Snapfile at `[AppDir]/fastlane/Snapfile` and set the scheme to your newly created scheme:

    
    # A list of devices you want to take the screenshots from
     devices([
       "iPhone 6",
        "iPhone 6 Plus",
       "iPhone 5",
        "iPhone 4s",
        "iPad Air"
    ])
    
    languages([
      "en-US"
    ])
    
    # The name of the scheme which contains the UI Tests
    scheme "TrailWalletSnapshot"
    
    

    Snapshot will use this new scheme and only run the relevant tests.

    It’s not ideal, as having Snapshot run through every test and verify they pass on every device before uploading a build is good practice, but given the unreliable nature of the simulator for me at the moment this at least gets me some of the benefits of using Snapshot.

Redefining Symbol Error and Core Data Entities

I have had modules switched off in Xcode for Trail Wallet since its introduction. I have a Core Data entity called “Category” and, when I tried to enable the modules feature (introduced in Xcode 5), I was hit with Redefinition of 'Category' as a different kind of symbol error.

Recently, however, things came to a head when I tried to import a mixed-source framework into Trail Wallet and it refused to see the Swift files.

The solution to getting Swift files in external frameworks to work in Objective-C projects, according to Apple’s Using Swift with Cocoa and Objective-C, is to import it as a module within Objective-C (e.g. @import ExternalFramework;).

So I assumed I was finally going to have to tackle entity renaming, which is handled by Core Data’s lightweight migration but only if it is set up correctly.

I generally dislike database migrations and try to avoid doing them if I can. I’ve never had an issue with a production version and migration (probably because I’m so paranoid about it), but it’s always nerve-wracking when a new version with a migration goes out into the world.

Previous searches had yielded no good news but I thought I should search one last time, which revealed this answer at Stack Overflow—I could avoid a migration after all.

The SO answer is a little light on details, so here are the exact steps I took:

1. Select the .xcdatamodeld file, then select the entity that’s causing the conflict (Category, in my case)

2. Select the Data Model inspector in the Utilities pane.

3. Rename the Class property:

A screenshot showing where to set the class name for a Core Data entity.

4. Re-generate the NSManagedObject subclasses by going to Editor -> Create NSManagedObject Subclass....

Xcode 7’s NSManagedObject subclasses work a little differently than they used to—it generates its own category that it is then free to overwrite should you change the entity in future versions, and leaves the core class for you to add additional methods and properties to.

5. If you have added additional functionality to the NSManagedObject subclasses, you’ll need to copy it across.

I had a category on my Category entity (of course I did) called Category+GetCategories.[h/m], so I had to copy out all of the API and implementation from these files and put it all in the newly generated CDCategory.[h/m] files.

6. Delete the old classes.

7. Update all references to the old class and replace them with the new class, in my case I had to replace Category and with CDCategory throughout the project.

It all worked just fine, no database migration or entity renaming required.

The only other gotcha was that, after attempting to run with the new framework, it was crashing out with a dyld: Library not loaded: @rpath/libswiftCore.dylib error.

This is caused by the build setting Embedded Content Contains Swift Code being set to No. Switching this to Yes allowed the project to run and use the Swift code in the framework.

Useful CGFloat Extensions

I’ve been working a lot with CALayers over the past few days, specifically layers with circular shapes, and so these two little extensions have been really useful.

extension CGFloat {  
    func degreesToRads() -> CGFloat {  
        let rads = self * CGFloat(M_PI / 180 )  
        return rads  
    }
    func positionOnCircleInRect(rect : CGRect) -> CGPoint {  
        let rads =  self.degreesToRads() - CGFloat( M_PI / 2)

        // Assume square  
        let x = rect.size.width / 2 * CGFloat(cos(rads))  
        let y = rect.size.height / 2 * CGFloat(sin(rads))  
        return CGPointMake(x + (rect.size.height / 2) + rect.origin.x, y + (rect.size.height / 2) + rect.origin.x)  
    }
}   

The first is your standard degrees to radians conversion function that every programmer that’s worked with UI has probably written at some point.

The second is a bit more interesting, though, as it finds a point on a circle within a frame of a given size (it also assumes that 0 is the top of the circle, not the right edge as in Core Animation):

let rectForCircle = CGRect(x: 0, y: 0, width: 50, height: 50 )  
let point = CGFloat(90).positionOnCircleInRect(rectForCircle) 

// Returns x 50, y 25—or the point at 90º on a circle