Skip to content

Repository files navigation

KeyValueCoding

Swift 5.10, 5.9, 5.8, 5.7Platforms: iOS, macOS, tvOS, visionOS, watchOSSwift Package Manager: compatibleBuildCodecovSwift Doc Coverage

Donate

KeyValueCoding provides a mechanism by which you can access the properties of pure Swift struct or class instances indirectly by a string key or a key path.

Getting Started

Basics

The basic approach of KeyValueCoding protocol for accessing an instance’s properties values is subscripting by string key or a key path. In order to make your types key-value coding compliant just adopt them from this protocol, for instance:

import KeyValueCoding
structResolution:KeyValueCoding{letwidth:Intletheight:Int}varresolution=Resolution(width:640, height:480)resolution["width"]=1920resolution["height"]=1080print(resolution) // Prints: Resolution(width: 1920, height: 1080)

NOTE: An instance variable must be declared as var, otherwise you'll have the following error: Cannot use mutating getter on immutable value.

The same works with the key paths as well:

resolution[\Resolution.width]=2560resolution[\Resolution.height]=1440print(resolution) // Prints: Resolution(width: 2560, height: 1440)

You can also read properties values in the same way:

iflet width:Int=resolution[\Resolution.width],let height:Int=resolution[\Resolution.height]{print("\(width)x\(height)") // Prints: 2560x1440
}

The following properties can be accessible for classes and structs in a consistent manner including:

  • Constant let and variable var properties.
  • lazy, @objc and optional properties.
  • Properties with any access level: public, internal, private etc.
  • Properties of any type: enum, struct, class, tuple etc.
  • Relationship properties by a string key or a key path.

But there are some limitations:

  • Computed properties are not addressable.
  • The willSet and didSet observers aren’t being called on changing values.
  • weak, unowned and the property wrappers are not supported.

Relationship

KeyValueCoding can access to relationship properties by a string key ("relationship.property") or a key path, for example:

import KeyValueCoding
structResolution{letwidth:Intletheight:Int}classVideoMode:KeyValueCoding{letname:Stringletresolution:Resolutioninit(name:String, resolution:Resolution){self.name = name
self.resolution = resolution
}}varvideoMode=VideoMode(name:"HD", resolution:Resolution(width:1920, height:1080))print("\(videoMode.name) - \(videoMode.resolution.width)x\(videoMode.resolution.height)")
// Prints: HD - 1920x1080
videoMode[\VideoMode.name]="4K"videoMode[\VideoMode.resolution.width]=3840videoMode[\VideoMode.resolution.height]=2160print("\(videoMode.name) - \(videoMode.resolution.width)x\(videoMode.resolution.height)")
// Prints: 4K - 3840x2160

NOTE: Your parent instance can access to its children's properties without conforming the children to KeyValueCoding protocol.

Class Inheritance

Properties from inherited classes are also accessible by KeyValueCoding protocol:

import KeyValueCoding
classMode{letname:Stringinit(name:String){self.name = name
}}classVideoMode:Mode,KeyValueCoding{letframeRate:Intinit(name:String, frameRate:Int){self.frameRate = frameRate
super.init(name: name)}}varvideoMode=VideoMode(name:"HD", frameRate:30)print("\(videoMode.name) - \(videoMode.frameRate)fps")
// Prints: HD - 30fps
videoMode[\VideoMode.name]="4K"videoMode[\VideoMode.frameRate]=25print("\(videoMode.name) - \(videoMode.frameRate)fps")
// Prints: 4K - 25fps

Protocols

You can inherit any protocol from KeyValueCoding and then all instances of this protocol will be to accessible to read and write their properties:

import KeyValueCoding
protocolSize:KeyValueCoding{varwidth:Int{get}varheight:Int{get}}structResolution:Size{letwidth:Intletheight:Int}varresolution:Size=Resolution(width:1920, height:1080)print(resolution)
// Prints: Resolution(width: 1920, height: 1080)
resolution[\Resolution.width]=3840resolution[\Resolution.height]=2160iflet width:Int=resolution[\Resolution.width],let height:Int=resolution[\Resolution.height]{print("\(width)x\(height)")
// Prints: 3840x2160
}

Advanced functions

In additional you can use pure API functions for getting and setting values of an instance's properties without adoptingKeyValueCoding protocol at all:

import KeyValueCoding
structResolution{letwidth:Intletheight:Int}varresolution=Resolution(width:1920, height:1080)print(resolution)
// Prints: Resolution(width: 1920, height: 1080)
swift_setValue(3840, to:&resolution, keyPath: \Resolution.width)swift_setValue(2160, to:&resolution, keyPath: \Resolution.height)iflet width =swift_value(of:&resolution, keyPath: \Resolution.width)as?Int,let height =swift_value(of:&resolution, keyPath: \Resolution.height)as?Int{print("\(width)x\(height)")
// Prints: 3840x2160
}

Methods

Swift instances of struct or class that adopt KeyValueCoding protocol are key-value coding compliant for their properties and they are addressable via essential subscriptions [key] and [keyPath].

metadata

Returns the metadata of the instance which includes its type, kind, size and a list of accessible properties:

import KeyValueCoding structResolution:KeyValueCoding{letwidth:Intletheight:Int}letresolution=Resolution(width:1920, height:1080)print(resolution.metadata)

Prints:

Metadata(type: Resolution, kind: .struct, size: 16, properties: [
Property(name: 'width', isStrong: true, isLazy: false, isVar: false, offset: 0), Property(name: 'height', isStrong: true, isLazy: false, isVar: false, offset: 8)
])

[key]

Gets and sets a value for a property identified by a string key.

import KeyValueCoding
structResolution:KeyValueCoding{letwidth:Intletheight:Int}varresolution=Resolution(width:1920, height:1080)resolution["width"]=2048iflet width:Int=resolution["width"]{print(width) // Prints: 2048
}

[keyPath]

Gets and sets a value for a property identified by a key path.

import KeyValueCoding
structResolution:KeyValueCoding{letwidth:Intletheight:Int}varresolution=Resolution(width:1920, height:1080)resolution[\Resolution.width]=2048iflet width:Int=resolution[\Resolution.width]{print(width) // Prints: 2048
}

API

Global API functions to set, get and retrieve metadata information from any instance or type even without adoptingKeyValueCoding protocol.

swift_metadata

Returns the metadata of an instance or a type which includes its type, kind, size and a list of accessible properties:

import KeyValueCoding
structResolution{letwidth:Intletheight:Int}letresolution=Resolution(width:1920, height:1080)varmetadata=swift_metadata(of: resolution)
// OR
metadata =swift_metadata(of:type(of: resolution))
// OR
metadata =swift_metadata(of:Resolution.self)print(metadata)

Prints:

Metadata(type: Resolution, kind: .struct, size: 16, properties: [
Property(name: 'width', isStrong: true, isLazy: false, isVar: false, offset: 0), Property(name: 'height', isStrong: true, isLazy: false, isVar: false, offset: 8)
])

swift_value

Returns the value for the instance's property identified by a given string key or a key path.

import KeyValueCoding
structResolution{letwidth:Intletheight:Int}varresolution=Resolution(width:1920, height:1080)iflet width =swift_value(of:&resolution, key:"width")as?Int{print(width) // Prints: 1920
}
// OR iflet width =swift_value(of:&resolution, keyPath: \Resolution.width)as?Int{print(width) // Prints: 1920
}

swift_setValue

Sets a property of an instance specified by a given string key or a key path to a given value.

import KeyValueCoding
structResolution{letwidth:Intletheight:Int}varresolution=Resolution(width:1920, height:1080)swift_setValue(2048, to:&resolution, key:"width")
// OR
swift_setValue(2048, to:&resolution, keyPath: \Resolution.width)print(resolution) // Prints: Resolution(width: 2048, height: 1080)

Installation

XCode

  1. Select Xcode > File > Add Packages...
  2. Add package repository: https://github.com/ikhvorost/KeyValueCoding.git
  3. Import the package in your source files: import KeyValueCoding

Swift Package

Add KeyValueCoding package dependency to your Package.swift file:

letpackage=Package(...
dependencies:[.package(url:"https://github.com/ikhvorost/KeyValueCoding.git", from:"1.0.0")],
targets:[.target(name:"YourPackage",
dependencies:[.product(name:"KeyValueCoding",package:"KeyValueCoding")]),......)

License

KeyValueCoding is available under the MIT license. See the LICENSE file for more info.

Donate

About

Key-value coding (KVC) for pure Swift.

Topics

Resources

Stars

35 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages