NSCodingNSObjectProtocol이라는 클래스 프로토콜이 필요하다. 그리고 그 프로토콜은 구제체가 따를 순 없다. NSCoding을 사용해서 인코딩하고 싶다면 가장 쉬운 방법이 클래스로 만들어 NSObject를 상속받는 것이다.

나는 구조체를 NSCoding으로 감쌀 수 있는 말끔한 방법을 찾았고 거추장스러운 작업 없이 저장할 수 있다. 예제로서 Coordinate를 사용할 것이다.

두개의 스칼라 프로퍼티를 가지는 간단한 한 타입이다. 이제 NSCoding을 따르고 Coordinate를 감싸는 클래스를 만들어보자.

이 로직을 다른 유형으로 사용하는 것이 좋으며 단일 책임 원칙에 더 잘 지킨다. 눈치 빠른 독자는 위 클래스에 Coordinate 프로퍼티의 EncodableCorrdinate가 옵셔널이라는 것을 눈치 챘을 것이지만, 꼭 그럴 필요는 없음을 알 수 있을 것이다. 우리는 옵셔널이 아닌 Coordinate를 받는(혹은 실패하게 만드는) 생성자를 만들 수 있는데, 그렇다면 이미 init(coder:) 메소드는 불가능하다. 그리하여 EnabledCoordinate 클래스 인스턴스를 들고 있을때 항상 coordinate를 가지고 있음을 보장해주어야한다.

그러나 더블(Double)타입(혹은 어떤 원시 타입)을 인코딩 할 때 NSCoder 동작 방법의 특징으로 인해 Any?를 반환하는 decodeObejct(forKey:)로 추출해낼 수 없게 되었다. DoubledecodeDouble(forKey:)로 하여 특정 윈시타입에 특정 메소드가 있다. 불행히도 이 특정 메소드는 옵셔널을 반환하지 않으며, 키가 맞지 않거나 오류가 나면 0.0을 반환해 버린다. 이러한 이유 때문에 coordinate 프로퍼티를 옵셔널로 두기로 했고 옵셔널로 인코딩한다. 그리하여 나는 decodeObject(forKey:)를 사용해 Double?을 얻어냄으로써 추가적인 안정성을 보장했다.

이제 Coordinate 오브젝트를 인코딩/디코딩 하기 위해 EncodableCoordinate를 하나 만들고 NSKeyedArchiver로 디스크에 저장할 수 있다.

이러한 추가적인 오브젝트를 만드는게 이상적이진 않으며, 나는 "가능하면 나를 캐싱해줘" 글에서 사용한 SKCache와같은 오브젝트로 작업하기를 좋아한다. 그리하여 내가 인코더와 인코딩 된 사이의 관계를 형식화 할 수 있다면, 아마 매번 NSCoding 컨테이너를 만들지 않아도 될 것이다.

그 끝으로 두가지 프로토콜을 추가하자.
그리고 우리의 두가지 타입에 대해 적용시킨다.

이렇게하여 이제 타입 시스템은 오브젝트 쌍에서 타입과 값 사이에서 어떻게 변환하여 넣고 빼는지 안다.

블로그 포스팅에서 나온 SKCache 오브젝트는 Encoded 타입을 넘어 제네릭으로 업그레이드 되어왔다. 인코더 값 타입이 그 자신이라는 조건으로, 이것은 이 두 타입 사이에 쌍방향으로 변환할 수 있게 해준다.

이 타입에서 마지막 퍼즐조각인 savefetch 메소드가 남아있다. saveencoder를 잡아두고있고(사실 이것은 NSCoding을 따르는 오브젝트이다) path로 저장해둔다.

패칭은 약간의 컴파일러 댄스(compiler dance)를 포함한다. 우리는 언아카이브(unarchive)된 오브젝트를 T.Encoable로 캐스팅 해야하는데, 이것은 encoder 타입이다. 그리고 그 값을 잡아두고, 동적으로 그것을 캐스팅하여 T로 돌려준다.

이제 캐시를 사용하기위해 한 인스턴스를 만들고 Coordinate 제네릭으로 만든다.

이렇게 하면 좌표 구조체를 쉽게 저장하고 검색할 수 있다.
이것을 가지면 NSCoding을 이용해 구조체를 인코딩할 수 있고, 단일 책임 원칙에 따렴, 타입 세이프티하게 만들어준다. 



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,