Skip to content →

Month: March 2016

Protocols and Mutability

Apple writes in their Swift Programming Langauge Guide (v2.2):

If you define a protocol instance method requirement that is intended to mutate instances of any type that adopts the protocol, mark the method with the mutating keyword as part of the protocol’s definition. This enables structures and enumerations to adopt the protocol and satisfy that method requirement.

This makes sense in the context of their example protocol that defines a mutating method:

protocol Togglable {
  mutating func toggle()
}

But what of a protocol for which there is no foreknowledge or expectation of mutability, such as for a delegate that is notified when a parsing event occurs? Consider this source:

protocol XMLParserDelegate {
  func didParseXMLElement(element: XMLElement)
}

struct XMLDocument: XMLParserDelegate {

  var elements = [XMLElement]()

  func didParseXMLElement(element: XMLElement) {
    // Non-mutating code here
  }

  mutating func addElement(element: XMLElement) {
    elements.append(element)
  }
}

In this case, the XMLParserDelegate‘s didParseXMLElement(_:) does not have an expectation—as the aforementioned toggle() method does—that a conforming type will modify its own state. Thus it does not claim to be mutating.

As written above, the compiler does not complain. But as soon I declare didParseXMLElement(_:) as mutating, it does.

mutating func didParseXMLElement(element: XMLElement) {
  addElement(element)
}

Making the method mutating means it no longer conforms to the XMLParserDelegate method, where it is not declared such, and so Xcode informs: “Type ‘XMLDocument’ does not conform to protocol ‘XMLParserDelegate’.”

This restriction doesn’t seem reasonable. Why should a protocol be responsible for whether an adopting struct or enum is mutating? Is it not sufficient for that type to use the mutating specifier as it would be otherwise required to do?

One work-around is to declare the protocol method mutating—even though its name carries with it no suggestion of mutation—just in case an adopting type needs to perform a mutating operation. That doesn’t seem quite right, either.

So the best work-around appears be to make XMLDocument a class, thus eliminating the need for the mutating specifier in the first place:

class XMLDocument: XMLParserDelegate {

  var elements = [XMLElement]()

  func didParseXMLElement(element: XMLElement) {
    addElement(element)
  }

  func addElement(element: XMLElement) {
    elements.append(element)
  }
}

For a type that for all other intents and purposes need only be a structure, this is an unfortunate “promotion”.

Leave a Comment

Portrait Orientation: Single Scene

Here’s a slight modification to my prior post, Portrait Orientation: Multiple Scenes. Instead of presenting a set of storyboard scenes in portrait mode for compact-width layouts (iPhone, iPod), you want to present just one of them in portrait.

Override the supportedInterfaceOrientations() in the scene’s root navigation controller (named here as PortraitNavigationController) as so:

class PortraitNavigationController: UINavigationController {
  override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return visibleViewController?.supportedInterfaceOrientations() ?? .All
  }
}

Then in a custom UIViewController subclass for the individual scenes you wish to keep in portrait orientation, override the following method in the view controller:

class PortraitViewController: UIViewController {
  override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return .Portrait
  }
}

By default, it’s only the navigation controller that gets asked, “What orientations do you support?” Our subclass forwards that question to the visible scene within the same storyboard. Because the controller for that visible view may not override the supportedInterfaceOrientations() method, nil is a valid response. In that case, we assume .All orientations are supported.

Activate your custom view controller via the “Custom Class” attribute in the desired Interface Builder scenes as you did previously for the navigation controller. This will trigger calls to those scene’s supportedInterfaceOrientations() method. Run it, and you’ll see the respective views will not animate to landscape orientation.

Assigning a Custom View Controller
Assigning a Custom View Controller

But you may see your scene isn’t always in portrait mode. For instance, if you navigate to the view from any preceding or subsequent view controller that is not similarly constrained and is itself in a landscape orientation, you will then see this controller also remains in a landscape orientation. It’s not until you rotate the device back to portrait that it again locks to a portrait orientation. This is likely to be undesirable.

To fix this, automatically force the view to return to portrait by adding this UIDevice call to the viewDidAppear(animated:) method of your view controller:

override func viewDidAppear(animated: Bool) {
  UIDevice.currentDevice().setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation")
}

Now that’s it.

Note. As noted in the prior post, this is technically a solution for iOS 6 and 7, but not iOS 8 and beyond. Also, you may notice that on the iPad the navigation bar animation is a bit unlike the others when transitioning back to the portrait-only view. These two factors leave me searching for a superior solution. Can you help?

Leave a Comment

Portrait Orientation: Multiple Scenes

Here’s a simple scenario. You need to present a set of storyboard scenes in portrait mode for compact-width layouts (iPhone, iPod). These scenes share a root navigation controller.

Create a custom subclass of UINavigationController and override the supportedInterfaceOrientations() method, like so:

class PortraitNavigationController: UINavigationController {
  override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return .Portrait
  }
}

Then in the storyboard’s navigation controller, set the “Custom Class” in Interface Builder to your subclassed UINavigationController.

PortraitNavigationController
Assigning a Custom Navigation Controller

That’s it.

On the compact-width devices (the iPhone and iPod), this will prevent a landscape orientation on all scenes presented with that navigation controller’s stack. For the iPad, either orientation will still be supported.

Note. This is technically a solution for iOS 6 and iOS 7, for Apple tells us that “as of iOS 8, all rotation-related methods are deprecated,” although this approach continues to work in iOS 8 and iOS 9 and the documentation for supportedInterfaceOrientations() makes no mention of deprecation. See “Handling View Rotations” in the UIViewController Class Reference for more information. Do you have a pure iOS 9 solution?

Leave a Comment

Inline Code Coverage Metrics

Well this was a timely find! My teammates and I were just sitting around the big screen this past week doing a code review and wondering what those numbers were along the right margin of the Xcode source view.

Code coverage in Xcode
Xcode Code Coverage (BigNerdRanch.com)

Mark Dalrymple explains:

Lines of code that were run during the test are displayed normally. Lines of code that never got run are given a shaded background. The gutter on the right-hand side of the source editor shows how many times that particular line of code was run during the test.

Mark also shares a cool tip for preventing the loading of your UI when running tests, by adding this line to your AppDelegate’s application(_: didFinishLaunchingWithOptions:) method just before your main storyboard is loaded:

if NSClassFromString(”XCTestCase”) { 
  return true 
}
Leave a Comment

Xcode Navigation HUD

Thanks to the WWDC 2015 video, Implementing UI Designs in Interface Builder, I learned, among other things, not only that Xcode supports tabs with different view configurations and that you can have multiple assistant editors open in each tab, but also that there’s a pretty nifty tip for quickly placing a file in any of those tabs, and primary or assistant editors!

Just option-shift-click on the file, and up pops this HUD…

image
Xcode Navigation HUD: Option-Shift-Click

Then select which view you want to place it in, or first add a new tab or assistant editor, and then hit return. Simple!

Leave a Comment

Container Views

I recently developed a passcode view with core functionality that allows a user to tap in a 6-digit passcode. I then added functionality to support two different modes of operation: new passcode entry and existing passcode entry. I had a single view controller that managed these two modes of operation. The mode was ultimately determined by two different calling view controllers that segued to it.

And it was just too complicated for my tastes.

So I set out this weekend to break it up. I ended up with three controllers: one for the basic passcode-entry functionality, a second for the new-passcode additions, and a third for the existing-passcode additions.

But how to handle this in Interface Builder? Thanks to Xcode 7 and the help of Mike Woelmer, container views was the answer.

Screen Shot 2016-03-05 at 10.29.51 PM
Define Once, Use Twice

As illustrated above, I simply added two additional view controllers to the storyboard, one for each mode of operation, set their Custom Class to one of the new UIViewController subclasses I’d just created, and filled their view with a Container View. Then I embedded the single passcode view controller into each by control-dragging from the container to the passcode controller.

Leave a Comment