To celebrate the arrival of iOS 9, we asked a few of our friends to share the most useful thing they learned when looking to update their apps to support this latest version of iOS. Here is some fantastic advice to to get up to speed and make the most of development for iOS 9.
Dave Verwer
@daveverwer — Founder of Curated, Runs iOS Dev Weekly
It’s rare that a new API in the iOS SDK actually helps with app discovery and marketing. So, for me some of the most exciting features in iOS 9 are the new search APIs.
These features build on top of NSUserActivity
which was introduced in iOS 8 for supporting Handoff. With iOS 9 additional metadata and deep link URLs can be added to your app so that it appears in iOS 9’s spotlight search, not only as an app for the user to launch but exposing its content as well.
This is going to be immediately beneficial to your existing users but Apple are also going to index these search results centrally as well. Your app could then appear as a suggestion when users who don’t yet have your app installed use spotlight. Free marketing? Well, maybe.
The interesting thing with this is that Apple are only going to suggest your app to new users when your existing users are regularly using and interacting with the search results you provide. This immediately cuts out spammy apps and focuses on those which provide real value and that’s got to be a good thing for the App Store.
There’s much more to this than there is room for in a quick tip. So, for a deeper look at these APIs and how to implement them you should watch Session 709 — Introducing Search APIs from WWDC. There’s also a handy tool for validating your web site to make sure that iOS can see your app links correctly.
Tim Oliver
@TimOliverAU — Creator of iComics
With the exciting announcement of iPhone 6s and iPhone 6s Plus, developers can now take advantage of enabling their apps with 3D Touch, adding (literally) a whole new dimension to UI interactions!
As expected of Apple, 3D Touch is exposed to developers via a very simple API, at its fundamental level, exposed simply as a new property in UITouch
.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else { return }
if traitCollection.forceTouchCapability == .Available {
println("Touch pressure is \(touch.force), maximum possible force is \(touch.maximumPossibleForce)")
}
}
This new API unlocks a vast amount of potential for new functionality in apps, such as additional control options in games, fine-grained control in drawing apps, or simply even a nicer alternative to the tap-and-hold paradigm we’ve had since the first iOS device.
In addition to the UITouch APIs, Apple has also provided two new sets of classes adding 3D Touch functionality to apps: UIPreviewAction
and UIApplicationShortcutItem
.
UIPreviewAction
allows developers to quickly present content in a new ‘preview’ overlay when you 3D Touch a UI element. This is a fantastic way to allow a quick glance at app-specific content, such as email messages, photos, or even websites without needing to commit to a full view controller transition.
UIApplicationShortcutItem
objects enable an amazing new feature right on the iOS Home screen. When users 3D Touch an app icon, a sheet of options is presented, allowing the user to quickly jump to a specific section of the app, or perform an in-app action.
Overall, the introduction of 3D Touch unlocks a whole new paradigm of iOS device interaction and will enable a new generation of innovation in upcoming iOS apps. Sample code, as well as more general info on 3D Touch itself is available on the 3D Touch page of the Apple Developer site. Good luck!
New in iOS 9.0 and OS X 10.11 are the UILayoutGuide
and NSLayoutGuide
classes, respectively. They allow you to create “view-like” objects that participate in the Auto Layout constraint solver without creating extraneous on-screen views. For example, you can use these new classes instead of creating empty views and constraining their dimensions, etc.
// Create the layout guides.
let layoutGuideA = UILayoutGuide()
let layoutGuideB = UILayoutGuide()
// Add them to the view.
let view: UIView = ...
view.addLayoutGuide(layoutGuideA)
view.addLayoutGuide(layoutGuideB)
// Add constraints using them.
layoutGuideA.heightAnchor.constraintEqualToAnchor(layoutGuideB.heightAnchor).active = true
// You can even set their identifiers...
layoutGuideA.identifier = "layoutGuideA"
layoutGuideB.identifier = "layoutGuideB"
// ...and get their calculated frames (valid once the owning view has been laid out)
print("layoutGuideA.layoutFrame -> \(layoutGuideA.layoutFrame)")
Indragie Karunaratne
@indragie — Mac and iOS Software Engineer, Student
The NSLayoutAnchor
API introduced in iOS 9 not only makes it cleaner to declare constraints, but also makes additional guarantees regarding the correctness of your constraints through the power of static type checking. For example, consider this constraint, created using the old NSLayoutConstraint
API:
NSLayoutConstraint *constraint =
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:view2
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];
This constraint is invalid because you’re constraining an attribute on the X axis (leading) to an attribute on the Y axis (top). However, this will compile without any warnings and will proceed to run and fail silently, leaving your layout in an undefined state and leaving you with a difficult mess to debug, given that nothing is logged to inform you that one of your dozens (or hundreds, or thousands…) of constraints is invalid.
NSLayoutAnchor
solves this issue by leveraging the power of generics in both Swift and Objective-C. The anchor accessors on UIView
expose subclasses of NSLayoutAnchor
that add type information to methods inherited from NSLayoutAnchor
. There are separate subclasses of NSLayoutAnchor
for X axis, Y axis, and dimension (width/height) anchors, since each of those types of anchors can only be constrained to another anchor of the same type. By constraining the anchor parameters in NSLayoutAnchor
’s methods to the same generic type as the receiver, the API uses the type checker to enforce rules for creating valid constraints.
Going back to the prior example, here is the equivalent constraint declared using the NSLayoutAnchor
API:
NSLayoutConstraint *constraint =
[view1.leadingAnchor constraintEqualToAnchor:view2.topAnchor];
Not only is this much more readable than with the old API, this also spits out an “Incompatible pointer type” compiler warning because the compiler knows that you can’t create a constraint using two different anchor types.
For more information, see the NSLayoutAnchor documentation.
Note regarding Swift and Objective-C Generics: At the time of this writing, using Xcode 7 beta 6, Objective-C generics don’t bridge correctly to Swift. This means that the additional type safety mentioned in this tip doesn’t apply if you use this API with Swift, but does work correctly if used with Objective-C, as confirmed by Joe Groff.
Ayaka Nonaka
@ayanonagon — iOS Engineer at Venmo
My top tip for iOS 9 is to drop iOS 7 and start using things like UIAlertController
(iOS 8+) which I love that it enforces that only view controllers can present alerts and action sheets and UIStackView
(iOS 9+) which is a much saner way to think about layouts. Seriously, try it out if you haven’t already. It’s so great. There are open source libraries (PSTAlertController
and TZStackView
) that back port these to previous iOS versions too, so worth using them and swapping them out with the UIKit versions once you’re ready to drop iOS 7 or iOS 8.
Apple introduced App Transport Security in iOS 9, which requires all apps to use HTTPS by default. Because not every asset is served over HTTPS, Apple also provides a way to disable ATS, either selectively or as a whole.
If your app needs to be able to load arbitrary URLs (for example, in a UIWebView), then you probably disabled ATS as a whole, by setting the NSAllowsArbitraryLoads
key to YES
. This is okay, but if you disable ATS as a whole, then you should enable ATS selectively for important domains. You can do this by using the NSExceptionDomains
key. For example, this is part of Workflow’s Info.plist:
You can see that we support users downloading files over HTTP, but we also require HTTPS when connecting to workflow.is (and all of the API domains that Workflow uses).
Also important to note is that ATS is applied on a per-bundle basis. This means that you need to add the ATS dictionary to not only to your main app’s Info.plist, but also to your extensions’ Info.plist files as well.
Jake Marsh
@jakemarsh — Creator of Little Bites of Cocoa
Implement as much of the new search functionality as you can. Search is going to be incredibly important going forward, and iOS 9 is just the beginning. I’d recommend adding NSUserActivity
support to your app as soon as possible. It’s incredibly straightforward, and when you’re done your app will support both Handoff and the new Proactive systems. If your app has any kind of searchable content, you should definitely take advantage of the awesome new Core Spotlight framework and tell the system how to index it all. You should also make sure any web content associated with your app is optimized for iOS 9’s new search results view, Apple has a great guide showing how a few simple meta tags can go a long way here.
The more the system knows about what a user is a doing, and what kind of content they’re creating or interacting with, the more intelligent suggestions and options it can present to them.
Sam Ritchie
@FakeSamRitchie — Chief codesplicer at codesplice
Everyone who’s worked on a shared iOS codebase eventually gets burnt by merge conflicts on a storyboard file, which often results in you manually redoing all your changes in IB. This can get so painful that some teams give up on concurrent storyboard development and implement some variant of the source control shingle.
If, unfortunately, you don’t have any roofing materials handy, the best way to minimize merge conflicts is to break your UI up into smaller storyboards. In the past this has meant you needed to perform your cross-storyboard navigation in code, but with Xcode 7 and iOS 9 you can now do this using a normal segue via a Storyboard Reference. These give you all the benefits of single-storyboard navigation, but let you split up the files for simpler merging.
The quickest & easiest way to start breaking up a monolithic storyboard is to zoom out, shift-select a group of related scenes and select ‘Editor > Refactor to Storyboard…’ (yes, storyboards get refactoring support before Swift). However, this will leave references for every scene and auto-generate unpleasant-looking Storyboard IDs where necessary, so personally I prefer doing a ‘File > Duplicate…’ and just deleting the extra scenes.
If you already have multiple storyboards, give yourself a high-five — you get to delete some code! Drag in a Storyboard Reference from the Object library and configure a segue, then remove your manual navigation code by pounding the delete key with excessive force.
Natasha Murashev
@NatashaTheRobot — iOS Engineer at Capital One, Blogger at Natasha The Robot
I haven’t looked into iOS 9 too much but I have been working with watchOS 2. If you have an Apple Watch app, I recommend re-writing it from scratch using the new Watch Connectivity framework. watchOS 2 is completely different and a lot more powerful than the preceding WatchKit extension. It’s the future, so keeping and maintaining your watch as a WatchKit extension is just adding to your technical debt.
Also, consider adding a Complication component to your Watch App. While currently the hierarchy of use on the Apple Watch is Notifications, Glances, and then App. The future #1 (or at least competing for #1) is going to be Complications — imagine having your app right there, every time the user raises their wrist!
To get started, check out these resources:
Riley Testut
@rileytestut — Creator of GBA4iOS, USC Student
If you’re anything like me, you use early exits in your code to better organize your validation logic. While early exits have been possible in Swift since day one, they brought with them a few caveats. First, they required you to check for the condition you didn’t want (e.g. if variable is nil
), instead of what you do want. More importantly though, in the common case that you were wanting to exit if a variable is nil and continue if it isn’t, you would then have to unwrap the variable manually, following the early exit to continue using the variable for the rest of the scope.
With Swift 2, the Swift team has graced us with a new keyword to help us with early exits: guard
. guard
fixes both issues above. Imagine, for a second, you’re working on a game. Because the developer was too lazy to implement separate callbacks, all input changes are handled through one callback function:
func gameController(gameController: GameControllerType, didActivateInput input: InputType?)
Having a second callback dedicated to handling when inputs are deactivated would be better, but this demonstrates the helpfulness of guards; if the optional input
is non-nil, then a button was pressed, and the game should react. To signal that the button is no longer pressed, input
will be nil. If we only care about when a button is pressed, we could’ve used the following early-exit strategy in Swift 1 to accomplish this:
func gameController(gameController: GameControllerType, didActivateInput input: InputType?)
{
if input == nil
{
return
}
self.performExampleMethodWithNonOptionalInput(input!)
}
Notice that we compare input to the case we don’t want to handle, which in this case is it being a nil value. More importantly though, notice that because input is still an optional, we still have to force-unwrap the value later in the function, even though we know it is non-nil. With Swift 2, this becomes much better:
func gameController(gameController: GameControllerType, didActivateInput input: InputType?)
{
guard let unwrappedInput = input else { return }
// in development, I call the unwrapped variable the same name
// as the variable. However, unwrappedInput demonstrates this better.
self.performExampleMethodWithNonOptionalInput(unwrappedInput)
}
Here, we “guard” the condition that input is non-nil and store it into unwrappedInput
, otherwise we return. Now, we can use unwrappedInput
as a non-optional, and everyone is happy! Thus, guards help make your iOS 9 code cleaner and less error-prone.
Janie Clayton
@RedQueenCoder — iOS/Mac developer, Blogger at Red Queen Coder, Host of NSBrief
My projects are actually a little different than most people’s. I write control software for robots on the Mac. The biggest thing we did to get ready for the release of iOS 9/Swift 2/ El Capitan was updating our error handling for Swift 2. When Swift 1 came out, we wrote and implemented our own error handling methods because we had a lot of problems with NSError.
Since we’re working with hardware, error handling was a really important part of our software because if you don’t handle or anticipate errors properly, it causes lots of physical damage and physical damage is expensive!! So, if I was going to give a tip for iOS 9, I would say if you are using Swift to get familiar with error handling. I know it’s one of those things like unit tests that we know we’re supposed to do and it isn’t fun or flashy, but we don’t control the outside world and it’s important to think about how you want your app to respond when things go wrong.
Glen Low
@pixelglow — Creator of Instaviz, Apple Design Award Winner 2004
To cultivate the Tao of UIKit programming, you need to stop fighting the system, and instead surf with the flow of the framework. Otherwise you’ll be wiped out every time Apple releases a new iOS.
Case in point: UIKit really wants to keep your presented view controllers presented on screen, even adapting them to different screens and orientations, and repositioning the pointy end of popovers.
Work with the system by mostly accepting the adaptive presentation defaults and supplying a new popover location in UIAdaptivePresentationControllerDelegate / UIPopoverPresentationControllerDelegate. Don’t dismiss your view controllers on a size or size class change. These delegates also only offer limited customization: while adapting, it’s easy to swap in a new view controller but it’s hard to actually change the existing view controller e.g. if you wanted to disable a back button.
Otherwise, at best you’ll risk weird behavior while rotating the device or with the new iPad multitasking or the latest Apple magic tech, at worst crash and burn debugging on a sleepless midnight on the eve of that new iOS release.
Thanks for reading! Now go forth and make some amazing apps!
Receive news and updates from Realm straight to your inbox