210523 Sun

Sunny·2021년 6월 2일
0

Today I Learned

목록 보기
60/88

1. 첫 번째 학습 내용: Keychain

Keychain Services

Securely store small chunks of data on behalf of the user.

The keychain services API helps you solve this problem by giving your app a mechanism to store small bits of user data in an encrypted database called a keychain.

The keychain is not limited to passwords, as shown in Figure 1. You can store other secrets that the user explicitly cares about, such as credit card information or even short notes. You can also store items that the user needs but may not be aware of. For example, the cryptographic keys and certificates that you manage with Certificate, Key, and Trust Services enable the user to engage in secure communications and to establish trust with other users and devices. You use the keychain to store these items as well.

Article Storing Keys in the Keychain

2. 두 번째 학습 내용: guard 구문

  • guard 구문은 if 구문과 마찬가지로 주어진 조건식/표현식의 결과가 참인지 거짓인지에 따라 구문의 실행 여부를 결정짓는 방식의 조건문
  • if 구문과의 차이점은 guard 구문에는 else 블록이 필수이지만, 표현식의 결과가 참일 때 실행되는 블록이 없음
guard <조건식 또는 표현식> else {
		<조건식 또는 표현식의 결과가 false일 때 실행될 코드>
}

guard 구문을 사용하는 이유

  • 주로 후속 코드들이 실행되기 전에 특정 조건을 만족하는지 확인하는 용도로 사용

= 특정 조건을 만족하지 않은 채로 후속 코드를 실행하면 심각한 오류가 발생할 경우에, 전체 구문을 조기 종료(Early Exit)하기 위한 목적으로 사용됨

  • 따라서 guard 구문의 else 블록에는 이후의 코드 진행을 막아주는 구문이 반드시 포함되어야 함 (e.g. return, break 구문 등)
  • guard 구문은 보통 함수나 메소드에서 사용되는데, 이때에는 return 구문이 이같은 조기 종료 처리 역할을 함
import Foundation

func divide(base: Int) {
    let result = 100 / base
    print(result)
}

입력받은 값을 받아 100을 나누기
단! 0으로 나눌 경우에는 오류가 발생함
이런 경우를 방지하기 위해 다음과 같이 guard 구문 사용 가능

func divide(base: Int) {

    guard base != 0 else {
        print("연산할 수 없습니다.")
        return
    }
    
    let result = 100 / base
    print(result)
}

divide(base: 0) // 연산할 수 없습니다.
divide(base: 5) // 20

if문으로 바꾸면 아래와 같이 조건식이 반대로 바뀜

func divide(base: Int) {

    if base == 0 {
        print("연산할 수 없습니다.")
        return
    }
    
    let result = 100 / base
    print(result)
}

guard 구문을 이용하여 인자값을 다양한 조건으로 필터링하는 코드

import Foundation

func divide(base: Int) {
    guard base != 0 else {
        print("연산할 수 없습니다.")
        return
    }
    guard base > 0 else {
        print("base는 0보다 커야 합니다.")
        return
    }
    guard base < 100 else {
        print("base는 100보다 작아야 합니다.")
        return
    }
    let result = (100 / base)
    print(result)
}

divide(base: -1) // base는 0보다 커야 합니다.
divide(base: 200) // base는 100보다 작아야 합니다.

3. 세 번째 학습 내용: 옵셔널 바인딩 guard let/ if let

  • guard 구문은 함수나 메소드에만 사용할 수 있기 때문에 intStr() 함수를 정의하고 안에 guard 구문을 작성
  • guard 구문은 조건에 맞지 않으면 무조건 함수의 실행을 종료시키는 특성이 있기 때문에, 실행 흐름상 옵셔널 값이 해제되지 않으면 더 이상 진행이 불가능할 정도로 큰일이 생길 때에만 사용하는 것이 좋음
import Foundation

var str = "Swift"

func intStr(str: String) {
    
    guard let intFromStr = Int(str) else {
        print("값 변환에 실패하였습니다")
        return
    }
    
    print("값이 변환되었습니다. 변환된 값은 \(intFromStr)입니다.")
}

intStr(str: str) // 값 변환에 실패하였습니다

if let으로 옵셔널 바인딩 처리

var str = "Swift"
if let intFromStr = Int(str) {
    print("값이 변환되었습니다. 변환된 값은 \(intFromStr)입니다.")
} else {
    print("값 변환에 실패하였습니다")
}
// 값 변환에 실패하였습니다

출처

꼼꼼한 재은씨의 Swift: 문법편

4. 네 번째 학습 내용: 연산 프로퍼티 (Computed Properties)

class/struct/enum 객체명 {
    var 프로퍼티명: 타입 {
        get {
            필요한 연산 과정
            return 반환값
        }
        set(매개변수명) {
            필요한 연산구문
        }
    }
}

연산 프로퍼티는 다른 프로퍼티에 의존적이거나, 혹은 특정 연산을 통해 얻을 수 있는 값을 정의할 때 사용됨
e.g. 개인 정보 중 - 나이
나이는 출생 연도에 의존적이며, 현재 연도를 기준으로 계산해야 하므로 매년 그 값이 달라짐

import Foundation

struct UserInfo {
    // 저장 프로퍼티: 태어난 연도
    var birth: Int!
    
    // 연산 프로퍼티: 올해가 몇년도인지 계산
    var thisYear: Int! {
        get {
            let df = DateFormatter()
            df.dateFormat = "yyyy"
            return Int(df.string(from: Date()))
        }
        
        // 연산 프로퍼티: (올해 - 태어난 연도) + 1
        var age: Int {
        get {
             return (self.thisYear - self.birth) + 1
            }
        }
    }
}

let info = UserInfo(birth: 1980)
print(info.age)

꼼꼼한 재은씨 예제 그대로 따라친건데 안됨 🤔

Error message

  • Expected 'get', 'set', 'willSet', or 'didSet' keyword to start an accessor definition
  • Value of type 'UserInfo' has no member 'age'

5. 다섯 번째 학습 내용: Error Handling

There are four ways to handle errors in Swift.

  • You can propagate the error from a function to the code that calls that function

  • handle the error using a do-catch statement

  • handle the error as an optional value

  • assert that the error will not occur.

  • Propagating Errors Using Throwing Functions

    import Foundation
    
    enum VendingMachineError: Error {
        case invalidSelection
        case insufficientFunds(coinsNeeded: Int)
        case outOfStock
    }
    
    struct Item {
        var price: Int
        var count: Int
    }
    
    class VendingMachine {
        var inventory = [
            "Candy Bar": Item(price: 12, count: 7),
            "Chips": Item(price: 10, count: 4),
            "Pretzels": Item(price: 7, count: 11)
        ]
        var coinsDeposited = 0
        
        func vend(itemNamed name: String) throws {
            guard let item = inventory[name] else {
                throw VendingMachineError.invalidSelection
            }
            
            guard item.count > 0 else {
                throw VendingMachineError.outOfStock
            }
            
            guard item.price <= coinsDeposited else {
                throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
            }
            
            coinsDeposited -= item.price
            
            var newItem = item
            newItem.count -= 1
            inventory[name] = newItem
            
            print("Dispensing \(name)")
        }
    }
    
    let favoriteSnacks = [
        "Alice": "Chips",
        "Bob": "Licorice",
        "Eve": "Pretzels",
    ]
    func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
        let snackName = favoriteSnacks[person] ?? "Candy Bar"
        try vendingMachine.vend(itemNamed: snackName)
    }
    
    struct PurchasedSnack {
        let name: String
        init(name: String, vendingMachine: VendingMachine) throws {
            try vendingMachine.vend(itemNamed: name)
            self.name = name
        }
    }
  • Handling Errors Using Do-Catch

    var vendingMachine = VendingMachine()
    vendingMachine.coinsDeposited = 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 coninsNeeded) {
        print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
    } catch {
        print("Unexpected error: \(error).")
    }
profile
iOS Developer

0개의 댓글