iOS Development

What's New in Bug Trackr 3.0.2

Bug Trackr 3.0.2 is now available on the App Store. While this update includes some important bug fixes, it also includes a new sync progress bar.

Located in settings, the sync status bar will appear while a sync session is in progress. It's a small addition but handy when you want to know what's happening behind the scenes.

(Speaking of behind the scenes, this version includes a few more classes rewritten in Swift. While Bug Trackr was already about 10% Swift code, new features and areas of the app that require significant change will be written entirely in Swift.)

Developing for iOS 9: Supporting Dynamic Type and Responding to User Changes

Dynamic Type was introduced with iOS 7 as a way for developers to abstract font details like typeface, size, weight, and kerning into a series of pre-defined font categories called Text Styles. 

In this post, you'll learn how to support Dynamic Type in your app including how to properly respond to user text size changes.

May 2016 News

I've just finished work on Screenshot++ 2.3 and Screenshot Sweeper 1.2. Both of these updates include enhancements to VoiceOver and Accessibility, keyboard shortcuts, and a refreshed UI.

Screenshot++ 2.3 is a sizeable update and includes a slew new features that I think you'll love. More info to come when the 2.3 update launches on May 12th. A big thank you to my wonderful beta testers for their hard work over the last 2 month for making it a great release!

My plan is to return to working on iSource 3. It's a massive new app/update that has taken a considerable amount of work. While it's been delayed past this school year, it'll be worth the wait. It's on schedule to release before the new school year in September. The first iSource 3 beta will be available in June and I'm looking for students and testers who are interested in testing and providing feedback. If this sounds like you, please get in touch!

- Wes Dyson

Developing for CloudKit - Part 4

Read Part 1 in series on Developing for CloudKit

Read Part 2 in series on Developing for CloudKit

Read Part 3 in series on Developing for CloudKit

In the first 3 parts of Developing for CloudKit, I walk through creating a sync layer for Core Data that uses CloudKit as its backend.

I mentioned at the end of part 3 that I wanted to “abstract the sync layer so it's independent of your data model. I hope to open source it at some point in 2015”.

In the past year, I improved and made many changes to the sync layer (called SSCloudManager) and it shipped as the syncing mechanism in Screenshot++.

The main issue is SSCloudManager is still tightly coupled to the Screenshot++ data model. Creating a version of SSCloudManager that’s data model agnostic would require substantial time to invest that I simply do not have. Because of that, I began searching for a SSCloudManager replacement that didn’t involve months of my time that I would rather spend elsewhere.

Currently, I’m using Ensembles 2 as a replacement for SSCloudManager in development of a few projects. It’s everything I envisioned SSCloudManager to be and then some. I love it.

I’ll be posting in future about adopting Ensembles 2 into my projects. In the mean time, if you’ve created your own sync layer, I would love to hear about it.

Sunset for Parse

Allen Pike’s piece on the Parse shutdown is insightful, in-depth, and a great read. Marco’s response adds his thoughtful perspective.


In early 2013, I began looking for a syncing service to use in multiple iOS apps. I was one in a long list of developers who viewed sync as an add on, that sync should be as easy as adding another project dependancy. Parse added much of the functionality I was looking for. It was easy, cheap, and the company seemed to understand the needs of their customers.

It wasn’t long after I began testing Parse integration in a sample project that news broke of the Facebook acquisition. Parse promised that with Facebook’s backing, Parse would be able to grow faster and not worry about monetization. All in the name of good will for developers.

While I applauded their enthusiasm, the acquisition meant that from that day forward, Parse would be acting in Facebook’s best interests, leaving developers in second place.

Because I have zero faith in Facebook, I could not have faith in Parse as a product or a company and began exploring alternatives.

That decision paid off this week. Instead of scrambling to figure out a new backend as a service (BaaS) and migration strategy, I can instead use that time and effort to continue building new apps and making improvements to existing products.

The Parse shutdown has brought the BaaS debate back to the forefront in the developer community. That is, should developers roll their own BaaS or migrate to a Parse alternative like CloudKit?

Instead of relying on Parse, I jumped into Apple’s CloudKit over a year ago. It’s baked into Apple’s platforms and (for now) has Apple’s support. I use CloudKit as a back-end for Screenshot++ and in unannounced projects.

Allen and Marco lay out the two sides of this debate quite well. For many developers who have experience in running their own servers, this has always an easy decision. Experienced backend developers like Marco haven’t had to rely on 3rd party solutions. For them, it’s relatively easy to roll their own solution and today they’re in the clear; it’s hard not to hear the smug comments coming from atop their high horses.

For the rest of us, Parse was the solution to a problem that few app developers have the experience or resources to properly solve. I don’t view Parse as a shortcut and I don’t think the objective should be to rely on a service like Parse until you can create something better in-house.

For the 600,000 apps that used Parse, they’re in a tight spot and most of those will end up abandonware if they’re not already. To insinuate their developers should have all created custom backends is out of touch with today’s App Store economic reality.

With Parse out of the picture and the app BaaS market underserved, now is the perfect time for a new competitor to fill the void.

Trials with UISplitViewController (iOS 8 and later)

I'm currently writing an updated version of a popular app that will take advantage of split views on iPhone and iPad.

With iOS 8, Apple made considerable changes to UISplitViewController (details can be found at developer.apple.com). It now respects size classes and presents master and detail view controllers at the same time on iPhone 6/6S plus.

I ran into an issue where, on the iPhone 6 Plus, if you ran the app in portrait, it would open to the detail view. This isn't want I wanted (and don't think this is what other developers expect to be default behaviour). When I began implementing the new UISplitViewController delegate methods, I put them into the app delegate until I had a better place to put them and thought nothing of it.

The problem lies in placing view controller delegate methods in an app delegate - they weren't called until the app's UI was visible and responsive.

The solution (sort of) is to place UISplitViewController's delegate methods within a subclass of UISplitViewController. It's ugly, it breaks Cocoa conventions - I hate it. It's also the only thing that seems to work. This cannot be Apple's intended implementation, can it?

For those who want a more detailed explanation, the method below wasn't being called until after UIKit had presented its stack.

- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {

 if ([secondaryViewController isKindOfClass:[UINavigationController class]]
&& [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[GenericResourceViewController class]]
&& ([(GenericResourceViewController *)[(UINavigationController *)secondaryViewController topViewController] resourceLoaded] == NO)) {
return YES;

} else if ([secondaryViewController isKindOfClass:[GenericResourceViewController class]]) {
return YES;
}

return NO;
}

This methods asks the delegate how it wants to fold the detail (right) view controller stack into the master (left) view controller stack.

When UISplitViewController is created in a storyboard, much of the work is done for you including creating the segues that connect the splitViewController to the master and detail view controllers. The above code tells the split view controller to make the master visible when collapsed (in portrait) if the detail's data isn't initialized. After moving this and other delegate methods to the splitViewController itself, it's now called when the splitViewController is laying out its subviews for the first time.

Ideally, I would like to move the delegate methods out of the splitViewController for reusability reasons, but what should be responsible for them? I don't want to maintain multiple UISplitViewController subclasses, especially if they just contain delegate methods. I'll look at creating dedicated controllers to place these delegate methods. The problem lies in where these are initialized. I can't create them in the app delegate or Ill be right back to square one. I had previously tried including the delegate methods in the master view controller with the same results.

If you have suggestions, please let me know on twitter @wesleydyson or in the comments.

Screenshot++ - Implementing Peek and Pop and Home Screen Quick Actions for iPhone 6s

Screenshot++DualiPhone6Promo.png

Screenshot++ 2.1 is about to enter beta testing and includes support for Peek and Pop and Home Screen Quick Actions for iPhone 6s and iPhone 6s Plus.

This is the first app I've updated to take advantage of these new features and I think they're awesome. Peeking is fast and fluid and a definite time saver for users.

Both of these new 6s features are relatively easy to implement if you've been using NSUserActivity and either table or collection views in your app. For a guide on implementing Peek and Pop, check out Apple's UIViewControllerPreviewing API sample code. For Home Screen Quick Actions, I used 3D-touch on Github to get started.

For Peeking into a UICollectionViewCell, set the sourceRect property on UIViewControllerPreviewing to that of the selected cell (I assume this is the same for UITableViewCell but I haven't tried Peek with a tableView yet.) Something like this:

UICollectionView *cv = self.collectionView;
UICollectionViewLayoutAttributes *attributes = [cv layoutAttributesForItemAtIndexPath:_openScreenshotCellIndexPath];

CGRect cellRect = attributes.frame;
[previewingContext setSourceRect:cellRect];
_assetViewController.preferredContentSize = CGSizeMake(asset.pixelWidth, asset.pixelHeight);

If you have any questions leave a comment or find me @wesleydyson on Twitter. Check out Screenshot++ and join the beta!

Developing for iOS 9 - canOpenURL: changes

I ran into an issue earlier this evening when my new iOS 9 app was sending me to twitter.com instead of opening the Twitter app. There doesn't seem to be much mention about it in either the WWDC sessions or online.

This used be perfectly safe code (pardon my obj-c):

if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"twitter://user?screen_name=screenshot_app"]]) {
[[UIApplication sharedApplication] openURL: [NSURL URLWithString:@"twitter://user?screen_name=screenshot_app"]];
} else {
[[UIApplication sharedApplication] openURL: [NSURL URLWithString:@"http://www.twitter.com/screenshot_app"]];
}

In fact, it still is with one gotcha via use your loaf. Apps linked against iOS 9.0 and later will have to white list app schemes they want to use with a new LSApplicationQueriesSchemes key in their Info.plist (That key maps to an array of strings).

In theory, this new block will stop apps from building a list of apps already installed on a device by querying through a lost of known URL schemes using canOpenURL. Presumably, if you can't explain why you're including over 100 schemes in LSApplicationQueriesSchemes, you'll be rejected during app review.

iOS 8 apps running on iOS 9 will apparently work as expected until they cross a 50 scheme threshold at which point canOpenURL: will return an error.

When updating to iOS 9, be sure to add LSApplicationQueriesSchemes to your Info.plist. A 'Find in Workspace...' for canOpenURL will show every instance where you're calling a url scheme.