Skip to content →

Tag: protocols

Text Field Advancing Protocol

In Keyboard Responsive View Controller, I discussed a demo project that presented a UIViewController extension for automatically moving the active text field into view when obscured by the keyboard. Not discussed there was functionality included in that project for advancing the cursor to the next text field when the user taps the Return key. I discuss it now.

By default, when the user taps the Return key on the iOS keyboard (in this project configured as a Next or Done button), nothing happens. By conforming your UIViewController to the TextFieldAdvancing protocol, your controller gains a text field auto-advance functionality. As a result, when the user taps Return, the focus automatically moves to the next field in the sequence. Optionally, if it’s the last field on the view, it will instead segue to a new scene.

This protocol does require your controller be registered as a delegate of each text field, which is easiest done via the storyboard. Just control-drag from the text field to the view controller button at the top of the scene, and select the “delegate” outlet. You can also do this programmatically with myTextField.delegate = self (this functionality is included in the Keyboard Responsive View Controller extension).

@objc protocol TextFieldAdvancing: UITextFieldDelegate {
  var textFieldSegueIdentifier: String? { get }
}

extension UIViewController {

  /// In response to the user tapping the Return key in a text field, calls upon `advanceFirstResponder(from:)`.
  func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    advanceFirstResponder(from: textField)
    return true
  }

  /// Advances first responder to the next text field in the `tag` sequence. For the last text field in the sequence, advances to the next scene via the segue given by `textFieldSegueIdentifier`.
  func advanceFirstResponder(from textField: UITextField) {
    guard let vc = self as? TextFieldAdvancing else { return }
    if let nextTextField = view.viewWithTag(textField.tag + 1) {
      nextTextField.becomeFirstResponder()
    } else {
      if let textFieldSegueIdentifier = vc.textFieldSegueIdentifier {
        performSegue(withIdentifier: textFieldSegueIdentifier, sender: nil)
      }
    }
  }
}

The @objc designation on line 1 is required in order for the Swift protocol’s UITextFieldDelegate method textFieldShouldReturn(_:) to be seen and called by the Objective-C UITextField. Without that, tapping Return has no effect.

Turning to the extension, in a perfect Swift world, this would be a protocol extension, with line 5 reading:

extension TextFieldAdvancing where Self: UIViewController {

However, the UITextFieldDelegate calls are similarly never made.  While it runs, the compiler warns:

Non-@objc method textFieldShouldReturn does not satisfy optional requirement of @objc protocol UITextFieldDelegate.

Matthew Seaman provides a good discussion of this problem, summing up the issue so:

@objc functions may not currently be in protocol extensions. You could create a base class instead, though that’s not an ideal solution.

No, not ideal. Using a base class effectively eliminates the utility of using a protocol in the first place. And because we can’t designate that this extension is of TextFieldAdvancing, we must explicitly test for conformance on line 15.

Let that not detract from the fact, however, that this protocol and extension do work. If you want the user to be able to advance automatically through text fields managed by your view controller, simply conform to TextFieldAdvancing, like so:

class MyViewController: UIViewController, TextFieldAdvancing {
  var textFieldSegueIdentifier: String? = "mySegue"
}

And don’t forget to assign the controller as the delegate of each text field!

Leave a Comment

App State-Change Notifications to Multiple Delegates

In a prior post on Modeling State with Associated Enums, I set up a mechanism for capturing and querying an app’s state, as follows:

AppState.state = .Authentication(.Active)
AppState.state = .DataRetrieval(.Success)
// ...
if AppState.state == .DataRetrieval(.Success) {
  // ...
}

In this post I use the delegate pattern to add state observation by any component of the app that needs to respond to state changes. I begin by defining the delegate protocol:

/**
 An `AppStateDelegate` receives notification when the application's state changes.
 */
protocol AppStateDelegate {

  /// Called when the app state has changed.
  func appStateChanged(state: AppState)

  /// Uniquely identifies the delegate.
  var hashValue: Int { get }
}

The appStateChanged(_:) method will be called any time the state changes (makes sense!). But what about that hashValue on line 11? When it comes to delegates, this is an instance where it is appropriate and, perhaps, necessary to register multiple delegates: there are likely to be multiple places across the app where I need to know about state changes. The hashValue is going to help with that…

My approach for supporting multiple delegates without using NSNotificationCenter is to add to the previously defined AppState enumeration a dictionary of delegates:

enum AppState: Equatable { // excerpt

  /// Delegates registered for `appStateChanged(_:)` notifications.
  private static var delegates = [Int: AppStateDelegate]()
}

(Before getting to the dictionary, note I have chosen to make this and future declarations static. I have done so because, not unreasonably, the state of the app applies to the entire app, and there is only one instance of the app running at a time.)

It is to this AppStateDelegate dictionary that notifications will be made. I have chosen a dictionary here as a meet-me-halfway point between an array and a set. A set would be ideal, but it requires the delegate to conform to the Hashable protocol, something not as easy as it seems, and that I leave for a future exercise. In the meantime, we’ll use the dictionary so as to prevent registration of duplicate delegates.

Delegate registration occurs via the delegate property setter, defined next:

enum AppState: Equatable { // excerpt

  /// Registers the `delegate` with `AppState`.
  /// All delegates are notified upon assignment to `state`. 
  static func addDelegate(delegate: AppStateDelegate) {
    delegates[newValue.hashValue] = newValue
  }
}

The addDelegate(_:) method simply adds the given delegate to the dictionary, relying on the default no-duplicate-keys functionality that comes with a dictionary to ensure no delegate is registered more than once.

A delegate property could be used in lieu of this method, using the setter to make this same dictionary addition, but doing so actually adds a level of unneeded complexity given we’re using multiple delegates: which delegate should the getter return? Using a method avoids that peculiarity.

The dictionary is keyed on the protocol’s hashValue integer. The expectation is any registering delegate itself already conforms to the Hashable protocol (without explicitly requiring such conformance), and thereby has a fitting hashValue implementation that will ensure the uniqueness of the key. If it doesn’t conform, the AppStateDelegate protocol requires a hashValue anyway, so I call it good for now.

With delegates registered, notification is triggered by the following didSet block:

enum AppState: Equatable { // excerpt

  /// The current state of the app. 
  /// Any assigned `AppStateDelegate`s are notified on assignment.
  static var state: AppState = .Ready(false) {
    didSet {
      for delegate in delegates.values {
        delegate.appStateChanged(state)
      }
    }
  }
}

The process of notification is quite simple: call appStateChanged(_:) on each of the delegates in the dictionary. (Note, as explained by the Swift Programming Language Guide, didSet is not called when the value is first initialized.)

Putting all that to use, here is a sample listener implementation that shows registering as a delegate for and receiving app state-change notifications.

import UIKit

class SomeViewController: UIViewController, AppStateDelegate {

  override func viewDidLoad() {
    AppState.delegate = self
  }

  func appStateChanged(state: AppState) {
    if state == .DataRetrieval(.Success) {
      // do something...
    }
  }
}

Note that because a UIViewController already conforms to the Hashable protocol, there is no need to explicitly provide a hashValue implementation. The result? Each time AppState.state is updated, this controller and any other registered delegates are notified via the protocol’s appStateChanged(_:) method.

Leave a Comment

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