Swift5를 기반으로 작성된 Swift Cheat Sheet 입니다. 본인이 성장하며 모르는 내용들을 하나씩 적어놨기 때문에 약간 내용이 파편화되어있고 당연히 모든 내용이 들어있지 않습니다.
- Markdown으로 작성됐습니다. star 후 찾아 보시거나 download후 찾아보시면 됩니다.
- 검색으로 궁금한 키워드를 입력해봐서 지금 해결가능한 문법이 있나 찾아봅니다
(ex. reduce, 자르기, index)
- 해당 레포지토리만 보고 학습한다기 보다는 문법이 알쏭달쏭할 때 Cheat Sheet를 찾아보시고 꼭 Apple Documentation을 참고해주세요
- Basic Operator
- Primitive Types
- String
- Int
- CharacterSet
- Float
- Double
- Bool
- ASCII
- etc.
- Collections
- Array
- Dictionary
- Set
- etc.
- Control Flow
- for
- if
- guard
- forEach
- Switch
- while, do-while
- Checking API Availability
- Error Handle
- Function
- Closure
정리되는데로 추가됩니다.
구분 | 연산자 | 우선순위 |
---|---|---|
단항 연산자 | +. -, !. ~ | 1 |
산술 연산자 | *, /, % | 2 |
산술 연산자 | +, - | 3 |
abs(-9) // 9 절대값
sqrt(9) // 3 루트
// 참고로 sqrt는 func squrt(_: Double) -> Double
pow(3, 2) // 9 제곱근
floor(9.1) // 9 소수점 버림
ceil(9.1) // 10 소수점 올림
print(2 << 2)
//4
let x = 6.5
// Equivalent to the C 'round' function:
print(x.rounded(.toNearestOrAwayFromZero))
// Prints "7.0"
// Equivalent to the C 'trunc' function:
print(x.rounded(.towardZero))
// Prints "6.0"
// Equivalent to the C 'ceil' function:
print(x.rounded(.up))
// Prints "7.0"
// Equivalent to the C 'floor' function:
print(x.rounded(.down))
// Prints "6.0"
let shiftBits: UInt8 = 4
print(shiftBits << 1) // 8
print(shiftBits << 2) // 16
print(shiftBits << 5) // 128
print(shiftBits << 6) // 256
print(shiftBits >> 2) // 1
- 대소문자 가리는 언어도 있다
7245 = 7245.00 = 7.245E+3
183857.419 = 1.83857419E+5
0.00007245 = 7.245E-5
0.0000000625431 = 6.25431E-8
- 10진법: 10
- 2진법: 0b1010 = 10
- 6진법: 0o12 = 10
- 16진법: 0xA = 10
-
swift는 String을 만드는 자체가 비교적 느리다
-
String끼리 더하는 것보다
String Interpolation
이 더 효율적이다
print(String(["a", "b", "d"]))
// "abd"
.trimmingCharacters(in: .whitespaces)
extension Int {
static func parse(from string: String) -> Int? {
return Int(string.components(separatedBy: CharacterSet.decimalDigits.inverted).joined())
}
}
-
해당 진법으로 변환(반환도
String
)print(String(2, radix: 2)) // "01"
let a = 1...3
let b = 1...10
print(b.starts(with: a))
// Prints "true"
// 세 자리까지 표현
print(String(format: "%.3d", Int.random(in: 0...1)))
// 001
또는
// 000
// 소수 첫째자리까지 표현
// %.2d 는 정수 두자리까지
String(format: "%.1f", (degree * 9 / 5) + 32)
describing은 강제성과 표현하려는 성질이 있다
struct Foo {
}
print(String(describing: Foo.self))
//Foo
struct Person {
var first: String
var last: String
}
let p = Person(first:"Matt", last:"Neuburg")
print("p is \(String(describing: p))")
// p is Person(first: "Matt", last: "Neuburg")
문자열을 Int로 형변환 하려해도 오류가 나지 않는다(강제로 하지 않는 이상)
var a = 1
var b = "hoho"
var num = Int(b)
print(num)
// nil
var str = "OOOXX"
print(str.components(separatedBy: "X"))
print(str.split(separatedBy: "X"))
// ["OOO", "", ""]
// ["OOO"]
var str = "abc"
var index = str.index(str.startIndex, offsetBy: 0)
print(str[index])
// "a"
var compareStr = "abcde"
if let index = compareStr.firstIndex(of: "c") {
compareStr.remove(at: index)
}
print(compareStr)
//abde
extension String {
func index(from: Int) -> Index {
return self.index(startIndex, offsetBy: from)
}
func substring(from: Int) -> String {
let fromIndex = index(from: from)
return String(self[fromIndex...])
}
func substring(to: Int) -> String {
let toIndex = index(from: to)
return String(self[..<toIndex])
}
func substring(with r: Range<Int>) -> String {
let startIndex = index(from: r.lowerBound)
let endIndex = index(from: r.upperBound)
return String(self[startIndex..<endIndex])
}
}
let str = "Hello, playground"
print(str.substring(from: 7)) // playground
print(str.substring(to: 5)) // Hello
print(str.substring(with: 7..<11))
let str = "abcde"
print(str[0])
// 'subscript(_:)' is unavailable: cannot subscript String with an Int, use a String.Index instead.
// Array와 같다고 생각하면 오산
print(str[str.startIndex])
// "a"
// 이렇게 해야한다
print(str[str.endIndex])
// String index is out of bounds
// String.index는 5를 반환한다
let endIndex = str.index(before: str.endIndex) // before - 1, after + 1
print(str[endIndex])
// 이렇게 해야 마지막 인덱스를 설정 가능하다
let startIndex = str.index(str.startIndex, offsetBy: 2) // 세 번째 숫자
print(str[startIndex])
// 중간에 있는 문자 인덱싱은 이렇게
let arrayofstring = Array("Hello") // 각 value는 string.character로 변환되니 주의!!!
var str = "adcadc"
str.replacingOccurrences(of: "d", with: "b") // "abcabc"
- 조건에 부합하는 끝을 잘라준다
var str = "Hello World!"
print(str.trimmingCharacters(in: ["!"]))
let str = String(repeating: "a", count: 5)
// "aaaaa"
let str = "Super long string here"
let filename = getDocumentsDirectory().appendingPathComponent("output.txt")
do {
try str.write(to: filename, atomically: true, encoding: String.Encoding.utf8)
} catch {
// failed to write file – bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
let url = "www.naver.com"
let fileManager = FileManager.default
let documentURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let tokenURL = documentURL.appendingPathComponent("urlToken")
if !fileManager.fileExists(atPath: tokenURL.path) {
try! url.write(to: tokenURL, atomically: false, encoding: .utf8)
}
let text = try! String(contentsOf: tokenURL, encoding: .utf8)
print(text)
- String과 Int는 정렬할 때 다르다
var intArray = [11, 42, 17, 30, 5]
var stringArray = ["11", "42", "17", "30", "5"]
print(intArray.sorted())
print(stringArray.sorted())
// [5, 11, 17, 30, 42]
// ["11", "17", "30", "42", "5"]
guard let filepath = Bundle.main.path(forResource: "xy", ofType: "csv") else {
return
}
let data = try! String(contentsOfFile: filepath)
let csv = data.split(separator: "\n").map{ $0.split(separator: ",") }
1.distance(to: 2)
// 1
var ternary = String(10, radix: 3) // String으로 반환
print(Int(ternary, radix: 3)!) // Int로 반환, Optional
// 10
//2진수를 Int로 변환
String(format: "%.\(total)d", (String(3, radix: 2) as NSString).integerValue)
print(Int("-123")!)
// -123
- UInt는 양수만 저장하는 타입(부호가 없는 정수)
var foo = 111111var bar = 111_111print(foo == bar)
//true
- 유니코드 일반 범주 L* 및 M*에 해당
- A character set containing the characters in Unicode General Category L* & M*.
-
십진수의 반대가(문자열) 해당된다
print("1a3b5".components(separatedBy: CharacterSet.decimalDigits.inverted).joined()) // 135
print(UnicodeScalar(90)!) // Z
print(UnicodeScalar("a").value) // 97
- 겹치는 CharacterSet
- index의 복수
let greeting = "Guten Tag"
for index in greeting.characters.indices {
print(greeting[index])
}
// G
// u
// t
// e
// n
//
// T
// a
// g
부호(1자리) + 지수(8자리) + 가수(23자리) = 32비트
single-precision 32-bit IEEE 754 floating point
let x = 8.625
print(x / 0.75)
// Prints "11.5"
let q = (x / 0.75).rounded(.towardZero)
// q == 11.0
let r = x.truncatingRemainder(dividingBy: 0.75)
// r == 0.375
let x1 = 0.75 * q + r
// x1 == 8.625
부호(1자리) + 지수(11자리) + 가수(52자리) = 64비트
double-precision 64-bit IEEE 754 floating point
- 부동소수점을 사용하면 오차가 발생합니다.
- Float는 소수점 6자리까지 Double은 15자리까지 정확하게 표현할 수 있습니다.
- 10진수를 정확하게 표현할 수는 없다. (무한소수, 순환소수, 비순환소수의 경우 가수부가 표현할 수 있는 비트 수를 넘어가게 되면 손실되는 부분이 생기기 때문, 실수 또한 이진수로 표현하기 때문에 가수부가 1/2^n 꼴로 표현되는 경우만 오차없이 정확하게 값이 계산된다.)
- 신기한 클로저의 세계
var comparator: (Int, Int) -> Bool = (<)
var str = "Ho"
extension StringProtocol {
var asciiValues: [UInt8] {
compactMap(\.asciiValue)
}
}
print(str.asciiValues)
// [72, 111]
print(str.utf16.map( {Int($0)} ))
// [72, 111]
// 소수점 반올림 없이 자르기
let numberFormatter = NumberFormatter()
numberFormatter.roundingMode = .floor // 형식을 버림으로 지정
numberFormatter.minimumSignificantDigits = 2 // 자르길 원하는 자릿수
numberFormatter.maximumSignificantDigits = 2
let originalNum = 1.6759402 // 원하는 숫자
let newNum = numberFormatter.string(from: originalNum) // result 1.67
var arr = [1, 2, 3]
print(arr[1...2])
// [2, 3]
// 출력된 타입은 Array<Int>.SubSequence임을 주의
let arr = ["a","b","c","a"]
let indexOfA = arr.firstIndex(of: "a") // 0
let indexOfB = arr.lastIndex(of: "a") // 3
// Optional(0) Optional(3)
var arr = [3, 4, 5]
arr.firstIndex(where: {$0 == 3})
// Optional(0)
- 해당 Array element와 다른 형식일때 컴파일러는
contentsOf:
를 권한다.
배열에 요소를 추가할 때, 해당 배열이 예약된 용량을 초과하기 시작하면 배열은 더 큰 메모리 영역을 할당하고, 요소를 방금 할당한 새 메모리에 복사합니다. 이때 새로운 저장소는 이전 저장소 크기의 2배입니다.
var arr = [Int]()
arr.append(1)
arr.capacity
// 2
arr.append(1)
arr.capacity
// 2
arr.append(1)
arr.capacity
// 4
// 빈 배열 선언하기
var empty: [Int] = []
var empty2 = [Int]()
var empty3: Array<Int> = []
// 임의의 데이터가 들어가 있는 배열 만들기
var a : Array<Int> = [1,2,3,4]
var b : [Int] = [1,2,3,4]
var c = [1,2,3,4] //타입유추
var d = Array<Int>(1...4) //[1,2,3,4]
// Range
let intArray: [Int] = Array(min...max)
// 응용 - 2차원 배열
var arr = [[Int]](repeating: Array(repeating: 0, count: 1), count: 15)
if let index = csvData.index(where: { $0["email"] as! String == email }) {
csvData.remove(at: index)
return true
}
var foo = [1, 2, 3]
print(foo.contains(where: { $0 > foo[0] }))
// true
for (index,word) in wordsCopy.enumerated() {
//만약 begin과 wodrdCopy에 있는 단어와 한글자만 다르다면
if isJustOneDifferent(begin, word) {
//해당 word를 삭제해줌
wordsCopy.remove(at: index)
//word와 target이 똑같다면
if word == target {
//answer를 반환
return answer
//다르다면
}else{
//다시 changeWord에 begin을 word로 해주고 반복
return changeWord(word, target)
}
}
}
return 0
combining the elements of the sequence
monthDay[0..<a-1].reduce(0, +) + b
var graph = (0..<n).reduce(into: [[Int]]()) { result, _ in
result.append(readLine()!.split(separator: " ").map { Int(String($0))! })
}
- Bool type 중 false 는 return하지 않는다
print([true, true, false, false].filter({ $0 }))
// [true, true]
Swift 4.1부터는 1차원 배열에서 nil을 제거하고 옵셔널 바인딩을 하고싶으실때는 compactMap 사용. 2차원 배열을 1차원 배열로 flatten하게 만들때 flatMap을 사용.
let array1 = [1, nil, 3, nil, 5, 6, 7]
let flatMapTest1 = array1.flatMap{ $0 }
// 'flatMap' is deprecated: Please use compactMap(_:) for the case where closure returns an optional value
let compactMapTest1 = array1.compactMap { $0 }
print("flatMapTest1 :", flatMapTest1)
print("compactMapTest1 :", compactMapTest1)
<출력>
flatMapTest1 : [1, 3, 5, 6, 7]
compactMapTest1 : [1, 3, 5, 6, 7]
ArraySlice always uses contiguous storage and does not bridge to Objective-C.
Warning: Long-term storage of ArraySlice instances is discouraged
Because a ArraySlice presents a view onto the storage of some larger array even after the original array's lifetime ends, storing the slice may prolong the lifetime of elements that are no longer accessible, which can manifest as apparent memory and object leakage. To prevent this effect, use ArraySlice only for transient computation.
-
ArraySlice는 일시적인 저장에만 사용해라
-
ArraySlice는 항상 연속적인 스토리지를 사용하며 Objective-C에 연결되지 않습니다ArraySlice는 원래 배열의 수명이 끝난 후에도 일부 더 큰 배열의 저장소에 보기를 제공하므로 슬라이스를 저장하면 더 이상 액세스할 수 없는 요소의 수명이 연장되어 명백한 메모리 및 개체 누출로 나타날 수 있습니다. 이 효과를 방지하려면 일시적인 계산에만 어레이슬라이스를 사용하십시오.
var continuousNumberArr = Array<Int>(1...5)
print(continuousNumberArr)
// [1, 2, 3, 4, 5]
let isIndexValid = array.indices.contains(index)
let zArry = [Int](repeating: 0, count: 10)
let yArry = [[Int]](repeating: zArry, count: 20)
let xArry = [[[Int]]](repeating: yArry, count: 30)
let val = xArry[2][1][1]
Swift는 ContiguousArray
를 가지는데 C 배열 성능이 필요할 때 사용합니다. classes or @objc protocol 를 사용하는 경우에 ContiguousArray가 성능이 더 좋습니다
var dic : [Int : String] = [:]
var dic2 = [Int : String]()
var dic3 : Dictionary = [Int:String]()
var dic4 : Dictionary<Int, String> = Dictionary<Int, String>()
var countVAlueDict: [String:Int] = [:]
var inputWords = ["a", "b", "b", "c"]
for value in inputWords {
countValueDict[value, default: 0] += 1 // default를 써주면 된다
}
var gradeDic = ["a" : 90, "b" : 80, "c" : 70, "d" : 60]
print(gradeDic["a"] ?? 0) // Nil 병합 연산자, 값이 없을시 0을 출력
- 순서없이 저장, Array와 비슷하되
: Set
붙여줘야 한다 - 고유한 element를 가지도록 보장된다.
var aSet: Set = [11, 12, 13]
aSet.contains(12)
- 받은 set의 상위 set인지(포함하고 있는지)
let set: Set<Int> = [1, 2, 3, 4, 5]
let setB: Set<Int> = [2, 3, 4]
let setC: Set<Int> = [2, 3, 6]
print(set.isSuperset(of: setB)) // true
print(set.isSuperset(of: setC)) // false
- 두 개의 sequence로 구성된 한개의 sequence쌍을 만든다
- 하나만 sequence 넣어도 된다.
func solution(_ progresses:[Int], _ speeds:[Int]) -> [Int] {
return zip(progresses, speeds)
.map { (100 - $0) / $1 }
.reduce(([], 0)) { (tuple, day) in
let (list, lastMax) = tuple
guard let lastCount = list.last else {
return ([1], day)
}
if lastMax >= day {
return (list.dropLast() + [lastCount + 1], lastMax)
}
return (list + [1], day)
}.0
}
func solution2(_ n:Int) -> Int {
var total = 0
Loop :for i in 2...n {
for j in 2...i{
if i % j == 0 && j != i{
continue Loop
}
}
total += 1
}
return total
}
let arrayOfOptionalInts : [Int?] = [nil, 2, 3, nil, 5]
for case let number? in arrayOfOptionalInts where number > 2 {
print ("Found a\(number)")
}
//Found a3
//Found a5
var arr = array == [] ? [-1] : arrayvar isOverPoint = doBlindDateFlag = (point>=80) ? true : false
- 가드는 무엇보다 조건을 빨리 쳐내는 목적이 있는 구문
- 들여쓰기를 줄여 옵셔널 바인딩이 가능하다
func hoho() {
var foo = Optional(1)
guard let item = foo, foo == 0 else {
print("HO")
return
}
}
hoho()
- forEach에서는
break
,continue
구문을 사용할 수 없고, return
을 통해서 빠져나갈 수 있습니다. (continue처럼 동작한다고 일단 생각해보기)
array.forEach {
print($0)
}
//1
//2
//3
//4
//5
let tuples = [(1,2), (1,-1), (1,0), (0,2)]
for tuple in tuples {
switch tuple {
case let (x,y) where x == y: print("같네")
case let (x,y) where x == -y: print("마이너스")
case let (x,y) where x>y: print("크다")
case (1, _): print("x=1")
default: print("\(tuple.0),\(tuple.1)")
}
}
//x=1
//마이너스
//크다
//0,2
switch str {
case _ where str.hasPrefix("Foo"):
print("prefix")
case _ where str.contains("Foo"):
print("contains")
default:
print("nope")
}
let someValue = 5
let someOptional: Int? = nil
switch someOptional {
case .some(someValue):
println("the value is \(someValue)")
case .some(let val):
println("the value is \(val)")
default:
println("nil")
}
- do-while은 먼저 실행하고 조건을 검사한다
- while은 검사하고 실행
do {
square2 += board2[square2]
if ++diceRoll2 == 7 { diceRoll2 = 1 }
square2 += diceRoll2
} while square2 < finalSquare
Swift에서는 기본으로 특정 플랫폼 (iOS, macOS, tvOS, watchOS)과 특정 버전을 확인하는 구문을 제공해 줍니다. 이 구문을 활용해 특정 플랫폼과 버전을 사용하는 기기에 대한 처리를 따로 할 수 있습니다. 구문의 기본 형태는 다음과 같습니다.
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
}
var vendingMachine = VendingMachine()
vendingMachine.conisDeposited = 8
do {
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
print("Success! Yum.")
} catch VendingMachineError.invalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
print("Unexpected error: \(error).")
}
// "Insufficient funds. Please insert an additional 2 coins." 를 출력합니다.
func exampleFunction(inout value: String, index: inout Int) -> Bool
- inout의 경우에는 그 파라미터가 변경될 수 있음을 암시한다
- swift에서 inout은 가급적 쓰지 않도록 하자 - 메모리 관리가 복잡
- 함수가 호출되면, 매개변수로 넘겨진 변수가 복사됩니다.
- 함수 몸체에서, 복사했던 값을 변화시킵니다.
- 함수가 반환될 때, 이 변화된 값을 원본 변수에 재할당합니다.
_ = testInterest(unitDay: 5)
파라미터 갯수를 여러개 넣을 수 있다
func calculateAverage(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / max(1, Double(numbers.count))
}
print(calculateAverage(1, 2, 3))
// 2
func foo(by num: Int)
- 호출할 때 매개변수와 실제 함수를 사용할 때 매개변수 네이밍이 다르다
- 오브젝티브-C와 호환성을 위해서 쓰는 경우도 있다
클로저는 일반적으로 이런 형태를 띕니다.
{ (parameters) -> return type in
statements
}
밑에서 리턴하는 결과는 모두 똑같다
var names: Array<String> = ["Chris", "Alex", "Ewa", "Barry", "Danialla"]
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reverseNames = names.sorted(by:backward)
var reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
var reversedNames = names.sorted(by: { s1, s2 in return s1 > s2}) // Inferring Type From context
var reversedNames = names.sorted(by: {s1, s2 in s1 > s2}) // Implicit Returns from Single-Expression Closures
var reversedNames = names.sorted(by: { $0 > $1 }) // Shorthand Argument Names
var comparator: (Int, Int) -> Bool = (>)
- defer는 클로저 안에 현재 스코프의 맨 뒤에 실행되어야 하는 코드를 넣어 사용한다
- defer는 클로져안에 현재 스코프의 맨 끝에 실행되어야 하는 코드 일부를 넣어서 사용한다.
var a = "Hello"
func b() -> String {
defer { a.append(" world") }
return a
}
func d() -> String {
a.append(" world")
return a
}
a = "Hello"
print(b())
a = "Hello"
print(d())
// Hello
// Hello world
함수가 종료된 뒤에 클로저가 필요한 경우
- 클로저 저장: 글로벌 변수에 클로저를 저장하여 함수가 종료된 후에 사용되는 경우
- 대표적인 예로 비동기 작업이 있는데 디스패치 큐 등을 사용하여 비동기 작업을 수행하는 경우, 함수가 종료된 뒤에도 클로저를 메모리에 잡아두어 비동기 작업을 이어가도록 해야 함
func work(after second: Int, completion: @escaping (() -> Void)) {
let deadline = DispatchTime.now() + .seconds(second)
DispatchQueue.main.asyncAfter(deadline: deadline) {
completion()
}
}
var text = "Hello, World!"
//closure로 변수 할당
var foo: String = {
return text
}()
//Read-Only Computed Properties
var bar: String {
return text
}
print(foo) //Hello, World!
print(bar) //Hello, World!
text = "Hello, okstring!"
print(foo) //Hello, World!
print(bar) //Hello, okstring!
enum Number: Int {
case three = 3, four, five
}
print(Number.five.rawValue)
// 5
enum Drink : Int, CustomStringConvertible {
case americano = 1, latte = 2, frapcinno = 3
var description: String {
switch self {
case .americano:
return "아메리카노"
case .latte:
return "라떼"
case .frapcinno:
return "프라프치노"
}
}
}
enum BookFormat {
case PaperBack (pageCount: Int, price: Double)
case HardCover (pageCount: Int, price: Double)
case PDF (pageCount: Int, price: Double)
case EPub (pageCount: Int, price: Double)
case Kindle (pageCount: Int, price: Double)
var pageCount: Int {
switch self {
case .PaperBack(let pageCount, _):
return pageCount
case .HardCover(let pageCount, _):
return pageCount
case .PDF(let pageCount, _):
return pageCount
case .EPub(let pageCount, _):
return pageCount
case .Kindle(let pageCount, _):
return pageCount
}
}
var price: Double {
switch self {
case .PaperBack(_, let price):
return price
case .HardCover(_, let price):
return price
case .PDF(_, let price):
return price
case .EPub(_, let price):
return price
case .Kindle(_, let price):
return price
}
}
func purchaseTogether(otherFormat: BookFormat) -> Double {
return (self.price + otherFormat.price) * 0.80
}
}
모든 case를 모을 수 있다
enum Stuff: CaseIterable {
case first
case second
case third
case forth
}
print(Stuff.allCases.count) // 4
print(Stuff.allCases) // [TestAndTest.Stuff.first, TestAndTest.Stuff.second, TestAndTest.Stuff.third, TestAndTest.Stuff.forth]
struct BlackjackCard {
// 중첩 열거체 Suit
enum Suit: Character {
case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣"
}
// 중첩 열거체 Rank
enum Rank: Int {
case two = 2, three, four, five, six, seven, eight, nine, ten
case jack, queen, king, ace
struct Values {
let first: Int, second: Int?
}
var values: Values {
switch self {
case .ace:
return Values(first: 1, second: 11)
case .jack, .queen, .king:
return Values(first: 10, second: nil)
default:
return Values(first: self.rawValue, second: nil)
}
}
}
// BlackjackCard 의 속성과 메소드
let rank: Rank, suit: Suit
var description: String {
var output = "suit is \(suit.rawValue),"
output += " value is \(rank.values.first)"
if let second = rank.values.second {
output += " or \(second)"
}
return output
}
}