Mirror, Keypath, DynamicMemberLookUp - Team-HGD/SniffMEET GitHub Wiki

setValue(Any, forKey: String) firebase ... ๊ธฐํƒ€ DB ์ž‘์—… ๋“ฑ์—์„œ String์œผ๋กœ ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์™•์™• ์žˆ๋‹ค.

๊ทธ๋Ÿผ ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ถ”๊ฐ€๋ ๋•Œ๋งˆ๋‹ค Constant๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ๋˜๋Š”๊ฑด๊ฐ€? -> ์—๊ตฌ ๊ท€์ฐฎ๋‹ค... ์‹ค์ˆ˜ํ•˜๊ธฐ๋„ ๋”ฑ ์ข‹๋‹ค!

๊ทธ๋ž˜์„œ ์ƒˆ๋กœ์šด ๋ฐฉ์‹์œผ๋กœ ํŽธ์˜ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด๋ณด๋ฉด ์–ด๋–จ๊นŒ ์‹ถ์—ˆ๋‹ค.

๋ฌผ๋ก  Decodable์„ ์ฑ„ํƒํ•œ ๋‹ค์Œ, json ํŒŒ์ผ์„ ํ†ตํ•ด property์˜ ์ด๋ฆ„์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์ง€๋งŒ, ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„์— ์ ‘๊ทผํ•  ๋•Œ๋งˆ๋‹ค Decoding์„ ํ•ด์ค˜์•ผ ํ•œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

์šฐ์„  ๋Œ€์ถฉ setValue ์— ํ•„์š”ํ•œ๊ฑด ์†์„ฑ์˜ ์ด๋ฆ„, ํƒ€์ž…, ๊ฐ’ ์ด๋ ‡๊ฒŒ ์„ธ๊ฐ€์ง€ ์ •๋„๊ฒ ๋‹ค.

๊ทธ๋Ÿผ ๊ฐ๊ฐ์˜ ์ •๋ณด๋ฅผ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์„๊นŒ?

Mirror

์ฒซ๋ฒˆ์งธ๋กœ ์†์„ฑ์˜ ์ด๋ฆ„์„ ์•Œ๊ธฐ ์œ„ํ•ด์„œ ์“ฐ๋Š” ์นœ๊ตฌ๋‹ค.

substructure๊ณผ display style์„ ํ‘œํ˜„ํ•œ๋‹ค.

์žฌ๋ฐŒ๊ฒŒ๋„ Mirror๋ฅผ ์“ฐ๋ฉด private ์†์„ฑ์—๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ์•ˆ ์“ฐ๋Š” ๊ฒŒ ๋‹น๊ทผ ์ข‹๋‹ค.

[[iOS - swift] private ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ• (#Mirror, private var, private let ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ)](https://ios-development.tistory.com/1523)

์—ด๊ฑฐํ˜•์ธ์ง€, ๊ตฌ์กฐ์ฒด์ธ์ง€ ํŒ๋‹จ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค. pointcofree์—์„œ๋Š” ์ด ๊ธฐ๋Šฅ์œผ๋กœ ์žฌ๋ฐŒ๋Š” ์นœ๊ตฌ๋ฅผ ๋งŒ๋“ค๊ธฐ๋„ ํ–ˆ๋‹ค.

Keypath

Keypath๋ฅผ ํ†ตํ•ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

Keypath<RootType, PropertyType> ๊ฐ€ ๊ธฐ๋ณธ์ ์ธ ๋ฌธ๋ฒ•์ด๋‹ค.

์˜ˆ์‹œ๋ฅผ ๋“ค๋ฉด

struct Member {
    let name: String
    let age: Int
}

let member = Member(name: "ํ—ˆ๊ฑฐ๋ฉ", age: 10)
let keypath: Keypath<Member, String> = \.name
member[keyPath: \Member.name] // ํ—ˆ๊ฑฐ๋ฉ 
member[keyPath: \.age] // 10 

@dynamicMemberLookUp

๋™์  ๋ฉค๋ฒ„ ์ ‘๊ทผ์ด๋ผ๋Š” ์นœ๊ตฌ์ธ๋ฐ, ๋งŽ์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์นœ๊ตฌ์ด์ง€๋งŒ ๋Ÿฐํƒ€์ž„์— ์–ด๋–ค ์ด๋ฆ„์˜ ๋ฉค๋ฒ„๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํŒ๋‹จํ•˜๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ์‹œ๋ฅผ ๋“ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

@dynamicMemberLookup
struct Book {

	subscript(dynamicMember member: String) -> String {
		return member
	}
}

let book: Book = .init() 
book.์šฐ์™€ // ์šฐ์™€ 
book.ํ  // ํ  

๋ณดํ†ต ๋”•์…”๋„ˆ๋ฆฌ์™€ ์กฐํ•ฉ์ด ๊ดœ์ฐฎ๊ธฐ๋„ ํ•˜๊ณ  ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค. ์•„๋ž˜์ฒ˜๋Ÿผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

@dynamicMemberLookup

struct Cache {
	private var map: [String: String] = ["์šฐ์™€" : "wowa"]
	subscript(dynamicMember member: String) -> String {
		return map[member]!
	}
}

let book: Book = .init() 
book.์šฐ์™€ // "wowa"

์˜ˆ์ „์—๋Š” member๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์ด String์œผ๋กœ ํ•œ์ •๋˜์—ˆ๋Š”๋ฐ (Swift 4)

์ตœ๊ทผ๋ถ€ํ„ฐ Keypath๋ฅผ ํ†ตํ•œ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ด์กŒ๋‹ค๊ณ  ํ•œ๋‹ค. (Swift 5.1)

์ด ํฌ์ŠคํŠธ์—์„œ ๋ชฉ์  ๋‹ฌ์„ฑ์€ ๋ชปํ–ˆ๋Š”๋ฐ, ์‚ฌ์‹ค ์ด ์นœ๊ตฌ๋ฅผ ๋„์ž…ํ•˜๊ณ  ์‹ถ์—ˆ๋˜ ์ด์œ ๋„ Keypath๋ฅผ ํ†ตํ•œ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ด์กŒ๊ธฐ ๋•Œ๋ฌธ์ด์—ˆ๋‹ค.

๊ฒฐ๊ณผ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

@dynamicMemberLookup
struct Proxy<T> {
    private let wrappedValue: T
    private var mapping: [String: Property] = [:]
    var allProperties: [Property] {
        Array(mapping.values)
    }
    
    init(wrappedValue: T) {
        self.wrappedValue = wrappedValue
        Mirror(reflecting: wrappedValue).children
            .compactMap {
                guard let label = $0.label else { return nil }
                return (label, Property(name: label,
                                        type: type(of: $0.value),
                                        value: $0.value))
            }
            .forEach{ key, value in
                mapping[key] = value
            }
    }
    
    subscript(dynamicMember member: String) -> Property? {
        mapping[member]
    }
}

์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์—์„œ Proxy๋ฅผ ์ ์šฉํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งŒ๋“ค์–ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

let dog: Dog = Dog(name: "ํ›„์ถ”์ถ”", size: .superSmall, keyword: [.energetic])
let managedObject = NSManagedObject(entity: dogEntity, insertInto: context)
managedObject.setValue(dog.keyword.map{ $0.rawValue }, forKey: "keyword")
managedObject.setValue(dog.name, forKey: "name")
managedObject.setValue(dog.size.rawValue, forKey: "size")
let dogProxy: Proxy<Dog> = .init(wrappedValue: dog)
dogProxy.allProperties {
    managedObject.setValue($0.value, forKey: $0.name)
}

๋”ฐ๋ผ์„œ ์ œ๋„ค๋ฆญ์œผ๋กœ๋„ ํ‘œํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

let proxy: Proxy<T> = .init(wrappedValue: T.init())
proxy.allProperties {
	managedObject.setValue($0.value, forKey: $0.name)
}

๋ฌธ์ œ๊ฐ€ ํ•˜๋‚˜ ์žˆ๋‹ค๋ฉด CoreData์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ proxy์™€ ๋งค์น˜์‹œ์ผœ์ฃผ์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค.

์žฅ์ : ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ถ”๊ฐ€๋  ๋•Œ๋งˆ๋‹ค ์ƒ์ˆ˜ ์ถ”๊ฐ€ํ•  ํ•„์š” ์—†์Œ. ๊ด€๋ฆฌ์˜ ํŽธ์˜์„ฑ

๋‹จ์ : ๋Ÿฐํƒ€์ž„์— ๊ฒฐ์ •๋˜๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์ƒ ์ด์Šˆ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.

๋” ํ•ด๋ณผ๊ฑฐ๋ฆฌ

๋™์  Keypath ๊ตฌ์„ฑํ•˜๊ธฐ

์‚ฌ์‹ค ์ด๊ฒŒ ์˜๋ฏธ๊ฐ€ ์žˆ์œผ๋ ค๋ฉด String ํƒ€์ž…์œผ๋กœ dynamicMember๋ฅผ ๊ตฌ์„ฑํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

๋™์  Keypath๋ฅผ ์ƒ์„ฑํ•˜๊ณ  keypath๋ฅผ ํ†ตํ•ด ์†์„ฑ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š”๋ฐ, (ํƒ€์ž… ์•ˆ์ •์„ฑ ๋•Œ๋ฌธ)

๋™์  Keypath๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ Swift ์—์„œ ์ œ๊ณตํ•˜๊ณ  ์žˆ์ง€ ์•Š์•„์„œ ์šฐํšŒ ๋ฐฉ๋ฒ•์„ ์ฐพ๋Š” ์ค‘โ€ฆ ์ข‹์€ ์•„์ด๋””์–ด ๋ฐ›์Šต๋‹ˆ๋‹ค.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ