Introduction 
: Swift는 우리들에게 이미 익숙한 여러가지 control flow statement를 제공한다.
while, for, for-in, if, guard, switch, continue, break 등등..
특히, Swift의 switch는 다른 언어에 비해 상당히 powerful한 기능들을 가지고 있다. 이 부분은 뒤에서 차차 알아가도록 하자.


for-In Loops
: 'for-in'을 사용해서 array, dictionary, ranges of numbers 등을 iterating할 수 있다.

let names = ["Anna", "Alex", "Brian", "Jack"]

for name in names {
    print("Hello, \(name)!")

// array iterate

또한 dictionary를 key-value tuple로 iterate할 수 있다.

let numberOfLegs = ["spider : 8", "ant" : 6, "cat" : 4]

for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}
// dictionary iterate

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// ranges of numbers iterate
// index값이 필요 없다면 '_'로 대체할 수 있다.

let minutes = 60

for tickMark in 0..<minutes {
    // render the tick mark each minute (60 times)
}
// half open range operator iterate

stride(from:to:by:)를 사용해서 iterate 간격을 설정할 수 있다.

let minuteInterval = 5

for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // 0, 5, 10, 15 ... 45, 50, 55
}

또한, stride(from:through:by)를 사용해서 'through' 값을 포함하면서 iterate할 수 있다.

let hours = 12
let hourInterval = 3

for tickMark in stride(from: 3, through: hours, by: hourInterval) {
    // render the tick mark every 3 hours (3, 6, 9, 12)
}


While Loops
: while, repeat-while이 있는데 while은 다른 언어의 while과 다를바 없고 repeat-while은 다른 언어의 do-while과 같다. 

repeat {
    "statements"
} while "condition"


If

var temperatureInFahrenheit = 90

if temperatureInFahrenheit <= 32 {
    print("It is very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
    print("It is really warm. Don't forget to wear sunscreen.")
} else {
    print("It is not that cold. Wear a t-shirt")
}


Switch

: 앞서 말했듯이, Swift의 Switch문은 다른 언어에 비해 powerful한 기능을 제공한다.
Swift의 Switch문은 모든 case를 포함해야 한다. 즉, 'case'가 비교할 값이 될수 있는 모든 경우를 나타내지 않으면 반드시 'default:'를 구현해야 한다. 반대로 'case'가 모든 경우를 나타내고 있으면 'default:'를 생략할 수 있다. 

let someCharacter: Character = "z"

switch someCharacter {
    case "a":
        print("The first letter of the alphabet")
    case "z"
        print("The last letter of the alphabet")
    default:
        print("Some other character")
}

No Implicit Fall through
: swift의 Switch문은  C, Objective-C와 달리 자동으로 fall through 하지 않는다. 
이는, 실수로 break문을 생략함으로서 일어나는 버그들을 예방해준다.
'break'를 쓰지 않아도 fall through 하지 않지만 각 case마다 'break'를 사용해도 된다. 

각 case마다 하나이상의 실행문을 구현해야 한다. 그렇지 않으면 compile error가 발생한다.

let anotherCharacter: Character = "a"

switch anotherCharacter {
    case "a": // Invalid, the case has an empty body
    case "A":
        print("The letter A")
    default:
        print("Not the letter A")
}
// C, Objective-C 스타일 코딩이다. compile error 발생.

let anotherCharacter: Character = "a"

switch anotherCharacter {
    case "a", "A":
        print("The letter A")
    default:
        print("Not the letter A")
}
// Swift 스타일 코딩.

Interval Matching
: 해당 값이 특정 간격안에 포함되어 있는지 체크할 수 있다.

let approximateCount =62
let countedThings = 'moons orbiting Saturn"
let naturalCount: String

switch approximateCount {
    case 0:
        naturalCount = "no"
    case 1..<5:
        naturalCount = "a few"
    case 5..<12:
        naturalCount = "several"
    case 12..<100:
        naturalCount = "dozens of"
    case 100..<1000:
        naturalCount = "hundreds of"
    default:
        naturalCount = "many"
}

print("There are \(naturalCount) \(countedThings).")

Tuples
: Switch문에서 Tuple을 비교하는 방법을 알아보겠다. 

let somePoint = (1, 1)

switch somePoint {
    case (0, 0):
        print("\(somePoint) is at the origin")
    case (_, 0):
        print("\(somePoint) is on the x-axis")
    case (0, _):
        print("\(somePoint) is on the y-axis")
    case (-2...2, -2...2):
        print("\(somePoint) is inside the box")
    default:
        print("\(somePoint) is outside of the box")
}
// Prints "(1, 1) is inside the box"
// '_'는 제약이 필요없는 경우일 때 사용한다.

Value Bindings
: 비교값이 특정 case에 matching될 때 case문 안에서 사용할 수 있도록 let 또는 var로 값을 binding할 수 있다.

let anotherPoint = (2, 0)

switch anotherPoint {
    case (let x, 0):
        print("on the x=axis with an x value of \(x)")
    case (0, let y):
        print("on the y-axis with a y value of \(y)")
    case let (x, y):
        print("somewhere else at (\(x), \(y))")
}
// Prints "on the x-axis with an x value of 2"
// binding 하기 위해서 '_'을 let 또는 var로 대체했다고 생각하면 된다.

Where
: Switch문 안에서 추가적이 condition을 체크하기 위해서 'where'문을 사용할 수 있다. 

let yetAnotherPoint = (1, 1)

switch yetAnotherPoint {
    case let(x, y) where x == y:
        print("(\(x), \(y)) is on the line x == y")
    case let(x, y) where x == -y:
        print("(\(x), \(y)) is on the line x == -y")
    case let (x, y):
        print("(\(x), \(y)) is just some arbitrary point")
}
// Prints "(1, -1) is on the line x == -y"

Compound Cases 
: ','를 사용해서 하나의 case 여러 조건을 명시할 수 있다. 여러 조건중 하나에만 matching되면 해당 case를 만족하는 것으로 간주한다. 이를 'Compound Cases' 라고 한다.

let someCharacter: Character = "e"

switch someCharacter {
    case "a", "e", "i", "o", "u":
        print("\(someCharacter) is a vowel")
    case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
            "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
        print("\(someCharacter) is not a vowel or a consonant")
}
// Prints "e is a vowel"
// case문이 길 경우 여러줄로 명시해도 된다.

'Compound Cases'도 value binding을 사용할 수 있다. 

let stillAnotherPoint = (9, 0)

switch stillAnotherPoint {
    case (let distance, 0), (0, let distance):
        print("On an axis, \(distance) from the origin")
    default:
        print("Not on an axis")
}
// Prints "On an axis, 9 from the origin"


Control Transfer Statements
: code 실행의 흐름을 전환시킬 수 있는 여러가지 statement들을 제공한다.
: 'continue', 'break', 'fallthrough', 'return', 'throw'가 있다.
이 장에서는 'continue', 'break', 'fallthrough'만 설명하겠다. 'return'은 "Function"에서, 'throw'는 "Propagating Errors Using Throwing Functions"에서 설명하겠다.

continue
: 현재 iteration 수행을 멈추고 다음 iteration을 실행하라는 뜻이다.

let puzzleInput = "great minds think alike"
var puzzleOutput = ""
let charactersToRemove: [Character] = ["a", "e", "i", "o", "e", "u", " "]

for character in puzzleInput.characters {
    if charactersToRemove.contains(character) {
        continue
    } else {
        puzzleOutput.append(character)
    }
}

print(puzzleOutput)
// Prints "grtmndsthnklk"

Break
: control flow statement의 실행을 멈춘다. if문에서 사용시 labeled statement를 사용해야 한다.
: 반복문 에서도 사용 가능하다.

Break in Loop Statement
: 현재 속해있는 loop의 실행을 멈추고 loop를 빠져나온다. closing brace '}'를 기준으로 현재 loop를 판단한다. 

Break in a Switch Statement
: Switch문 안에서 'break'를 만나면 현재 실행을 중단 시키고 Switch문을 빠져나온다.
: 앞서 말했다시피, Swift의 Switch문은 모든 case를 명시해줘야 한다. 그리고 각 case는 하나 이상의 실행문을 가지고 있어야 한다. 그러면 어떠한 동작도 원하지 않는 case는 어떻게 처리할까? 그것은 해당 case문 안에 'break'만 명시해주면 된다.

let numberSymbol: Character = "1"
var possibleIntegerValue: Int?

switch numberSymbol {
    case "1"
        possibleInteger = 1
    case "2"
        possibleInteger = 2
    case "3"
        possibleInteger = 3
    default:
        break
}

if let integerValue = possibleIntegerValue {
    print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
    print("An integer value could not be found for \(numberSymbol).")
}

Fallthrough
: C, Objective C의 switch문과 달리 Swift의 switch문은 자동으로 case를 fall through 하지 않는다. 만약 case를 fall through 하고 싶다면 case문 안에서 'fallthrough' 키워드를 사용해야 한다.
: 'fallthrough'를 사용하면 다음 case에 대해 조건을 체크하지 않고 무조건 실행 시킨다.(C와 마찬가지로)

let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"

switch integerToDescribe {
    case 2, 3, 5, 7, 11, 13, 17, 19:
        description += " a prime number, and also"
        
        fallthrough
    default:
        description += " an integer."
}

print(description)
// Prints "The number 5 is a prime number, and also an integer."

Labeled Statements
: 간단히 말해서, break 또는 continue 하려고 하는 조건문, 반복문에 이름을 지어줘서 원하는 조건문, 반복문의 실행을 break, continue 하겠다는 것이다. 

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

var square = 0
var diceRoll = 0

gameLoop: while square != finalSquare {
    diceRoll += 1

    if diceRoll == 7 { diceRoll = 1 }
   
    switch square + diceRoll {
        case finalSquare:
            // diceRoll will move us to the final square, so the game is over
            break gameLoop
        case let newSquare where newSquare > finalSquare:
            // diceRoll will move us beyond the final square, so roll again
            continue gameLoop
        default:
            // this is a valid move, so find out its effect
            square += diceRoll
            square += board[square]
    }
}

print("Game over!")


Early Exit
: 'guard' statement는 'else' statement와 같이 쓰인다.
'guard' 뒤에 나오는 조건이 true가 아니면 else문이 실행 되는데 else문 안에서는 guard문이 존재하는 code block을 빠져 나오는 동작을 실행해야만 한다. 그렇지 않으면 compile error 발생. 
이러한 동작은 'return', 'break', 'continue', 'throw' statement를 사용하거나 return 하지 않는 method, function을 호출해서 실행할 수 있다. 여기서 return 하지 않는 method, function란 'fatalError(_:file:line:)과 같은 것들이다.


Checking API Availability
: Swift는 API의 Availability를 체크할수 있는 'availability condition'을 제공한다. 

if #available(iOS 10, macOS 10.12, *) {
    // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
    // Fall back to earlier iOS and macOS APIs
}
// iOS10, macOS 10.12 API를 지원하는지 체크한다. 맨 뒤 파라미터 '*'는 필수로 들어가야 되고, 이는 앞에 명시된 플랫폼 버전 이외에 다른 플랫폼 버전의 제약은 없다는 뜻이다. 


'IOS > Swift' 카테고리의 다른 글

[Swift 4.0] Closures  (0) 2017.04.01
[Swift 4.0] Functions  (0) 2017.04.01
[Swift 4.0] Collection Types  (0) 2017.02.03
[Swift 4.0] Strings and Characters  (0) 2017.01.22
[Swift 4.0] Basic Operators  (0) 2017.01.16
Posted by 홍성곤
,