Error throwing initializer 만들기

SteadySlower·2022년 11월 1일
0

iOS Development

목록 보기
13/38
post-custom-banner

Error를 던지는 initializer가 필요한 이유

아래는 제가 선언한 구조체입니다. dictionary를 받아서 각각의 stored property를 초기화하고 있습니다. 하지만 이 initializer는 코드로는 무조건 성공할 수 있게 되어 있지만 결과적으로는 실패할 수 있는 여지를 가지고 있습니다.

studyState라는 property를 예로 들어보겠습니다. 이 property는 enum인 StudyState 타입을 가지고 있습니다. 그리고 init을 할 때 일단 dictionary 안에 있는 rawValue를 Int로 캐스팅하고 그 rawValue를 사용해서 다시 enum의 인스턴스를 만들어서 property에 할당합니다.

이 과정에서 dict["studyState"]가 nil인 경우, dict["studyState”]를 Int로 캐스팅하는 것을 실패한 경우, 마지막으로 StudyState(rawValue: rawValue)가 nil인 경우 3가지 케이스의 경우 해당 init은 실패했다고 간주해야 합니다.

하지만 아래에 구현된 initializer의 경우 ??를 통해 기본값을 넣어줌으로서 일단 init이 되도록 하고 있습니다. 하지만 올바른 방법은 아닙니다. 위 예시와 같은 문제가 생기는 경우 error를 던지도록 initializer를 수정해보겠습니다.

import Foundation

enum StudyState: Int {
    case undefined = 0, success, fail
}

protocol Word {
    var id: String { get }
    var wordBookID: String { get }
    var meaningText: String { get }
    var meaningImageURL: String { get }
    var ganaText: String { get }
    var ganaImageURL: String { get }
    var kanjiText: String { get }
    var kanjiImageURL: String { get }
    var studyState: StudyState { get set }
    var timestamp: Date { get }
    var hasImage: Bool { get }
}

struct WordImpl: Word {

    let id: String
    let wordBookID: String
    let meaningText: String
    let meaningImageURL: String
    let ganaText: String
    let ganaImageURL: String
    let kanjiText: String
    let kanjiImageURL: String
    var studyState: StudyState
    let timestamp: Date

    var hasImage: Bool {
        !self.meaningImageURL.isEmpty || !self.ganaImageURL.isEmpty || !self.kanjiImageURL.isEmpty
    }

    // TODO: Handle Parsing Error
    init(id: String, wordBookID: String, dict: [String : Any]) {
        self.id = id
        self.wordBookID = wordBookID
        self.meaningText = dict["meaningText"] as? String ?? ""
        self.meaningImageURL = dict["meaningImageURL"] as? String ?? ""
        self.ganaText = dict["ganaText"] as? String ?? ""
        self.ganaImageURL = dict["ganaImageURL"] as? String ?? ""
        self.kanjiText = dict["kanjiText"] as? String ?? ""
        self.kanjiImageURL = dict["kanjiImageURL"] as? String ?? ""
        let rawValue = dict["studyState"] as? Int ?? 0
        self.studyState = StudyState(rawValue: rawValue) ?? .undefined
        self.timestamp = dict["timestamp"] as? Date ?? Date()
    }
}

Error Throwing Initializer

일반적인 함수나 메소드와 마찬가지로 에러를 던지는 initializer도 마찬가지로 throws라는 키워드를 써서 에러를 던지는 메소드라는 것을 알려줍니다.

그리고 내부에서 if문을 통해서 stored property에 할당할 변수가 nil이 된다면 에러를 던지도록 구현하면 됩니다.

init(id: String, wordBookID: String, dict: [String : Any]) throws {
    self.id = id
    self.wordBookID = wordBookID
    
    if let meaningText = dict["meaningText"] as? String,
       let meaningImageURL = dict["meaningImageURL"] as? String,
       let ganaText = dict["ganaText"] as? String,
       let ganaImageURL = dict["ganaImageURL"] as? String,
       let kanjiText = dict["kanjiText"] as? String,
       let kanjiImageURL = dict["kanjiImageURL"] as? String,
       let rawValue = dict["studyState"] as? Int,
       let studyState = StudyState(rawValue: rawValue),
       let createdAt = dict["createdAt"] as? Date
    {
        self.meaningText = meaningText
        self.meaningImageURL = meaningImageURL
        self.ganaText = ganaText
        self.ganaImageURL = ganaImageURL
        self.kanjiText = kanjiText
        self.kanjiImageURL = kanjiImageURL
        self.studyState = studyState
        self.createdAt = createdAt
    } else {
        throw AppError.Initializer.wordImpl
    }
}
profile
백과사전 보다 항해일지(혹은 표류일지)를 지향합니다.
post-custom-banner

0개의 댓글