복사/붙여넣기를 지원하는 타입을 작성할 때, 해당 타입은 반드시 직렬화 및 역직렬화가 가능해야 했다. 그런데 NSPasteboardReading
프로토콜은 지정 이니셜라이저를 포함하고 있기 때문에 클래스를 직접 수정하거나 서브클래싱하지 않으면 이 방법을 적용할 수가 없다. 따라서 NSPasteboardItem
을 대신 사용하는 방법을 적용해야 한다. 이 때 핵심은 해당 클래스가 어떤 모종의 방법을 사용해서 직렬화 및 역직렬화가 가능해야 한다는 점이다.
Swift의 새로운 데이터 인코딩 방법인 Codable
프로토콜을 사용해서 기존의 ‘간단한’ 데이터 타입을 복사/붙여넣기 하는 방법을 알아보도록 하자. 이 구현의 기본 아이디어는 다음과 같다.
- 모든 표준 Swift 타입들은
Codable
프로토콜을 지원한다. - 모든 프로퍼티가
Codable
인 임의의 타입은 ‘공짜로’Codable
타입이 될 수 있다. - 타입 자체가
Codable
인 경우, JSON이나 프로퍼티 리스트로 간단하게 인코딩/디코딩이 가능하다. NSPasteboardItemDataProvider
프로토콜은 클래스 자체의 수정 없이 클립보드로 데이터를 넘겨줄 수 있다.
그렇다면 이전글에서 작성했었던 Person
클래스를 다시 한 번 다음과 같이 정의할 수 있다.
class Person: Codable
{
var firstName: String
var lastName: String
var fullName: String { reutrn "\(firstName) \(lastName)" }
// 인코딩되는 프로퍼티 키를 정의한다.
enum CodingKeys : CodingKey {
case firstName, lastName
}
init(firstName: String, lastName: String)
{
self.firstName = firstName
self.lastName = lastName
}
}
이제 여기에 NSPasteboardItemDataProvider
프로토콜을 적용하기 위해 확장해보자. 참고로 클립보드에 넘겨주는 타입 구분값은 일반 문자열 타입의 UTI가 아니라 NSPasteboard.PasteboardType
형식으로 정의해야 한다.
// UTI 추가를 위한 타입 확장
extension NSPasteboard.PasteboardType
{
static let person = NSPastebord.PasteboardType("com.sooop.person")
}
// Person 확장
extension Person: NSPasteboardItemDataProvider
{
func pasteboard(_ pasteboard:NSPasteboard?, item: NSPasteboardItem, provideDataForType type: NSPasteboard.PasteboardType)
{
// 문자열일 때와 Person 타입을 요구할 때
// 각 타입의 데이터를 전달한다.
switch type {
case .string:
item.setString(fullName, forType: type)
case .person:
guard let plist = try? PropertyListEncoder().encode(self)
else { return }
item.setData(plist, forType: type)
default:
break
}
}
}
문자열과 Person
, 두 가지 타입에 대해서 각각의 데이터를 페이스트보드 아이템에 넘겨주는 동작을 수행하도록 메소드를 작성했다. 이제 이 타입을 어떻게 복사하는지 살펴보자.
let person = Person(firstName: "Hello", lastName: "World")
let pboard = NSPasteboard.general
pboard.clearContents()
let item = NSPasteboardItem()
item.setDataProvider(person, forTypes: [.string, .person])
pboard.writeObjects([item])
NSPasteboardItem
객체를 하나 만들고, 복사할 값인 person
을 데이터 프로바이더로 세팅해서 아이템 객체를 클립보드에 쓰는 것으로 복사가 완료된다. 이 코드를 실행한 후에 텍스트 편집기나 그외에 텍스트를 붙여넣을 수 있는 곳에서 붙여넣기 하면 “Hello World”가 붙여넣어 질 것이다.
다음은 Person
객체로 붙여넣기를 하는 방법이다. 클립보드에 복사된 Person
타입 객체가 있다면 다음의 코드로 사본을 얻게 된다.
let pboard = NSPasteboard.general
guard let item = pboard.pasteboardItems?.first
, let plist = item.data(forType: .person)
, let p = try? PropertyListDecoder().decode(Person.self, from: plist)
else { return }
NSLog("\(p.fullName) has been pasted.")