Categories
Programming

Looking for a great conference on iOS development? Check out RWDevCon!

If you’re interested in iOS development and are looking for a conference to attend next year, I highly recommend RWDevCon, the all-tutorial, mostly-iOS conference run by the fine people at the tutorial site RayWenderlich.com!

It takes place during April 5 through 7, 2018 in Alexandria, Virginia, and will feature…

…four in-depth workshops…

  1. Swift algorithms: build your own collection type, and while doing so, dive into the semantics, performance, and expectations of each Swift collection protocol. Then you’ll explore ways to write your code that takes advantage of this new knowledge.
  2. Machine learning: A hands-on workshop where you’ll harness CoreML and Vision framework and find out what machine learning is, train a model, and then integrate it into an app.
  3. Practical instruments: Finally learn how to use Xcode’s instruments to see how you apps works, find out where the bottlenecks are, and boost your app’s performance.
  4. And finally, the workshop I’m giving: ARKit — where you’ll learn about the features of Apple’s ARKit augmented reality framework, harness data from the camera and your users’ motions, present information and draw images over real-world scenes, and make the world your View Controller!

…and all these presentations…

  • Living Style Guides
  • Swift 4 Serialization
  • Architecting Modules
  • Cloning Netflix: Surely it Can’t be That Hard
  • Auto Layout Best Practices
  • Clean Architecture on iOS
  • The Game of Life
  • Android for iOS Developers
  • The Art of the Chart
  • Spring Cleaning Your App
  • Improving App Quality with Test Driven Development
  • Advanced WKWebView
  • Clean Architecture on Android
  • Getting Started with ARKit (that’s the one I’m giving!)
  • Custom Views
  • App Development Workflow
  • Integrating Metal Shaders with SceneKit
  • Xcode Tips & Tricks
  • Advanced Unidirectional Architecture
  • Embracing the Different
  • Lessons from the App Store

…and a party every night…

…all in a great venue:

Want to find out more? Visit RWDevCon.com!

Categories
Programming

Why “?:” is called Kotlin’s “Elvis operator”

At Victoria Gonda’s presentation on Kotlin at DevFest Florida 2017, she talked about many of Kotlin’s language features. (Be sure to check out the slides from her presentation, Kotlin Uncovered!)

When she got to the “Elvis operator”?: — there were murmurs in the crowd, and I could hear people whispering “why’s it called that?”. Hopefully, the photo above answers the question: it looks like an emoticon for Elvis.

The more formal name for the Elvis operator is the null coalescing operator, and it’s a binary operator that does the following:

  • It returns the first operand if it’s non-null,
  • otherwise, it returns the second operand.

It’s far more elegant to write

val result = value1 ?: value2

than

if (value1 != null) {
  result = value1
} else {
  result = value2
}

And in case you iOS developers were wondering, Swift has a null coalescing operator: it’s ??.

Categories
Humor Programming

Demonstrating map, filter, and reduce in Swift using food emoji

In my last article, I posted this graphic, which uses emoji to make it easier to understand what the map, filter, and reduce functions do:

map filter reduce in emoji

Since then, I’ve been asked by a couple of friends if what’s in the graphic is just pseudocode or if it could actually be implemented. I told them it was the latter, and here’s my implementation in Swift:

// Map

func cook(_ item: String) -> String {
  let cookupTable = [
    "🐮": "🍔", // Cow face -> burger
    "🐄": "🍔", // Cow -> burger
    "🐂": "🍖", // Ox -> meat on bone
    "🐷": "🍖", // Pig face -> meat on bone
    "🐽": "🍖", // Pig nose -> meat on bone
    "🐖": "🍖", // Pig -> meat on bone
    "🐑": "🍖", // Sheep -> meat on bone
    "🐐": "🍖", // Goat -> meat on bone
    "🐔": "🍗", // Chicken -> poultry leg
    "🦃": "🍗", // Turkey -> poultry leg
    "🐸": "🍗", // Frog  -> poultry leg (no frog leg emoji...yet)
    "🐟": "🍣", // Fish -> sushi
    "🐠": "🍣", // Tropical fish -> sushi
    "🐡": "🍣", // Blowfish -> sushi
    "🐙": "🍣", // Octopus -> sushi
    "🍠": "🍟", // (Sweet) potato -> French fries
    "🌽": "🍿", // Corn -> popcorn
    "🌾": "🍚", // Rice -> cooked rice
    "🍓": "🍰", // Strawberry -> shortcake
    "🍂": "🍵", // Dried leaves -> tea
  ]
  if let cookedFood = cookupTable[item] {
    return cookedFood
  }
  else {
    return "🍽" // Empty plate
  }
}

let cookedFood = ( ["🐮", "🍠", "⚽️", "🐔", "🌽"].map { cook($0) } )
// cookedFood == ["🍔", "🍟", "🍽", "🍗", "🍿"]


// Filter

func isVegetarian(_ item: String) -> Bool {
  let vegetarianDishes = Set([
    "🍟", // French fries
    "🍿", // Popcorn
    "🍚", // Cooked rice
    "🍰", // Shortcake
    "🍵", // Tea
  ])
  return vegetarianDishes.contains(item)
}

let meatFree = ["🍔", "🍖", "🍟", "🍽", "🍗", "🍿", "🍰"].filter { isVegetarian($0) }
// meatFree == ["🍟", "🍿", "🍰"]


// Reduce

func eat(_ previous: String, _ current: String) -> String {
  let qualifyingFood = Set([
    "🍔", // Burger
    "🍖", // Meat on bone
    "🍗", // Poultry leg
    "🍣", // Sushi
    "🍟", // French fries
    "🍿", // Popcorn
    "🍚", // Cooked rice
    "🍰", // Shortcake
  ])
  if (previous == "" || previous == "💩") && qualifyingFood.contains(current) {
    return "💩" // Poop
  }
  else {
    return ""
  }
}

let aftermath = ["🍔", "🍟", "🍗", "🍿"].reduce("", combine: eat)
// aftermath == "💩"

I put this into a Swift playground, which you can copy from this Gist or download here.

Categories
Programming

How to program an iOS text field that takes only numeric input or specific characters with a maximum length [Updated]

Check out this update (May 24, 2016)!

update

The material in this article is still applicable, but you’ll also want to read a newer one titled A better way to program iOS text fields that have maximum lengths and accept or reject specific characters, which shows you how to make text fields that let you specify the following in Interface Builder or using less code:

  • The maximum number of characters that a text field will accept
  • The only characters that can be entered into a text field
  • The only characters that can’t be entered into a text field

I’m already using that material in a couple of projects, and I think you’ll find it handy, too. Check it out!

 

 

 

And now, the original article…

Constraining text fields

constrained text fields demo app

Click the image to see it at full size.

Update, August 26, 2015: I’ve updated this article so that its code works with Swift 2. It compiles under the latest version of Xcode 7, beta 6.

swift kickA little while back, I published an article that covered constraining text fields so that they accepted only values that evaluated to numeric ones, and limited them to a specified maximum number of characters (don’t bother looking for it; it redirects to this article now). This article expands and improves on it by showing you how to create iOS text fields that:

  • accept only numeric values,
  • accept only characters that appear in a specified string,
  • accept any characters except those that appear in a specified string, and
  • combine any of the features listed above

zip file iconIn order to demonstrate this, I’ve created a quick sample app, ConstrainedTextFieldDemo. You can download it here [90K Xcode project and associated files, zipped]. When you run it, you’ll the screen pictured above. It contains a set of text fields, each one with its own set of constraints:

  1. A text field that accepts only vowel characters (upper- and lowercase), and no more than 6 of them.
  2. A text field that accepts any character except vowels, and no more than 8 of them.
  3. A text field that accepts digits only, and no more than 3 of them.
  4. A text field that accepts only numeric values, as long as they’re 7 characters or fewer in length. Note that this field allows numbers in scientific notation.
  5. A text field that accepts only positive integers up to 5 characters in length.

In this article, I’ll walk you through the app and show you how to create your own constrained text fields in iOS. Better still, I’ll give you the project files so that you can experiment with the app.

Cut to the code (and the storyboard, too)!

Before we get into the explanations, let me cut to the chase and just give you the code.

For the purposes of discussing constrained text fields, we need to consider only two files:

  1. The view controller, ViewController.swift, and
  2. a set of string utility methods contained in StringUtils.swift.

Here’s ViewController.swift:

//
//  ViewController.swift
//

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

  // MARK: Outlets
  
  @IBOutlet weak var vowelsOnlyTextField: UITextField!
  @IBOutlet weak var noVowelsTextField: UITextField!
  @IBOutlet weak var digitsOnlyTextField: UITextField!
  @IBOutlet weak var numericOnlyTextField: UITextField!
  @IBOutlet weak var positiveIntegersOnlyTextField: UITextField!
  
  
  // MARK: View events and related methods
  
  override func viewDidLoad() {
    super.viewDidLoad()
    initializeTextFields()
  }

  // Designate this class as the text fields' delegate
  // and set their keyboards while we're at it.
  func initializeTextFields() {
    vowelsOnlyTextField.delegate = self
    vowelsOnlyTextField.keyboardType = UIKeyboardType.ASCIICapable

    noVowelsTextField.delegate = self
    noVowelsTextField.keyboardType = UIKeyboardType.ASCIICapable
    
    digitsOnlyTextField.delegate = self
    digitsOnlyTextField.keyboardType = UIKeyboardType.NumberPad
    
    numericOnlyTextField.delegate = self
    numericOnlyTextField.keyboardType = UIKeyboardType.NumbersAndPunctuation
    
    positiveIntegersOnlyTextField.delegate = self
    positiveIntegersOnlyTextField.keyboardType = UIKeyboardType.DecimalPad
  }
  
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
  // Tap outside a text field to dismiss the keyboard
  // ------------------------------------------------
  // By changing the underlying class of the view from UIView to UIControl,
  // the view can respond to events, including Touch Down, which is
  // wired to this method.
  @IBAction func userTappedBackground(sender: AnyObject) {
    view.endEditing(true)
  }
  
  
  // MARK: UITextFieldDelegate events and related methods
  
  func textField(textField: UITextField,
                 shouldChangeCharactersInRange range: NSRange,
                 replacementString string: String)
       -> Bool
  {
    // We ignore any change that doesn't add characters to the text field.
    // These changes are things like character deletions and cuts, as well
    // as moving the insertion point.
    //
    // We still return true to allow the change to take place.
    if string.characters.count == 0 {
      return true
    }
    
    // Check to see if the text field's contents still fit the constraints
    // with the new content added to it.
    // If the contents still fit the constraints, allow the change
    // by returning true; otherwise disallow the change by returning false.
    let currentText = textField.text ?? ""
    let prospectiveText = (currentText as NSString).stringByReplacingCharactersInRange(range, withString: string)
  
    switch textField {
    
      // Allow only upper- and lower-case vowels in this field,
      // and limit its contents to a maximum of 6 characters.
      case vowelsOnlyTextField:
        return prospectiveText.containsOnlyCharactersIn("aeiouAEIOU") &&
               prospectiveText.characters.count <= 6

      // Allow any characters EXCEPT upper- and lower-case vowels in this field,
      // and limit its contents to a maximum of 8 characters.
      case noVowelsTextField:
        return prospectiveText.doesNotContainCharactersIn("aeiouAEIOU") &&
               prospectiveText.characters.count <= 8
        
      // Allow only digits in this field, 
      // and limit its contents to a maximum of 3 characters.
      case digitsOnlyTextField:
        return prospectiveText.containsOnlyCharactersIn("0123456789") &&
               prospectiveText.characters.count <= 3
        
      // Allow only values that evaluate to proper numeric values in this field,
      // and limit its contents to a maximum of 7 characters.
      case numericOnlyTextField:
        return prospectiveText.isNumeric() &&
               prospectiveText.characters.count <= 7
        
      // In this field, allow only values that evalulate to proper numeric values and
      // do not contain the "-" and "e" characters, nor the decimal separator character
      // for the current locale. Limit its contents to a maximum of 5 characters.
      case positiveIntegersOnlyTextField:
        let decimalSeparator = NSLocale.currentLocale().objectForKey(NSLocaleDecimalSeparator) as! String
        return prospectiveText.isNumeric() &&
               prospectiveText.doesNotContainCharactersIn("-e" + decimalSeparator) &&
               prospectiveText.characters.count <= 5
        
      // Do not put constraints on any other text field in this view
      // that uses this class as its delegate.
      default:
        return true
    }
    
  }
  
  // Dismiss the keyboard when the user taps the "Return" key or its equivalent
  // while editing a text field.
  func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    return true;
  }
  
}

I gave the outlets for the text fields sensible names, but I thought that it might be helpful to show you an annotated storyboard that points out which outlet belongs to which text field:

constrained text fields screenshot

The code in the view controller calls on some string utility methods that I decided to put into their own module: the StringUtils.swift file:

//
//  StringUtils.swift
//

import Foundation

extension String {
  
  // Returns true if the string has at least one character in common with matchCharacters.
  func containsCharactersIn(matchCharacters: String) -> Bool {
    let characterSet = NSCharacterSet(charactersInString: matchCharacters)
    return self.rangeOfCharacterFromSet(characterSet) != nil
  }
  
  // Returns true if the string contains only characters found in matchCharacters.
  func containsOnlyCharactersIn(matchCharacters: String) -> Bool {
    let disallowedCharacterSet = NSCharacterSet(charactersInString: matchCharacters).invertedSet
    return self.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
  }
  
  // Returns true if the string has no characters in common with matchCharacters.
  func doesNotContainCharactersIn(matchCharacters: String) -> Bool {
    let characterSet = NSCharacterSet(charactersInString: matchCharacters)
    return self.rangeOfCharacterFromSet(characterSet) == nil
  }
  
  // Returns true if the string represents a proper numeric value.
  // This method uses the device's current locale setting to determine
  // which decimal separator it will accept.
  func isNumeric() -> Bool
  {
    let scanner = NSScanner(string: self)
    
    // A newly-created scanner has no locale by default.
    // We'll set our scanner's locale to the user's locale
    // so that it recognizes the decimal separator that
    // the user expects (for example, in North America,
    // "." is the decimal separator, while in many parts
    // of Europe, "," is used).
    scanner.locale = NSLocale.currentLocale()
    
    return scanner.scanDecimal(nil) && scanner.atEnd
  }

}

Let’s take a closer look at the code…

The delegate pattern and text fields

The delegate pattern in general

the delegate pattern

The Delegate pattern is a fundamental part of iOS app development. You’ll encounter it often when programming user interfaces, including those times when you want to your program to react to what the user does with text fields.

The delegate pattern involves two categories of object:

  • A delegator, which needs to perform a task, but doesn’t have some needed information, resources, or logic to do so. It gets that needed information, resources, or logic from…
  • A delegate. While it typically can’t do what the delegator does, it has the information, resources, or logic that the delegator needs to perform its task.

My pet analogy for the delegate pattern is pictured above: an airplane and air traffic control. Unlike the driver of a car, who’s controlled only by traffic signals and pretty much free to choose any destination and route s/he pleases, the pilot of a plane has to delegate a lot of those choices to air traffic control. The airplane, which does the actual flying, is the delegator, and air traffic control, which gives clearance for takeoff and landing and tells the plane the heading, speed, and altitude at which it should fly, is the delegate.

The delegate pattern in iOS

If you look at the delegate pattern in Wikipedia, you’ll see that there are a number of ways to implement it. Here’s how it’s done in iOS (and Cocoa), whether you’re doing it in Objective-C or Swift:

delegate pattern in iOS

There are three things in play:

  • The delegator, which keeps a reference to the delegate, which will end up having the task delegated to it,
  • The delegate, which implements the methods and properties used to accomplish the delegated task, and
  • The protocol, which connects the delegator and delegate by:
    • giving the delegator a way to send messages to the delegate, and
    • giving the delegate a way to perform actions on behalf of the delegator.

The delegate pattern with iOS’ text fields

Let’s make the above diagram a little more specific and talk about delegation in terms of iOS’ text fields:

delegation with text fields

iOS text fields — that is, instances of the UITextField class — participate in a delegate pattern as delegators. They’ve got the power to control what happens when the user starts and stops editing their contents and what characters can be typed into them, but they offload the logic that handles those tasks to another object: the delegate.

A specific protocol, the UITextFieldDelegate protocol, connects the text field and its delegate together. A protocol is simply a set of declarations of class members — instance properties, instance methods, type methods, operators, and subscripts. These instance properties, instance methods, type methods, operators, and subscripts are implemented in the delegate (implementing a protocol’s members is called adopting the protocol), and the delegator calls on these implemented members.

The protocol: UITextFieldDelegate

Let’s look at the UITextFieldDelegate protocol. You can actually check it out for yourself; the simplest way is to control-click or right-click on any occurrence of UITextField in your code and then click on Jump to Definition in the contextual menu that appears:

getting to uitextfielddelegate

You’ll be taken to UITextField.h, a header file that allows Swift to connect to the Objective-C code on which UITextField is built. It contains the declarations for all the publicly-accessible parts of UITextField, including the UITextFieldDelegate protocol. You’ll find it near the end of the file. I’ve reproduced it below:

protocol UITextFieldDelegate : NSObjectProtocol {
    
    optional func textFieldShouldBeginEditing(textField: UITextField) -> Bool // return NO to disallow editing.
    optional func textFieldDidBeginEditing(textField: UITextField) // became first responder
    optional func textFieldShouldEndEditing(textField: UITextField) -> Bool // return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end
    optional func textFieldDidEndEditing(textField: UITextField) // may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called
    
    optional func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool // return NO to not change text
    
    optional func textFieldShouldClear(textField: UITextField) -> Bool // called when clear button pressed. return NO to ignore (no notifications)
    optional func textFieldShouldReturn(textField: UITextField) -> Bool // called when 'return' key pressed. return NO to ignore.
}

The delegate: ViewController

In order to become a delegate, a class has to adopt the protocol. If you’re familiar with languages like C# and Java, “adopting a protocol” is similar to “implementing an interface”: we add the protocol to a class’ definition, as if we’re inheriting it. In this case, we’ll have the view controller adopt the protocol:

class ViewController: UIViewController, UITextFieldDelegate {

This says that the ViewController class inherits from the UIViewController class and adopts the UITextFieldDelegate protocol. Having the view controller act as the delegate makes sense: it controls the user interface, and the text fields are part of the user interface.

Just as you have to implement the methods in an inherited interface in C# and Java, you have to implement the methods in an adopted protocol in Swift. There is a difference, however: in Swift, you can choose not to implement methods marked as optional.

You may have noticed that all the methods in the UITextFieldDelegate protocol are optional. This means that a delegate that adopts the protocol can implement as many or as few of its methods as necessary. For the purposes of our app, we’re implementing two of them in ViewController:

  • textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool: The text field calls this whenever the user tries to change the contents of a text field, whether by typing in or deleting a character, or by cutting or pasting. The method should return true if the change is to be accepted, and false to reject the change and keep the contents of the text field the way they are. We’ll use it to limit the types of character that can be entered into the view’s text fields and set a maximum the number of characters that can be entered for each field.
  • textFieldShouldReturn(textField: UITextField) -> Bool: The text field calls this whenever the user taps the Return key or its equivalent on the keyboard. We’ll use it to dismiss the keyboard when the user taps Return.

We’ll talk about the implementation of these methods in the next section. We have to take care of the delegators first.

The delegators: the text fields

We’ve got a protocol, and we’ve got a delegate that adopts it. Now we need to set up the delegators, which in this case, are the text fields — we need to tell them who their delegates are. We do this by setting each text field’s delegate property in the initializeTextFields method of the ViewController class:

  // Designate this class as the text fields' delegate
  // and set their keyboards while we're at it.
  func initializeTextFields() {
    vowelsOnlyTextField.delegate = self
    vowelsOnlyTextField.keyboardType = UIKeyboardType.ASCIICapable

    noVowelsTextField.delegate = self
    noVowelsTextField.keyboardType = UIKeyboardType.ASCIICapable
    
    digitsOnlyTextField.delegate = self
    digitsOnlyTextField.keyboardType = UIKeyboardType.NumberPad
    
    numericOnlyTextField.delegate = self
    numericOnlyTextField.keyboardType = UIKeyboardType.NumbersAndPunctuation
    
    positiveIntegersOnlyTextField.delegate = self
    positiveIntegersOnlyTextField.keyboardType = UIKeyboardType.DecimalPad
  }

By setting all the text fields’ delegate properties to self, we’re saying that this class is their delegate. Any events arising from editing the text fields will be handled by this class.

Constraining the text fields

The magic that constrains a text field so that it’s vowels-only, numbers-only and so on happens inside the protocol method with the very long signature, textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool. This method needs to be coded so that it returns true if we want the user’s changes to be accepted (which then updates the text field), or false if we don’t want the user’s changes to be accepted (which leaves the text field unchanged).

Here’s its code:

  func textField(textField: UITextField,
                 shouldChangeCharactersInRange range: NSRange,
                 replacementString string: String)
       -> Bool
  {
    // We ignore any change that doesn't add characters to the text field.
    // These changes are things like character deletions and cuts, as well
    // as moving the insertion point.
    //
    // We still return true to allow the change to take place.
    if string.characters.count == 0 {
      return true
    }
    
    // Check to see if the text field's contents still fit the constraints
    // with the new content added to it.
    // If the contents still fit the constraints, allow the change
    // by returning true; otherwise disallow the change by returning false.
    let currentText = textField.text ?? ""
    let prospectiveText = (currentText as NSString).stringByReplacingCharactersInRange(range, withString: string)
  
    switch textField {
    
      // Allow only upper- and lower-case vowels in this field,
      // and limit its contents to a maximum of 6 characters.
      case vowelsOnlyTextField:
        return prospectiveText.containsOnlyCharactersIn("aeiouAEIOU") &&
               prospectiveText.characters.count <= 6

      // Allow any characters EXCEPT upper- and lower-case vowels in this field,
      // and limit its contents to a maximum of 8 characters.
      case noVowelsTextField:
        return prospectiveText.doesNotContainCharactersIn("aeiouAEIOU") &&
               prospectiveText.characters.count <= 8
        
      // Allow only digits in this field, 
      // and limit its contents to a maximum of 3 characters.
      case digitsOnlyTextField:
        return prospectiveText.containsOnlyCharactersIn("0123456789") &&
               prospectiveText.characters.count <= 3
        
      // Allow only values that evaluate to proper numeric values in this field,
      // and limit its contents to a maximum of 7 characters.
      case numericOnlyTextField:
        return prospectiveText.isNumeric() &&
               prospectiveText.characters.count <= 7
        
      // In this field, allow only values that evalulate to proper numeric values and
      // do not contain the "-" and "e" characters, nor the decimal separator character
      // for the current locale. Limit its contents to a maximum of 5 characters.
      case positiveIntegersOnlyTextField:
        let decimalSeparator = NSLocale.currentLocale().objectForKey(NSLocaleDecimalSeparator) as! String
        return prospectiveText.isNumeric() &&
               prospectiveText.doesNotContainCharactersIn("-e" + decimalSeparator) &&
               prospectiveText.characters.count <= 5
        
      // Do not put constraints on any other text field in this view
      // that uses this class as its delegate.
      default:
        return true
    }
    
  }

This method takes three parameters:

  • textField: the text field that either had a character added to or removed from it.
  • range: the range of the characters within the text field that are to be replaced.
  • string: the replacement string.

Does the change add characters?

The first thing the method does is see if the change adds characters to the text field:

if string.characters.count == 0 {
  return true
}
  • If the user has typed a character or pasted non-empty text into the text field, string is non-empty and has a length greater than zero. In this case, we’ll want to do more processing.
  • If the user has deleted a character, cut text, or simply moved the insertion point, string is empty and has a length of zero. In this case, we don’t want to do any more processing; removing characters means we don’t have to see if we want to disallow any added character or if the maximum number of characters for the text field has been exceeded. We’ll just exit the method, returning true so that the change still happens, whether it’s a deletion, a cut, or moving the insertion point.

What will the text field look like after the change?

We want to figure out what the text field would contain if the change were allowed. We’ll call that the prospective text, which we’ll assign to a local constant called prospectiveText. We can figure out what the prospective text is by using NSString‘s stringByReplacingCharactersInRange method on the contents of textField.text.

Here’s where we run into a problem:

  • In order to use NSString‘s stringByReplacingCharactersInRange method, we need to convert a Swift String into an NSString.
  • The type of a text field’s text property type isn’t String, but String?. That’s because a text field’s value can either be:
    • a string when it contains at least one character, or
    • nil when it’s empty
  • String can be cast into NSString; String? can’t.

To get around this problem, we’re going to create a String constant called currentText, which we’ll fill as follows:

  • If the text field isn’t empty — that is, if its value isn’t nil — we’ll simply assign currentText the value of textField.text.
  • If the text field is empty — that is, if its value is nil — we’ll assign currenttext the value "", the empty string. There’s a difference between nil (which denotes no value) and the empty string (which is a value, just one that has a length of 0 characters).

Here’s the code:

 let currentText = textField.text ?? ""
 let prospectiveText = (currentText as NSString).stringByReplacingCharactersInRange(range, withString: string)

As we’ll see shortly, having prospectiveText lets us set a maximum number of characters that can be put into a text field.

Taking care of business

Now that we’ve dealt with cases where the change to the text field deletes characters and have created prospectiveText, we can now start constraining text fields. This is handled in the switch statement, which we use to separate the constraining logic for each text field:

switch textField {
    
      // Allow only upper- and lower-case vowels in this field,
      // and limit its contents to a maximum of 6 characters.
      case vowelsOnlyTextField:
        return prospectiveText.containsOnlyCharactersIn("aeiouAEIOU") &&
               prospectiveText.characters.count <= 6

      // Allow any characters EXCEPT upper- and lower-case vowels in this field,
      // and limit its contents to a maximum of 8 characters.
      case noVowelsTextField:
        return prospectiveText.doesNotContainCharactersIn("aeiouAEIOU") &&
               prospectiveText.characters.count <= 8
        
      // Allow only digits in this field, 
      // and limit its contents to a maximum of 3 characters.
      case digitsOnlyTextField:
        return prospectiveText.containsOnlyCharactersIn("0123456789") &&
               prospectiveText.characters.count <= 3
        
      // Allow only values that evaluate to proper numeric values in this field,
      // and limit its contents to a maximum of 7 characters.
      case numericOnlyTextField:
        return prospectiveText.isNumeric() &&
               prospectiveText.characters.count <= 7
        
      // In this field, allow only values that evalulate to proper numeric values and
      // do not contain the "-" and "e" characters, nor the decimal separator character
      // for the current locale. Limit its contents to a maximum of 5 characters.
      case positiveIntegersOnlyTextField:
        let decimalSeparator = NSLocale.currentLocale().objectForKey(NSLocaleDecimalSeparator) as! String
        return prospectiveText.isNumeric() &&
               prospectiveText.doesNotContainCharactersIn("-e" + decimalSeparator) &&
               prospectiveText.characters.count <= 5
        
      // Do not put constraints on any other text field in this view
      // that uses this class as its delegate.
      default:
        return true
    }

The cases for each text field are listed in the order in which they appear onscreen. Let’s look at them one by one:

The “Vowels only” text field

In this text field, we want the user to be able to enter only vowels — the upper- and lower-case versions of the letters a, e, i, o, and u. We also want to limit its contents to a maximum of 6 characters. Here’s the code that does this:

      // Allow only upper- and lower-case vowels in this field,
      // and limit its contents to a maximum of 6 characters.
      case vowelsOnlyTextField:
        return prospectiveText.containsOnlyCharactersIn("aeiouAEIOU") &&
               prospectiveText.characters.count <= 6

This code makes use of the String extension method containsOnlyCharactersIn, which I defined in StringUtils.swift. It returns true if the String contains only characters in the provided parameter.

If both conditions in the return statement evaluate to trueprospectiveText contains only vowels and has 6 characters or fewer — the method returns true, the change to the text field is allowed, and the text field is updated. If both conditions don’t evaluate to true, the method returns false, the change to the text field is not allowed, and the text field’s contents remain the same.

The “Anything BUT vowels” text field

In this text field, we want the user to be able to enter any character except vowels and limit its contents to a maximum of 8 characters. Here’s the code that does this:

      // Allow any characters EXCEPT upper- and lower-case vowels in this field,
      // and limit its contents to a maximum of 8 characters.
      case noVowelsTextField:
        return prospectiveText.doesNotContainCharactersIn("aeiouAEIOU") &&
               prospectiveText.characters.count <= 8

This code is similar to the code for the “Vowels only” text field. The major difference is that it makes use of another String extension method defined in StringUtils.swift: doesNotContainCharactersIn, which returns true if the String doesn’t contain any of the characters in the provided parameter.

The “Digits only” text field

In this text field, we want the user to be able to enter only digits, and no more than three of them at most. Here’s the code that does this:

      // Allow only digits in this field, 
      // and limit its contents to a maximum of 3 characters.
      case digitsOnlyTextField:
        return prospectiveText.containsOnlyCharactersIn("0123456789") &&
               prospectiveText.characters.count <= 3

This code is almost the same as the code for the “Vowels only” text field.

The “Numeric values only” text field

Here’s an interesting one: a text field that accepts only user input that evaluates to a proper numeric value. That means it will accept the following characters:

  • The digits 0 through 9
  • The (negative) symbol
  • The decimal separator, which is either . or , depending on the user’s locale settings
  • The letter e, which is used for numbers specified in scientific notation

Even when limiting the user to these characters, it’s possible for non-numeric values to be entered. Here are a couple of example non-numeric values that we don’t want the user to be able to enter:

  • More than one decimal separator, such as 1.2.3 or 1,2,3, depending on the user’s locale settings
  • The unary minus being placed anywhere other than the start, such as 4-5

We also want to limit the text field to a maximum of 7 characters.

Here’s the code:

      // Allow only values that evaluate to proper numeric values in this field,
      // and limit its contents to a maximum of 7 characters.
      case numericOnlyTextField:
        return prospectiveText.isNumeric() &&
               prospectiveText.characters.count <= 7

This code makes use of the String extension method isNumeric, which I defined in StringUtils.swift. It returns true if the String contains a value that evaluates to a numeric value. It’s powered by NSScanner, a class that’s handy for going through strings and extracting useful data from them, and its scanDecimal method, which returns true if it finds a value that can be evaluated as an NSDecimal value.

If both conditions in the return statement evaluate to trueprospectiveText evaluates to a numeric value and has 7 characters or fewer — the method returns true, the change to the text field is allowed, and the text field is updated. If both conditions don’t evaluate to true, the method returns false, the change to the text field is not allowed, and the text field’s contents remain the same.

The “Positive integers only” text field

This is a more strict version of the “Numeric values only” text field. It requires that anything entered into it needs to evaluate as a proper numeric value, but it also requires that the value be a positive integer and not be stated in scientific notation. It has a maximum length of 5 characters. Here’s the code:

      // In this field, allow only values that evalulate to proper numeric values and
      // do not contain the "-" and "e" characters, nor the decimal separator character
      // for the current locale. Limit its contents to a maximum of 5 characters.
      case positiveIntegersOnlyTextField:
        let decimalSeparator = NSLocale.currentLocale().objectForKey(NSLocaleDecimalSeparator) as! String
        return prospectiveText.isNumeric() &&
               prospectiveText.doesNotContainCharactersIn("-e" + decimalSeparator) &&
               prospectiveText.characters.count <= 5

In order to disallow negative numbers, we use the String extension method doesNotContainCharactersIn to block out characters. We disallow scientific notation by using the same method to block out e characters. The tricky part is disallowing the decimal separator, which can be either . or , depending on the user’s locale. We identify it with NSLocale.currentLocale().objectForKey(NSLocaleDecimalSeparator), which we add to the parameter for doesNotContainCharactersIn.

Any other text fields that might be on the screen

Finally, we handle the default case: any other text fields that might be on the screen, which we won’t constrain:

      default:
        return true

Other UI goodies

This app has a couple of UI features that I’ll cover in a subsequent article:

  • specifying the keyboard for a specific text field,
  • dismissing the keyboard when the user taps the Return key or its equivalent, and
  • dismissing the keyboard when the user taps on the view

It’s all in the code, so if you’d like to jump in and learn how it works on your own, go right ahead!

Resources

zip file iconIn case you missed it, here are the zipped project files for the demo project, ConstrainedTextFieldDemo [90K Xcode project and associated files, zipped].

Categories
Process Programming

FizzBuzz Still Works

The Sorting Hat

I recently interviewed some programmers for a couple of available positions at CTS, the startup crazy enough to take me as its CTO. The interviews started with me giving the candidate a quick overview of the company and the software that we’re hiring people to implement, after which it would be the candidate’s turn to tell his or her story. With the introductions out of the way — usually at the ten- or fifteen-minute mark of the hour-long session, I ran each candidate through the now-infamous “FizzBuzz” programming test:

Write a program that prints out the numbers from 1 through 100, but…

  • For numbers that are multiples of 3, print “Fizz” instead of the number.
  • For numbers that are multiples of 5, print “Buzz” instead of the number
  • For numbers that are multiples of both 3 and 5, print “FizzBuzz” instead of the number.

That’s it!

This was the “sorting hat”: the interview took different tacks depending on whether the candidate could or couldn’t pass this test.

I asked each candidate to do the exercise in the programming language of their choice, with pen and paper rather than on a computer. By requiring them to use pen and paper rather than letting them use a computer, I prevented them from simply Googling a solution. It would also force them to actually think about the solution rather than simply go through the “type in some code / run it and see what happens / type in some more code” cycle. In exchange for taking away the ability for candidates to verify their code, I would be a little more forgiving than the compiler or interpreter, letting missing semicolons or similarly small errors slide.

The candidates sat at my desk and wrote out their code while I kept an eye on the time, with the intention of invoking the Mercy Rule at the ten-minute mark. They were free to ask any questions during the session; the most common was “Is there some sort of trick in the wording of the assignment?” (there isn’t), and the most unusual was “Do you think I should use a loop?”, which nearly made me end the interview right then and there. Had any of them asked to see what some sample output would look like, I would have shown them a text file containing the first 35 lines of output from a working FizzBuzz program.

The interviews concluded last week, after which we tallied the results: 40% of the candidates passed the FizzBuzz test. Worse still, I had to invoke the Mercy Rule for one candidate who hit the ten-minute mark without a solution.

The Legend of FizzBuzz

I was surprised to find that only one of the candidates had heard of FizzBuzz. I was under the impression that that it had worked its way into the developer world’s collective consciousness since Imran Ghory wrote about the test back in January 2007:

On occasion you meet a developer who seems like a solid programmer. They know their theory, they know their language. They can have a reasonable conversation about programming. But once it comes down to actually producing code they just don’t seem to be able to do it well.

You would probably think they’re a good developer if you’ld never seen them code. This is why you have to ask people to write code for you if you really want to see how good they are. It doesn’t matter if their CV looks great or they talk a great talk. If they can’t write code well you probably don’t want them on your team.

After a fair bit of trial and error I’ve come to discover that people who struggle to code don’t just struggle on big problems, or even smallish problems (i.e. write a implementation of a linked list). They struggle with tiny problems.

So I set out to develop questions that can identify this kind of developer and came up with a class of questions I call “FizzBuzz Questions” named after a game children often play (or are made to play) in schools in the UK.

Ghory’s post caught the attention of one of the bright lights of the Ruby and Hacker News community, Reg “Raganwald” Braithwaite. Reg wrote an article titled Don’t Overthink FizzBuzz, which in turn was read by 800-pound gorilla of tech blogging, Jeff “Coding Horror” Atwood.  It prompted Atwood to ask why many people who call themselves programmers can’t program. From there, FizzBuzz took on a life of its own — just do a search on the term “FizzBuzz”; there are now lots of blog entries on the topic (including this one now).

In Dave Fayram’s recent article on FizzBuzz (where he took it to strange new heights, using monoids), he observed that FizzBuzz incorporates elements that programs typically perform:

  1. Iterating over a group of entities,
  2. accumulating data about that group,
  3. providing a sane alternative if no data is available, and
  4. producing output that is meaningful to the user.
If you can’t write a program that does all of the above in its most basic form, you can’t program. It’s that simple. Needless to say, anyone who didn’t pass FizzBuzz didn’t pass the interview.

My FizzBuzz Solution

I can’t go fairly judge other programmers with FizzBuzz without going through the test myself. I implemented it in Ruby, my go-to language for hammering out quick solutions, and in the spirit of fairness to those I interviewed, I did it with pen and paper on the first day of the interviews (and confirmed it by entering and running it):

I consider myself a half-decent programmer, and I’m pleased that I could bang out this solution on paper in five minutes. My favourite candidates from our interviews also wrote up their solutions in about the same time.

A Forty Percent Pass Rate?!

Okay, the picture above shows a 33% pass rate. Close enough.

How is it that only 40% of the candidates could write FizzBuzz? Consider that:

  • All the candidates had some kind of college- or university-level computer programming education; many had computer science certificates or degrees.
  • The candidates had varying degrees of work experience, ranging from years to decades.
  • Each candidate could point to a sizeable, completed project on which they worked.
  • All the candidates came to us pre-screened by a recruiting company.

A whopping sixty percent of the candidates made it past these “filters” and still failed FizzBuzz, and I’m not sure how. I have some guesses:

  • Moving away from programming: Some of them may have started as developers initially, but over time, their work changed. They still hold some kind of programming-related title and their work still has something to do with the applications they were hired to develop, but these days, most of their job involves tasks outside the realm of actually writing code.
  • “Copy and paste, adjust to taste”-style programming: Also known as “Coding by Googling”,  this is when a developer writes applications by searching for code that’s similar to the problem that s/he’s trying to solve, copy-and-pastes it, and then tweaks it as needed. I get the feeling that a lot of client-side JavaScript is coded this way. This is one of the reasons I insisted that the FizzBuzz exercise be done on paper rather than with a computer.
  • Overspecialization: It could be that some candidates have simply been writing the same kind of program over and over, and now do their work “on autopilot”. I’ve heard that FizzBuzz is pretty good at catching this sort of developer unaware.

If you’re interviewing prospective developers, you may want to hit them with the FizzBuzz test and make them do it on paper or on a whiteboard. The test may be “old news” — Imran wrote about it at the start of 2007; that’s twenty internet years ago! — but apparently, it still works for sorting out people who just can’t code.

Be sure to read the follow-up article, Further Into FizzBuzz!