Going Global

A Peek At Swift’s Global Functions


aString.length.

We all tried it. We all scratched our heads. As soon as swift revealed itself to developers and was subsequently made available, there were inevitable hiccups.

To the correct use of optionals, discovering tuples, and swift’s syntax - all called for a few trips to the documentation. Through these required visits, programmers may have noticed some functionality has been reimagined as global functions.

This week, we’ll take a look at some of the things available to use hanging out in global scope.

contains()

The addition of generics means that swift is able to move code in places where Objective-C dare not go. The function contains is a result of this.

func contains<S : SequenceType where S.Generator.Element : Equatable>(seq: S, x: S.Generator.Element) -> Bool

Contains makes sifting through collections, or really any generic object, trivial:

let ar = [“spend stack”, “dib”]
let containsSpendStack = contains(ar, “spend stack”) //True
let jordan = “Jordan”
let containsJ = contains(jordan, “j”)//False
let oneToTen = [1,2,3,4,5,6,7,8,9,10]
let contains6 = contains(oneToTen, 6)//True

As shown, contains has no problem dutifully searching for primitives or reference types. Should one need finer control over their search, contains has an overload which allows a predicate to determine the outcome.

filter()

Down the road and in the same neighborhood as contains, you’ll find filter.

func filter<S : SequenceType>(source: S, includeElement: (S.Generator.Element) -> Bool) ->[S.Generator.Element]

Filter is already quite popular with the swift crowd due to its functional programming heritage, but no matter its upbringing, their is no denying its usefulness:

struct dibApps
{
var appName:String
init(name:String)
{
appName = name
}
}
//All DIB apps
let SpendStack = dibApps(name: “Spend Stack”)
let HaloTimer = dibApps(name: “Halo Timer”)
let AnyPost = dibApps(name: “AnyPost”)
let allDIBApps = [SpendStack, HaloTimer, AnyPost]
//Get DIB’s first released app (returns SpendStack)
let dibFirstRelease = filter(allDIBApps, {
$0.appName == “Spend Stack”
})

A lot of these global functions make use of a predicate, which only extends much needed flexibility to the programmer. In keeping with the previous code sample, we can return back to contains and utilize the predicate in the exact same way:

//Returns true
let dibFirstRelease = contains(allDIBApps, {
$0.appName == “Spend Stack”
})

reduce()

Reduce can lend a helping hand in many scenarios, but its forte is summing up a collection by reducing it to a single value.

func reduce<S : SequenceType, U>(sequence: S, initial: U, combine: @noescape (U, S.Generator.Element) -> U) -> U

I’ve seen many a for loop out in the wild that simply adds all the elements in an array. Reduce scoffs at these for loops, and instead does this:

let spendStackReviews = 324
let haloTimerReviews = 1254
let anyPostReviews = 835
let dibAppReviews = [spendStackReviews, haloTimerReviews, anyPostReviews]
//2,413
let total = dibAppReviews.reduce(0, {
$0 + $1
})

Neat, but I know what you’re thinking. You want all of those app reviews represented as strings. You’re upset because you have to go to back to a for loop.

And you’re wrong, thanks to map.

map()

Map is optimistic and believes in the power of transformation.

func map<S : SequenceType, T>(source: S, transform: (S.Generator.Element) -> T) -> [T]

In keeping with the previous code sample:

//Convert all the reviews into their string representation
let reviewsAsStrings = map(dibAppReviews, {
String($0)
})

The power of a predicate repeats itself. Of course, if you are feeling incredibly productive, you can combine these all together to get incredibly specific. Take in this slightly modified example from Peter Stuart:

let numbers = [10000, 10303, 30913, 50000, 100000, 101039, 1000000]
let evenNumbersAsString = numbers.filter{
$0 % 2 == 0
}.map{
NSNumberFormatter.localizedStringFromNumber($0, numberStyle: .DecimalStyle)
}.reduce(“”){
countElements($0) == 0 ? $1 : $0 + “ - ” + $1
}
//Produces "10,000 - 50,000 - 100,000 - 1,000,000"

Swift is clearly making life easier.

join()

Don’t look for the door just yet, T.T.I.D.G. didn’t just map itself to a SQL blog. Join is great, though, as it can take a generic set of data and combine it into one tidy package.

func join<C : ExtensibleCollectionType, S : SequenceType where C == C>(separator: C, elements: S) -> C

In many ways, one can think of this as componentsJoinedByString’s younger brother:

let dibApps = [“Spend Stack”, “Halo Timer”, “AnyPost”]
let dibAppsCSV = join(“, “, dibApps)
//"Spend Stack, Halo Timer, AnyPost"

…except with a tad bit less to type.

For bonus points, if you stopped by to visit last week, astute readers may also notice that enumerate is also defined as a global function.

Wrapping Up

These global functions make manipulating, searching, or otherwise working with collections modish and elementary. But their utility certainly doesn’t stop, or even start, with just collections.

There are functions for logging debug messages, encoding, inspecting references to checking the size of an object. Why not pay a visit to the curated list? Until we meet again next week, may your remaining weekend.filter({ $0.beEnjoyable == true }).


Jordan Morgan is an iOS software engineer who runs Dreaming In Binary.
@jordanmorgan10.


If you learned something about global functions, please NSRecommend this article below.