Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 3 Ranges: Best Practices

Tags:

swift

swift3

I downloaded the Xcode 8.0 beta yesterday and consequently Swift 3. The first thing I did was trying to update my project for Swift 3 and I nearly cried. One of the gravest changes is (in my opinion) the new management of Swifts Range struct, especially because the automatic conversion to the current Swift syntax does not do anything with the ranges.

Range is split into Range, CountableRange, ClosedRange and CountableClosedRange which does make sense when considering what is now possible when using ranges (though it's mostly fairly unnecessary).

However: I have lots of functions accepting a Range<Int> as parameter or returning a Range<Int>. The problem is: I called these functions by 0..<5 for example or 0...4 (because it's semantically more expressive sometimes). Of course, I could simply adjust these type of things. But why don't all these range types have a common interface? I'd have to overload every single function for each of these range types and it would perform the exact same operations every time.

Are there any best practices yet for using ranges in Swift 3?

like image 508
borchero Avatar asked Jul 03 '16 15:07

borchero


1 Answers

Closed Range Operator

The closed range operator (a...b) defines a range that runs from a to b, and includes the values a and b. The value of a must not be greater than b.

The closed range operator is useful when iterating over a range in which you want all of the values to be used, such as with a for-in loop:

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25

Half-Open Range Operator

The half-open range operator (a..<b) defines a range that runs from a to b, but does not include b. It is said to be half-open because it contains its first value, but not its final value. As with the closed range operator, the value of a must not be greater than b. If the value of a is equal to b, then the resulting range will be empty.

Half-open ranges are particularly useful when you work with zero-based lists such as arrays, where it is useful to count up to (but not including) the length of the list:

let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
    print("Person \(i + 1) is called \(names[i])")
}
// Person 1 is called Anna
// Person 2 is called Alex
// Person 3 is called Brian
// Person 4 is called Jack

Note that the array contains four items, but 0..<count only counts as far as 3 (the index of the last item in the array), because it is a half-open range.

Closed Range: a...b

let myRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]

Half-Open Range: a..<b

let myRange = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]

Here's a real-world SpriteKit example I had to convert using arc4Random which is in virtually all SpriteKit projects. Random often deals with ranges.

Swift 2

Tools.swift

func randomInRange(_ range: Range<Int>) -> Int {
    let count = UInt32(range.upperBound - range.lowerBound)
    return  Int(arc4random_uniform(count)) + range.lowerBound
}

GameScene.swift

let gap = CGFloat(randomInRange(StackGapMinWidth...maxGap))

Swift 3

Tools.swift

func randomInRange(range: ClosedRange<Int>) -> Int {
    let count = UInt32(range.upperBound - range.lowerBound)
    return  Int(arc4random_uniform(count)) + range.lowerBound
}

GameScene.swift

let gap = CGFloat(randomInRange(range: StackGapMinWidth...maxGap))

So if the randomInRange() calculates a random number in the given range, including the upper bound, then it should be defined as ClosedRange<Bound>

Migrating to Swift 3

Range and ClosedRange can’t be iterated over (they are not collections anymore), since a value that is merely Comparable cannot be incremented. CountableRange and CountableClosedRange require Strideable from their bound and they conform to Collection so that you can iterate over them.

like image 170
Edison Avatar answered Nov 04 '22 22:11

Edison