Skip to content

yucelokan/KeyValueCoding

Repository files navigation

KeyValueCoding

Swift: 5.x Platforms: iOS, macOS, tvOS, watchOS Swift Package Manager: compatible Build Codecov Swift Doc Coverage

Donate

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

Overview

The basic methods of KeyValueCoding protocol for accessing an instance’s values are setValue(_ value: Any?, key: String), which sets the value for the property identified by the specified key, and value(key: String) -> Any?, which returns the value for the property identified by the specified key. Thus, all of an instance’s properties including properties with enum and Optional types can be accessed in a consistent manner.

In order to make your own instances key-value coding compliant just adopt them from the KeyValueCoding protocol:

enum UserType {
    case none
    case guest
    case user
    case admin
}

class User: KeyValueCoding {
    let id: Int = 0
    let type: UserType = .none
    let name: String = ""
    let SSN: Int? = nil
}

var user = User()

user.setValue(123, key: "id")
user.setValue(UserType.guest, key: "type")
user.setValue("Bob", key: "name")
user.setValue(123456789, key: "SSN")

guard let id = user.value(key: "id") as? Int,
      let type = user.value(key: "type") as? UserType,
      let name = user.value(key: "name") as? String,
      let ssn = user.value(key: "SSN") as? Int
else {
    return
}

print(id, type, name, ssn) // 123 guest Bob 123456789

You can also use subscripts to set and retrieve values by key without needing separate methods for setting and retrieval:

var user = User()

user["id"] = 123
user["type"] = UserType.guest
user["name"] = "Bob"
user["SSN"] = 123456789

guard let id = user["id"] as? Int,
      let type = user["type"] as? UserType,
      let name = user["name"] as? String,
      let ssn = user["SSN"] as? Int
else {
    return
}

print(id, type, name, ssn) // 123 guest Bob 123456789

KeyValueCoding doesn't conflict with key-value conding of NSObject class and they can work together:

class Resolution: NSObject, KeyValueCoding {
    @objc var width = 0
    @objc var height = 0
}

var resolution = Resolution()

// NSObject: setValue(_ value: Any?, forKey key: String)
resolution.setValue(1024, forKey: "width")

// KeyValueCoding: setValue(_ value: Any?, key: String)
resolution.setValue(760, key: "height")

print(resolution.width, resolution.height) // 1024 760

The same works with structs as well:

struct Book: KeyValueCoding {
    let title: String = ""
    let ISBN: Int = 0
}

var book = Book()

book["title"] = "The Swift Programming Language"
book["ISBN"] = 1234567890

print(book) // Book(title: "The Swift Programming Language", ISBN: 1234567890)

In additional there are also global functions to set and get values of properties without adopting KeyValueCoding protocol:

struct Song {
    let name: String = ""
    let artist: String = ""
}

var song = Song()

swift_setValue("Blue Suede Shoes", to: &song, key: "name")
swift_setValue("Elvis Presley", to: &song, key: "artist")

guard let name = swift_value(of: &song, key: "name"),
      let artist = swift_value(of: &song, key: "artist")
else {
    return
}

print(name, "-", artist) // Blue Suede Shoes - Elvis Presley

KeyValueCoding Protocol

Swift instances of struct or class that adopt KeyValueCoding protocol are key-value coding compliant for their properties and they are addressable via essential methods value(key:) and setValue(_: key:).

metadataKind

Returns the metadata kind of the instance.

let user = User()
print(user.metadataKind) // MetadataKind.class

let book = Book()
print(book.metadataKind) // MetadataKind.struct

properties

Returns the array of the instance properties.

let user = User()
user.properties.forEach {
    print($0)
}

Outputs:

PropertyMetadata(name: "id", type: Swift.Int, isStrong: true, isVar: false, offset: 16)
PropertyMetadata(name: "type", type: KeyValueCodingTests.UserType, isStrong: true, isVar: false, offset: 24)
PropertyMetadata(name: "name", type: Swift.String, isStrong: true, isVar: false, offset: 32)
PropertyMetadata(name: "SSN", type: Swift.Optional<Swift.Int>, isStrong: true, isVar: false, offset: 48)

value(key:)

Returns a value for a property identified by a given key.

var user = User()
if let type = user.value(key: "type") as? UserType {
    print(type) // none
}

setValue(_:, key:)

Sets a property specified by a given key to a given value.

var user = User()

user.setValue(UserType.admin, key: "type")

if let type = user.value(key: "type") as? UserType {
    print(type) // admin
}

[key]

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

var user = User()

user["type"] = UserType.guest

if let type = user["type"] as? UserType {
    print(type) // guest
}

Functions

Global functions to set, get and retrieve metadata information from any instance or type without adopting KeyValueCoding protocol.

swift_metadataKind(of:)

Returns the metadata kind of the instance or type.

var song = Song()

print(swift_metadataKind(of: song)) // MetadataKind.struct
// OR
print(swift_metadataKind(of: type(of: song))) // MetadataKind.struct
// OR
print(swift_metadataKind(of: Song.self)) // MetadataKind.struct

swift_properties(of:)

Returns the array of the instance or type properties.

var song = Song()

let properties = swift_properties(of: song)
// OR
swift_properties(of: type(of:song))
// OR
swift_properties(of: Song.self)

properties.forEach {
    print($0)
}

Outputs:

PropertyMetadata(name: "name", type: Swift.String, isStrong: true, isVar: false, offset: 0)
PropertyMetadata(name: "artist", type: Swift.String, isStrong: true, isVar: false, offset: 16)

swift_value(of:, key:)

swift_setValue(_:, to:, key:)

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:

let package = 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.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 100.0%