How to search for a Character in a String with Swift 2

Working with Strings in Swift 2 has changed significantly. There are several big upcoming changes to how you work with String. 

1. String is no longer a collection type, instead the characters contained with it are accessed via a new property named characters (as mentioned on Apple’s blog).

2. Apple introduced Protocol Extensions in Swift 2 that will significantly change many APIs and functions. There’s a ton of movement from global functions (hard to find!) to methods (easier to find).

Swift 1.2 String Character Search

Searching for a Character in Swift 1.2 uses the global function contains() or find(), you won’t find them in Auto Complete – this greatly reduced the discovery of basic actions in Swift 1.2.

Using the lowercaseString property allows you to make the search case-insensitive.

// Swift 1.2
var word = "Apple"
var searchCharacter: Character = "p"

if contains(word.lowercaseString, searchCharacter) {
    print("word contains \(searchCharacter)")
}

if let index = find(word.lowercaseString, searchCharacter) {
    print("Index: \(index)")
}

To make character search work for both upper and lower case, you can use the lowercaseString property on the String.

Swift 2 String Search

In Swift 2 global many functions are gone and replaced with new instance methods. Some of these transformations were one-to-one and others were renamed. Some error messages can suggest how to change your code, otherwise you may have to dig.

The contains() function has been replaced by the contains() method that can be invoked on the characters property of the new Swift 2 String.

The find() function has been replaced with a new method called indexOf() that works on your characters property.

// Swift 2
var word = "Apple"
var searchCharacter: Character = "p"

if word.lowercaseString.characters.contains(searchCharacter) {
    print("word contains \(searchCharacter)")
}

if let index = word.lowercaseString.characters.indexOf(searchCharacter) {
    print("Index: \(index)")
}

Questions: the evolution of Swift

Working with Strings and Characters is fundamental for any programming language – when it’s hard to find the methods or the language uses different conventions it can be hard to figure out what code to write next.

What other functions, methods, or code tasks have you struggled with in Swift?

Reply below or on Twitter: @PaulSolt

 

OptionSetType Swift 2 Error: Nil is not compatible with expected argument

If you've been using Swift 1.2 or earlier, or you're an experienced Objective-C developer you'll discover using some APIs is different. 

Apple has been revamping all the APIs in Swift + Objective-C to support the latest Swift 2 language.

Apple has added Generics, Error Handling, and the OptionSetType which will change a large portion of the APIs – especially anything that takes option/setting flags.

Code Massaging

Moving forward you're going to have to massage your existing code, or your old programming habits to work with the new APIs.

I highly recommend opening a Documentation window (Shift + Command + 0) with Swift, since the Quick Help fails to work when you have a syntax error – something that didn't happened in Objective-C.

OptionSetType

The animatWithDuration: API that works with the spring physics has changed it's options: parameter to the new OptionSetType.

var myFirstLabel: UILabel!

func tapped(gesture: UITapGestureRecognizer) {
    UIView.animateWithDuration(0.5, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.0, options: nil, animations: {
        
        self.myFirstLabel.center = CGPoint(x: 100, y:40 + 200)
        
        }, completion: nil)
}

In Xcode 6 this code works with Swift 1.2 or lower, however if you run it in Xcode 7 + Swift 2 you'll get the following Error.

Swift 2 Errors

You might see either of these errors, depending on the version of Xcode. Error messages are continuing to evolve, some are even more helpful in diagnosing the problem.

ViewController.swift Nil is not compatible with expected argument type 'UIViewAnimationOptions'

ViewController.swift Cannot invoke 'animateWithDuration' with an argument list of type '(Double, delay: Double, usingSpringWithDamping: Double, initialSpringVelocity: Double, options: nil, animations: () -> _, completion: nil)'

Solution

The error is because the type for the options: parameter is wrong – you'll have to pass in an empty OptionSetType in order to compile.

nil should be replaced with []

Apple has changed options for many existing APIs, instead of being a NS_OPTIONS bitmask, in Swift 2.1+ they are treated as a Set in syntax and behavior. To pass no options (i.e. nil) you pass the empty set: [ ]

UIView.animateWithDuration(0.5, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.0, options: [], animations: {
    
    self.myFirstLabel.center = CGPoint(x: 100, y:40 + 200)
    
    }, completion: nil)

Follow me

Have you seen any strange error messages?

Send a Tweet to @PaulSolt

Fix UIViewController supportedInterfaceOrientations() to use UIInterfaceOrientationMask and OptionSetType in Swift 2

There are many changes coming in Swift 2 that further unify the Swift language and provide better support and method signatures.

A welcome change is the OptionSetType in Swift 2, this makes it easier to work with settings that relied on bit masks (a complex topic of binary logic) to use a Set and it's common operations.

The automatic conversion utility didn't help fix this change, so I had to fiddle a few times until I figured it out.

You'll see an error for your supportedInterfaceOrientations() method because the method signature has changed again. It now returns a UIInterfaceOrientationMask instead of an Int. 

Error: Method does not override any method from its superclass

While it breaks code now, this change makes Swift easier to use moving forward. Changes to how Objective-C code looks using new macros, generic types, and the OptionSetType will improve how you write code in Swift.

// Swift 1.2
override func supportedInterfaceOrientations() -> Int {
    let orientation = Int(UIInterfaceOrientationMask.Portrait.rawValue | UIInterfaceOrientationMask.PortraitUpsideDown.rawValue)
    return Int(UIInterfaceOrientationMask.All.rawValue)
}

Becomes a bit more verbose and uses the array notation to create a OptionSetType. It seems you need to set the type, otherwise the statement gets interpreted as an Array type, which isn't what you want.

// Swift 2
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    let orientation: UIInterfaceOrientationMask = [UIInterfaceOrientationMask.Portrait, UIInterfaceOrientationMask.PortraitUpsideDown]
    return orientation
}

This new change helps make Swift 2 code feel more at home, and a little less crazy than the hoops you had to jump through in Swift 1.2 and earlier.

Links