Objective-C Developer in a Swift World/ Map and Filter

Old Dog Swift Tricks

Over the past year and half I’ve spent as much time as possible learning Swift. I don’t care how senior of an iOS developer you are, Swift is a shift in language and paradigms. I hit the reset button in my development career and wanted to get up to speed with the new language changes in a short amount of time.

I’m Loving It

I’ve been reading, coding, cursing and embracing Swift since 1.0 (including Swift 3) and I love the language. Before I taught myself Objective-C I programmed in Java, PHP and occassionally Python. All of the later languages where extremely fun, but didn’t hold a candle to Objective-C or Swift.

Below are some examples of the new language features.

New Paradigms

  • Map
  • Reduce
  • Filter
  • Generics
  • ErrorType
  • Functional
  • Protocols
  • Value Types

Adjustments

Adjusting to these paradigms hasn’t been easy, but all been worth it. Swift is the future and the latest XCode is my Delorian.

Full Steam Ahead

Apple and the community have spoken…Swift is the future. With that said I’m not letting my Objective-C (fu) go to waste. I, like many others, have a TON of code written (and needs to be maintained) in Objective-C. The language and features are amazing and if you are starting out in iOS development then I would strongly suggest boning up on you Objective-C (fu).

Lessons Learned

Embrace the Swift change swiftly…and by change I mean high order functions mapand filter.

Map

Definition

The map method solves the problem of transforming the elements of an array using a function. Let’s look at some examples.

Example

Let’s say you want to display a list of names from a Person object in a UITableView. In the “old” way I would have a list of Person objects and then in my UITableViewCell I would have grabbed the instance of the Person object from the list and set the textLabel.text to the name property value.

In the Swift “way” map would simplify this.

struct Person {
    let name: String
    let age: Int
}

let names: [String] = people.map { return $0.name }

You can also apply the same principles to dictionaries.

Uppercase the values

let payload: Dictionary <String, String> = ["hello": "world", "steve": "jobs"]
let result = payload.map { (key, value) in (key, value.uppercased()) }

/// Output

[("hello", "WORLD"), ("steve", "JOBS")]

Filter

Definition

The filter method solves the problem of selecting the elements of an array that pass a certain condition

Example (name filter)

Filter names from a pre-defined list

Extending our map example from above let’s say you have a list of Person objects that you want to filter from another list. Somewhat like a search filter.

struct Person {
    let name: String
    let age: Int
}

let people = [
    Person(name: "Katie",  age: 23),
    Person(name: "Bob",    age: 21),
    Person(name: "Rachel", age: 33),
    Person(name: "John",   age: 27),
    Person(name: "Megan",  age: 15)
]

let matchingNames = ["Katie", "Rachel", "Megan"]
let names: [String] = people.map { return $0.name }
let filteredNames = names.filter({matchingNames.contains($0)})

/// Output

["Katie", "Rachel", "Megan"]

Example (character filter)

let badCharacters: [Character] = [".", "$", "#", "[", "]"]
let badChannelName: String = "thisis my string[.$#"

let cleanName = String(badChannelName.characters.filter{!badCharacters.contains($0)})

/// Output

"thisis my string"

Example (filter dictionary)

let data: [String: String] = ["hello": "world", "steve": "jobs"]
let filtered = data.filter { $0.1 == "world" }
// let filtered = data.filter { $1 == "jobs" }

Real World

I have a list of objects with three properties: name, imageName and selected. You can toggle the selection from the list display. If user selected one then I would toggle the “selected” flag then I would add it to a local Array of toggled items. Didn’t care there were dupes. When the user was finished with workflow then I need to pass the selected options to another view controller with only names.

Obvious question is “How do you filter an array of duplicates?”. With a a combination of map, filter and a computed property the answer is out there.

Let’s assume I have two lists of sports that can be selected or unselected and those datasource for those lists. What is the optimal solution for returning a final list of objects who has a selected value of true?

Let’s start with the lists

let SportPickerTypesRight: Array<SportPickerType> = [SportPickerType.create("NFL", imageName: "nfl_background"),
                                                     SportPickerType.create("NCAA Football", imageName: "football_background"),
                                                     SportPickerType.create("NCAA Basketball", imageName: "basketball_background"),
                                                     SportPickerType.create("NBA", imageName: "nfl_background")]

let SportPickerTypesLeft: Array<SportPickerType> = [SportPickerType.create("NHL", imageName: "nhl_background"),
                                                    SportPickerType.create("Soccer", imageName: "soccer_background"),
                                                    SportPickerType.create("MLB", imageName: "mlb_background"),
                                                    SportPickerType.create("Other", imageName: "other_background")]
var SportPickerTypes: Array<SportPickerType> {

    let combined = [SportPickerTypesRight, SportPickerTypesLeft]
    
    return combined.flatMap { $0 }
}

struct SportPickerType {

    let name: String
    
    let imageName: String
    
    var selected: Bool
    
    static func create(name: String, imageName: String) -> SportPickerType {
        return SportPickerType(name: name, imageName: imageName, selected: false)
    }
}

extension SportPickerType: Equatable {}

extension SportPickerType: Hashable {
    
    var hashValue: Int {
        return (name + imageName).hashValue
    }
}

func ==(lhs: SportPickerType, rhs: SportPickerType) -> Bool {
    return lhs.name == rhs.name
}

/// When you want to return a list of unique selected sports
///
/// What the property does is return a set (unique values)
/// and the filters that set for only values with selected == true
/// and returns that array of names

   var selectedSportTypes: Array<String> {
        
        let filteredSet: Set<SportPickerType> = Set(self.unfilteredSportTypes)
        let mappedNames: Array<String> = filteredSet.filter({$0.selected == true}).map{return $0.name}
 
        return mappedNames
    }

Understanding these higher level function provide great functionality and clarity to your coding. Swift is the future and learning / understanding these higher level functions will provide much more elegance to your code.