decode 타입 지정 오류 해결
NSCoding과 NSKeyed(Un)Archiver
디바이스를 껐다 켜도 데이터가 영구적으로 남아있도록 하기 위해 직접 만든 클래스가 NSCoding
이라는 프로토콜의 정의를 따르도록 했다. 자세하게 알아보지 않고 그냥 사용법만 대충 보고 구현해봤다. NSCoding
프로토콜의 정의를 따르기 위해서는 requred init
과 encode
메소드를 구현해야 한다. 개인적으로 이해한 바로는 encode
가 데이터를 인코딩 하는 것이고 requred init
에서는 인코딩된 데이터를 디코딩하여 쓰는 것이라 생각하고 있다. 위의 메소드를 직접 불러서 사용하기 보다는 NSKeyedArchiver
와 NSKeyedUnarchiver
를 통해 자동으로 불려지는 것 같다.
decode 타입 지정 오류
encode
할 때에는 데이터 타입에 따라 구분이 없지만 디코딩 할때에는 다음과 같이 타입에 따라 다르게 써야한다. 처음에 타입의 다양성을 제대로 보지 않고 그냥 다 Object로 디코딩하고 뒤에 as로 타입을 확인하는 식으로 했다. 그랬더니 문제가 생겼다.
required init?(coder: NSCoder) {
guard let brand = coder.decodeObject(forKey: "brand") as? String,
let capacity = coder.decodeObject(forKey: "capacity") as? Int,
let price = coder.decodeObject(forKey: "price") as? Int,
let name = coder.decodeObject(forKey: "name") as? String,
let manufacturedDate = coder.decodeObject(forKey: "manufacturedDate") as? Date,
let shelfLife = coder.decodeObject(forKey: "shelfLife") as? Date,
let calorie = coder.decodeObject(forKey: "calorie") as? Int else {
return nil
}
self.brand = brand
self.capacity = capacity
self.price = price
self.name = name
self.manufacturedDate = manufacturedDate
self.shelfLife = shelfLife
self.calorie = calorie
}
func encode(with coder: NSCoder) {
coder.encode(self.brand, forKey: "brand")
coder.encode(self.capacity, forKey: "capacity")
coder.encode(self.price, forKey: "price")
coder.encode(self.name, forKey: "name")
coder.encode(self.manufacturedDate, forKey: "manufacturedDate")
coder.encode(self.shelfLife, forKey: "shelfLife")
coder.encode(self.calorie, forKey: "calorie")
}
decodeObject
말고 다른 것을 쓸 수 있는 것에 대해서 바꿔줬더니 해당 오류는 해결되었다.
required init?(coder: NSCoder) {
guard let brand = coder.decodeObject(forKey: "brand") as? String,
let name = coder.decodeObject(forKey: "name") as? String,
let manufacturedDate = coder.decodeObject(forKey: "manufacturedDate") as? Date,
let shelfLife = coder.decodeObject(forKey: "shelfLife") as? Date
else { return nil }
self.brand = brand
self.capacity = coder.decodeInteger(forKey: "capacity")
self.price = coder.decodeInteger(forKey: "price")
self.name = name
self.manufacturedDate = manufacturedDate
self.shelfLife = shelfLife
self.calorie = coder.decodeInteger(forKey: "calorie")
}
XCTAssertEqual의 클래스 비교 오류
그렇지만 이번엔 다른 오류가 발생하였다. 분명 static func ==
을 정의해줬음에도 해당 클래스의 description이 나왔다. 심지어 description이 동일함에도 불구하고 xcode는 beverage1
과 beverage2
가 다르다고 판단했다.
override var description: String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
//제조사, 용량, 가격, 브랜드, 제조일자
return "\(name), \(capacity)ml, \(price)원, \(brand), \(formatter.string(from: manufacturedDate))"
}
static func ==(lhs: Beverage, rhs: Beverage) -> Bool {
if lhs.brand != rhs.brand { return false }
if lhs.capacity != rhs.capacity { return false }
if lhs.price != rhs.price { return false }
if lhs.name != rhs.name { return false }
if lhs.manufacturedDate != rhs.manufacturedDate { return false }
if lhs.shelfLife != rhs.shelfLife { return false }
if lhs.calorie != rhs.calorie { return false }
return true
}
아무래도 XCTAssertEqual
가 ==
으로 비교하는 것이 아니고 객체 그 자체 즉 메모리 주소까지 비교하는 ===
을 사용하는 것 같아서 찾아보니 그런 듯 하였다.
XCTAssertEqual for custom objects in Swift
오류 해결
결국 참고 문서에 나온 대로 XCTAssertEqual
대신 XCTAssert
를 사용하였더니 문제는 해결되었다.
func testNSCoding() throws {
let brand = "조지아"
let capacity = 200
let price = 2000
let name = "고티카"
let shelfLife = 100
let calorie = 130
let beverage1 = Beverage(brand: brand, capacity: capacity, price: price, name: name, shelfLife: shelfLife, calorie: calorie)
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: beverage1, requiringSecureCoding: false) else {
XCTAssert(false, "beverage1 아카이브 실패")
return
}
guard let beverage2 = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Beverage else {
XCTAssert(false, "beverage1 언아카이브 실패")
return
}
XCTAssert(beverage1 == beverage2)
}
'iOS > iOS' 카테고리의 다른 글
iOS) UITableView로 채팅 UI 만들기(1) (0) | 2020.09.23 |
---|---|
iOS) NSCoding과 Archive를 통한 데이터 저장 - (2) (0) | 2020.09.20 |
iOS) 레이아웃(1) - 인터페이스 빌더 (0) | 2020.08.23 |
iOS) Scene(4) - 코드로 뷰 제어 (0) | 2020.08.23 |
iOS) Scene(3) - 코드로 씬 작성 (0) | 2020.08.17 |