본문은 야곰 아카데미 커리어 스타터 캠프를 통해 학습한 내용을 회고한 글입니다.
Mirror
모든 유형의 인스턴스에 대한 하위 구조 및 표시 스타일의 표현
iOS 8.0+
iPadOS 8.0+
macOS 10.10+
Mac Catalyst 13.0+
tvOS 9.0+
watchOS 2.0+
Declaration
struct Mirror
Overview
mirror는 인스턴스의 저장 프로퍼티, 컬렉션이나 튜플 요소들 혹은 active enumeration case와 같은 특정 인스턴스를 구성하는 부분을 설명한다. Mirror는 또한 이 mirror가 렌더링 될 수 있는 방법을 제안하는 “display style” 프로퍼티를 제공한다.
Playgrounds와 debugger는 Mirror 타입을 사용하여 모든 유형의 값 표현을 표시한다. 예를 들어서, 인스턴스를 dump(::::) 함수에 전달하면, 해당 인스턴스의 런타임 콘텐츠를 렌더링하는 데에 mirror가 사용된다.
struct Point {
let x: Int, y: Int
}
let p = Point(x: 21, y: 30)
print(String(reflecting: p))
// Prints "▿ Point
// - x: 21
// - y: 30"
mirror 표현의 커스텀 타입을 커스텀하려면 CustomReflectable 프로토콜에의 적합성을 추가하라.
--- # init(reflecting: Any)
주어진 인스턴스를 반영하는 mirror를 만든다.
iOS 8.0+
iPadOS 8.0+
macOS 10.10+
Mac Catalyst 13.0+
tvOS 9.0+
watchOS 2.0+
Declaration
init(reflecting subject: Any)
Parameters
subject
mirror를 생성할 인스턴스
Discussion
subject의 dynamic type이 CustomReflectable을 준수하는 경우, mirror는 customMirror 프로퍼티에 의해 결정된다. 그렇지 않으면, 결과는 언어에 의해 생성된다.
만약 subject의 dynamic type에 값 의미론이 있는 경우, subject의 그 다음의 변화는 Mirror에서 관찰 될 수 없다. 하지만, 일반적으로, 변화의 관찰가능성은 지정되지 않는다.
children: Mirror.Children
반영된 subject의 구조를 설명하는 Child 요소의 컬렉션.
iOS 8.0+
iPadOS 8.0+
macOS 10.10+
Mac Catalyst 13.0+
tvOS 9.0+
watchOS 2.0+
Declaration
let children: Mirror.Children
제네릭한 CoreDataManager를 만들고 싶어서 용쓰다가 찾아본 친구..
struct Dog {
let name: String
let sex: String
let birthday: Int
var age: Int
}
let hodoo = Dog(name: "hodoo", sex: "girl", birthday: 170227, age: 6)
let hodooMirror = Mirror(reflecting: hodoo)
let hodooMirrorChildren = hodooMirror.children
print(hodooMirrorChildren.compactMap { $0.label }) //["name", "sex", "birthday", "age"]
print(hodooMirrorChildren.compactMap { $0.value }) //["hodoo", "girl", 170227, 6]
print(hodooMirrorChildren.compactMap({ $0.self })) //[(label: Optional("name"), value: "hodoo"), (label: Optional("sex"), value: "girl"), (label: Optional("birthday"), value: 170227), (label: Optional("age"), value: 6)]
for children in hodooMirrorChildren {
print(children)
}
/*
(label: Optional("name"), value: "hodoo")
(label: Optional("sex"), value: "girl")
(label: Optional("birthday"), value: 170227)
(label: Optional("age"), value: 6)
*/
요런 결과가 나왔달까..
그래서 어떻게 사용했냐면..
import CoreData
import Foundation
class CoreDataManager<T> {
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Diary")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
var context: NSManagedObjectContext {
return persistentContainer.viewContext
}
var diaryEntity: NSEntityDescription? {
return NSEntityDescription.entity(forEntityName: "Entity", in: context)
}
func createDiary(_ diary: T) {
if let entity = diaryEntity {
let managedObject = NSManagedObject(entity: entity, insertInto: context)
let properties = Mirror(reflecting: diary).children.filter { $0.label != nil }
for property in properties {
guard let label = property.label else { return }
managedObject.setValue(property.value, forKey: label)
}
do {
try self.context.save()
} catch {
print(error.localizedDescription)
}
}
}
func readDiary() -> [Diary]? {
...
return diaries
}
func updateDiary(diary: T) {
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Entity")
let properties = Mirror(reflecting: diary).children.filter { $0.label != nil }
guard let id = properties[0].value as? any CVarArg else { return }
fetchRequest.predicate = NSPredicate(format: "id == %@", id)
do {
let objects = try context.fetch(fetchRequest)
let objectToUpdate = objects[0]
let properties = Mirror(reflecting: diary).children.filter { $0.label != nil && $0.label != "id" }
for property in properties {
guard let label = property.label else { return }
objectToUpdate.setValue(property.value, forKey: label)
}
do {
try self.context.save()
} catch {
print(error.localizedDescription)
}
} catch {
print(error.localizedDescription)
}
}
func deleteDiary(diary: T) throws {
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Entity")
let properties = Mirror(reflecting: diary).children.filter { $0.label != nil }
guard let id = properties[0].value as? any CVarArg else { return }
fetchRequest.predicate = NSPredicate(format: "id == %@", id)
do {
let objects = try context.fetch(fetchRequest)
let objectToDelete = objects[0]
context.delete(objectToDelete)
do {
try context.save()
} catch {
throw error
}
} catch {
throw error
}
}
}
이런식으로 read를 제외한 나머지 아이들을 T로 사용할 수 있게 만들었다.
위의 코드가 돌아가긴 한다.. 신기하게도...
mirror를 사용할 때 중요한 건 인스턴스를 반영한다는 것..
근데 이해 안 가는건..
value가 죄다 String인데, Diary의 각각의 프로퍼티들은 다양한 타입이란 말이지:
struct Diary {
let id: UUID
var title: String
var body: String
var date: Double
}
id는 제외하고 넣었다고 쳐도.. date는 어떻게 들어간거지...?
약간 공부를 하고도 잘 모르겠는 것 투성이다🥲😂
'YAGOM CAREER STARTER' 카테고리의 다른 글
[iOS] 비전공자 이대생의 야곰 아카데미 수료 후기(비전공자 iOS개발자 되기, 새싹, 포스텍 애플 디벨로퍼 아카데미) (0) | 2023.06.19 |
---|---|
[TIL] 20230515: CoreData/SQLite/Realm (0) | 2023.05.17 |
[TIL] 20230509: Core Location, Getting the current location of a device (0) | 2023.05.09 |
[토요스터디A반] 20230408: KeyChain (0) | 2023.04.14 |
[TIL] 20230411: URL Loading System (0) | 2023.04.14 |