NSSecureCoding으로 (un)archive

iOS 앱 개발을 하다보면 가끔 겉보기로는 별것 아닌 일에 막대한 시간을 들여 강도 높은 삽질을 하곤 하는데, 기록 차원에서 여기에 좀 남겨야겠다.

Metapho 3.2.4에서 최소 요구사항을 iOS 12로 올렸더니 NSSecureCoding과 관련된 많은 메소드들이 iOS 12에서 deprecated 되었다고 Xcode가 경고한다. WWDC2018 영상을 찾아보니 이것은 NSSecureCoding을 좀 더 권장하기 위한 결정이었다.

핵심은 archive할 때 어떤 클래스가 들어 있었는지 unarchive할 때 지정해 주라는 것.

그러나 하라는데로 했는데 DataNSArray로 바꾸는 것이 영 안 된다.

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의 세계…