์ด์ ๊ธ๋ค์์ Objective C์ KeyPath์ Swift์์์ KeyPath๋ฅผ ํ๋ฒ์ฉ ๋ณด์๋ค. ๋ชจ์์ด ์ข ๋ฌ๋์๋๋ฐ ์ ๋ค๋ฅธ์ง์ ๋ํด์ ์์๋ณด์. ์ด๋ฒ ๊ธ์ WWDC 17์ ๊ธฐ์ค์ผ๋ก ํ๋ค.
KeyPath in Swift 3
์์ KVC, KVO in Swift ๊ธ์์ ๋ฐ๋ผ๋ณธ KeyPath์ด์ ์ ์ฌ์ฉ๋๋ KeyPath๊ฐ ์์๋ค.
// Swift 3 String Key Paths
@objcMembers class Kid : NSObject {
dynamic var nickname: String = ""
dynamic var age: Double = 0.0
dynamic var bestFriend: Kid? = nil
dynamic var friends: [Kid] = []
}
var ben = Kid(nickname: "Benji", age: 5.5)
let kidsNameKeyPath = #keyPath(Kid.nickname) // Swift 3
let name = ben.valueForKeyPath(kidsNameKeyPath)
ben.setValue("Ben", forKeyPath: kidsNameKeyPath)#์ ์ฌ์ฉํ KeyPath๋ฅผ ํตํด Swift์์ KVC๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ด์๋ค. Compile time์๋ #keyPath(Kid.nickname)์ String์ผ๋ก ๋ณ๊ฒฝํ๊ณ Objective C Runtime์ ํ์ฉํด์ ๊ฐ์ฒด๋ฅผ ์ฐพ๊ฒ ๋๋ค. ์ฆ, ์ด๋ ๊ฒ ๊น์ํ ๋ฐฉ์์ผ๋ก KeyPath๋ฅผ ์ฌ์ฉํ์ฌ, ๊ธฐ์กด์ String์ ์ฌ์ฉํ์ ๋๋ณด๋ค ์์ ์ฑ์ ๋ํ์์๋ ๋ถ๊ตฌํ๊ณ , ๊ทผ๋ณธ์ Objective C runtime์ ์ฌ์ฉํ๊ณ ์๋ค๋ ๊ฒ์ด๋ค. Type์ ๋ํ ์ ๋ณด๋ ์๋ ์์ํ String์ด๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ Return value๋ Any๋ก ๋ฐ์์ด์ผ ํ๋ค.
valueForKeyPath(_: String) -> Any
setValue(_, forKeyPath: String) -> AnyNew KeyPath

Swift์์ ์ ๊ณตํ๋ KeyPath์ ๋ชจ์์ ์์ ๊ฐ์ด ๋ณ๊ฒฝ๋์๋ค.

Swift์ด๊ธฐ ๋๋ฌธ์ Type ์ถ๋ก ๋ ๊ฐ๋ฅํ๋ค. ๋ํ Sequence๋ก ์ฐ๊ฒฐํด์ ์ฌ์ฉํ๋ ๊ฒ ์ญ์ ๊ฐ๋ฅํ๋ค.

Optional๋ ๊ฐ๋ฅํ๊ณ , Subscript๋ ๊ฐ๋ฅํ๋ค.

Subscript๋ก ์ง์ ์ฌ์ฉ๋ ๊ฐ๋ฅํ๋ฉฐ, Type ์ถ๋ก ๊น์งํ๋ฉด ์๋์ ๊ฐ์ด ์ฌ์ฉ๋ ํ ์ ์๋ค.
// Using Swift 4 KeyPaths
struct BirthdayParty {
let celebrant: Kid
var theme: String
var attending: [Kid]
}
let bensParty = BirthdayParty(celebrant: ben, theme: "Construction", attending: [])
let birthdayKid = bensParty[keyPath: \BirthdayParty.celebrant] // == \.celebrant
bensParty[keyPath: \BirthdayParty.theme] = "Pirate" // == \.theme๊ทธ๋์ ์ต์ข ์ ์ผ๋ก ๋ณ๊ฒฝ์ฌํญ์ ์ ์ฉํ๋ฉด ์ด์ ๊ฐ๊ฒ ๋๋ค. Objective-C runtime์ ์ฌ์ฉํ์ง ์๊ณ ๋ฐ๋ก ์ ์ฉ์ด ๊ฐ๋ฅํ๋ค. ๋ํ Value type์์๋ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค!
KeyPath Type

let birthdayKidsAgeKeyPath = \BirthdayParty.celebrant.age // KeyPath<BirthDayParty, Double>
let birthdayBoysAge = bensParty[keyPath: birthdayKidsAgeKeyPath] // DoubleKeyPath๋ผ๋ Type์ด ์๊ฒผ๋ค. Base Type๊ณผ Property์ ์ค์ Type์ ์ ์ด์ ์ ์ธํ ์ ์๋ค.
func partyPersonsAge(party: BirthdayParty,
participantPath: KeyPath<BirthdayParty, Kid>) -> Double {
let kidsAgeKeyPath = participantPath.appending(\.age)
return party[keyPath: kidsAgeKeyPath]
}Type์ผ๋ก ์๋ KeyPath๋ฅผ ๋ฐ์ ์ํ๋ Type์ ๋ฆฌํดํ๋ ํจ์๋ฅผ ๋ง๋ค ์๋ ์๊ฒ ๋ค.
The .appending Rule
์ด๋ฐ ํน์ง๋ค์ ํตํด KeyPath๋ผ๋ฆฌ appendํ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ค.


๋ณด๋ฉด ์๊ฒ ์ง๋ง, ์์ฐจ์ ์ผ๋ก Type์ด ๋ง๋ ๊ฒฝ์ฐ ์ฐ์ฐ์ ํตํด ์ต์ข ์ ์ผ๋ก ์ํ๋ KeyPath๋ฅผ ์ป์ ์ ์๋ค.
Other KeyPaths

| Type | Description |
|---|---|
AnyKeyPath | ํ์ ์ด ์ง์์ง KeyPath |
PartialKeyPath | ๋ถ๋ถ์ ์ผ๋ก ํ์ ์ด ์ง์์ง KeyPath |
KeyPath | Read-only getํ๋ ์ฉ๋๋ก๋ง ์ฌ์ฉ๊ฐ๋ฅ |
WriteableKeyPath | Value type instance์ ์ฌ์ฉ๊ฐ๋ฅ โ๋ณ๊ฒฝ ๊ฐ๋ฅํโ ๋ชจ๋ Property์ ๋ํด( var) read & write access ์ ๊ณต |
ReferenceWriteableKeyPath | Reference type instance์ ์ฌ์ฉ๊ฐ๋ฅ โ๋ณ๊ฒฝ ๊ฐ๋ฅํโ ๋ชจ๋ Property์ ๋ํด( var) read & write access ์ ๊ณต |
๋ง์ฝ ๊ฐ์ KeyPath๋ฅผ ๊ณ์ํด์ ์ ์ด์ค์ผ ํ๋ค๋ฉด ์ผ๋ง๋ ๋ถํธํ ๊น?
let bensParty = BirthdayParty(celebrant: ben, theme: "Construction", attending: [])
let birthdayKid = bensParty[keyPath: \BirthdayParty.celebrant] // == \.celebrant
bensParty[keyPath: \BirthdayParty.theme] = "Pirate"
bensParty[keyPath: \BirthdayParty.theme] = "Villan"
bensParty[keyPath: \BirthdayParty.theme] = "Hero"์ด๋ฐ ๊ฒฝ์ฐ ์ด๋ฅผ Type์ผ๋ก ์ ์ํ ๋ค ์ฌ์ฌ์ฉํ๋ฉด ํธํ ๊ฒ ๊ฐ๋ค. ์ด๋ฐ ๊ฒฝ์ฐ writeableKeyPath๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. Value Type์ Property์ ๋ํด write๊น์ง ๊ฐ๋ฅํด์ผํ๊ธฐ ๋๋ฌธ์ด๋ค.
struct BirthdayParty {
let celebrant: Kid
var theme: String
var attending: [Kid]
}
let bensParty = BirthdayParty(celebrant: ben, theme: "Construction", attending: [])
let birthdayKid = bensParty[keyPath: \BirthdayParty.celebrant] // == \.celebrant
let writeableKeyPath = \BirthdataParty.theme
bensParty[keyPath: writeableKeyPath] = "Pirate"
bensParty[keyPath: writeableKeyPath] = "Villan"
bensParty[keyPath: writeableKeyPath] = "Hero"์ด๋ฐ์์ผ๋ก ๋ค์ํ KeyPath๊ฐ ์ค๋น๋์ด ์๋ค. ์ง๊ธ ๊ฐ์ ๊ฒฝ์ฐ๋ ์์ ํ๋ ์์ (write)์ ํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์๋์ผ๋ก ํ์ ์ถ๋ก ํ์ฌ writeableKeyPath๊ฐ ๋ ์ํ์ด๋ค. ๊ทธ๋์ ๋ณ์๋ช ๋ ๊ทธ๋ ๊ฒ ์ง์ด์ฃผ์๋ค.
class BirthdayParty { // Changed
let celebrant: Kid
var theme: String
var attending: [Kid]
}
let bensParty = BirthdayParty(celebrant: ben, theme: "Construction", attending: [])
let birthdayKid = bensParty[keyPath: \BirthdayParty.celebrant] // == \.celebrant
let referenceWriteableKeyPath = \BirthdataParty.theme
bensParty[keyPath: referenceWriteableKeyPath] = "Pirate"
bensParty[keyPath: referenceWriteableKeyPath] = "Villan"
bensParty[keyPath: referenceWriteableKeyPath] = "Hero"์ด๋ฒ์๋ Class๋ก ๋ณ๊ฒฝํด์ฃผ์๋ค. ์ด ๊ฒฝ์ฐ์๋ type ์ถ๋ก ํ์ฌ ReferenceWriteableKeyPath์ผ๋ก ๋ณ๊ฒฝ๋๋ค.
class BirthdayParty {
let celebrant: Kid
let theme: String // Changed
var attending: [Kid]
}
let bensParty = BirthdayParty(celebrant: ben, theme: "Construction", attending: [])
let birthdayKid = bensParty[keyPath: \BirthdayParty.celebrant] // == \.celebrant
let keyPath = \BirthdataParty.theme
bensParty[keyPath: keyPath] = "Pirate" // Cannot Assign through subscript: 'keypath' is a read-only key path
bensParty[keyPath: keyPath] = "Villan" // Cannot Assign through subscript: 'keypath' is a read-only key path
bensParty[keyPath: keyPath] = "Hero" // Cannot Assign through subscript: 'keypath' is a read-only key path๋ง์ฝ ๋ณ๊ฒฝํ๊ณ ์ ํ๋ property์ ์ ์ธ์ด let์ผ๋ก ๋ณ๊ฒฝ๋๋ฉด ์ด๋จ๊น? ์ด ๊ฒฝ์ฐ์๋ type ์ถ๋ก ์ด ๋์ํ์ฌ KeyPath๋ก ๋ณ๊ฒฝ๋๋ค. ์ด ๊ฒฝ์ฐ, write ๋์์ด ๋ถ๊ฐํ๋ค.
Key Path ํจ์๋ก ์ฌ์ฉํ๊ธฐ
Swift5.2 ์์๋ Key Path Expressions ์ ํจ์๋ก ์ฌ์ฉํ ์ ์๋๋ก ํ๋ Proposal์ด ๊ตฌํ๋์๋ค. \Root.value๋ฅผ ์ ์์ผ๋ก์จ (Root) -> Value์ ์ ๊ฐ์ ํจ์ ํํ๋ก ์ฌ์ฉํ ์ ์๋ค. ์ฌ๊ธฐ์ rootType์ keypath๊ฐ ๊ฐ์ง๊ณ ์๋ ํ์
์ด๋ฆ์ด๋ค.
ํด๋น ์ด์ ๋ฐ์ ์๋, users.map { $0.email }๊ณผ ๊ฐ์ ๊ตฌ๋ฌธ์ด ์์ ๋, ๋ณด๋ค ๊น๋ํ๊ฒ ํํํ๋ ๋ฐฉ๋ฒ์ด ์๋ค ํ๊ณ , ๊ทธ ๋ต์ด keyPath๋ผ ํ๋ค. ์ต์ข
์ ์ผ๋ก users.map(\.email)๊ณผ ๊ฐ์ด ์ฌ์ฉํ๋ ๊ฒ์ ์ ์ํ๊ณ , ์ด๋ฅผ keypath๋ฅผ ์
๋ ฅํ์ ์ ์ด๋ฅผ clouser ํํ๋ก ๋ณํํ๋ ๊ฒ์ผ๋ก ๊ฐ๋ฅํ๊ฒ ํ์๊ณ ํ๋ค.
// You write this:
let f: (User) -> String = \User.email
// The compiler generates something like this:
let f: (User) -> String =
{
kp in {
root in root[keyPath: kp]
}
}(\User.email)\User.email์ด๋ผ๊ณ ์ ์ ์, Compiler๊ฐ ์ด๋ฅผ ์๋์ ๊ฐ์ function์ผ๋ก ์์ฑํ๊ณ , ์ด๋ฅผ ํตํด ์์ ์ ์ ๋์์ ๊ฐ๋ฅํ๊ฒ ํ์ ์ ์ํ๋ค. ์ค์ ๋ก ๊ตฌํ๋์ด ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
๋ง๋ฌด๋ฆฌ
์ด๋ ๊ฒ ์ ๋ฒ ๊ธ์์ ์ ๋๋ก ์์๋ณด์ง ๋ชปํ๋ KeyPath์ ๋ํด์ ์ ๋ฆฌํด๋ณด์๋ค. ๋!