NSSecureCoding으로 (un)archive
iOS 앱 개발을 하다보면 가끔 겉보기로는 별것 아닌 일에 막대한 시간을 들여 강도 높은 삽질을 하곤 하는데, 기록 차원에서 여기에 좀 남겨야겠다.
Metapho 3.2.4에서 최소 요구사항을 iOS 12로 올렸더니 NSSecureCoding
과 관련된 많은 메소드들이 iOS 12에서 deprecated 되었다고 Xcode가 경고한다. WWDC2018 영상을 찾아보니 이것은 NSSecureCoding
을 좀 더 권장하기 위한 결정이었다.
핵심은 archive할 때 어떤 클래스가 들어 있었는지 unarchive할 때 지정해 주라는 것.
그러나 하라는데로 했는데 Data
를 NSArray
로 바꾸는 것이 영 안 된다.
let array = [CLLocation(latitude: 0, longitude: 0)]
let data = try! NSKeyedArchiver.archivedData(withRootObject: array, requiringSecureCoding: true)
let unarchivedArray = try! NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: data)
// Fatal error
한참을 삽질하다가 애플 개발자 포럼의 포스트 하나를 보고 대오각성. CLLocation
같은 object들이 들어있는 Collection (Array, Dictionary, Set)을 unarchive할 때는 unarchiveObject(ofClass:from:)
이 아니라 unarchiveObejct(ofClasses:from:)
에서 ofClasses에 모든 원소들의 클래스를 때려 넣어야 하는 것이었다.
let unarchivedArray = try! NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, CLLocation.self], from: data)
애플 개발자 포럼이 Stack overflow보다 도움이 된 흔치 않은 경험.
그런데, unarchiveTopLevelObjectWithData(_:) throws
를 사용하면 클래스지정 없이도 멀쩡하게 unarchive된다. 그러나 쓰기 찜찜한 부분이… 알수없게도 iOS 12를 기준으로 NSKeyedArchiver.h
헤더를 보면 Objective-C에서는 +unarchiveTopLevelObjectWithData:error:
가 deprecated 인데, Swift에서는 아니다? 오묘한 ObjC interoperability의 세계…