Created
May 27, 2016 13:36
-
-
Save RoyalIcing/315ee4dfca0c240755b534e1a5ee183f to your computer and use it in GitHub Desktop.
Swift Dynamic Properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public struct Person { | |
public var firstName: String | |
public var middleName: String? | |
public var lastName: String | |
public var ageInYears: Int | |
public var fullName: String { | |
return [firstName, middleName, lastName].flatMap{ $0 }.joinWithSeparator(" ") | |
} | |
} | |
var john = Person(firstName: "John", middleName: nil, lastName: "Doe", ageInYears: 30) | |
/* Getting */ | |
let firstNameState = john[.firstName] // Person.PropertyState.firstName("John") | |
let ageInYearsState = john[.ageInYears] // Person.PropertyState.ageInYears(30) | |
let fullNameState = john[.fullName] // Person.PropertyState.fullName("John Doe") | |
// Mutate ageInYears | |
john.ageInYears = 80 | |
let ageInYearsState2 = john[.ageInYears] // Person.PropertyState.ageInYears(80) | |
/* Initializing keys from strings */ | |
if let validKey = Person.Property(rawValue: "lastName") { // Person.Property.lastName | |
// Get value | |
let validKeyState = john[validKey] // Person.PropertyState.lastName("Doe") | |
} | |
let invalidKey = Person.Property(rawValue: "nonExistant") // nil | |
/* Creating from arbitrary values */ | |
if let | |
validatedFirstName = Person.PropertyState(validating: "Bob", property: .firstName), | |
validatedAgeInYears = Person.PropertyState(validating: 50, property: .ageInYears) | |
{ | |
var bob = john | |
// Make alterations | |
validatedFirstName.set(&bob) | |
validatedAgeInYears.set(&bob) | |
print(bob) // Person(firstName: "Bob", middleName: nil, lastName: "Doe", ageInYears: 50) | |
} | |
// Invalid values | |
let failedFirstName = Person.PropertyState(validating: ["Not going to work"], property: .firstName) // nil | |
let failedAgeInYears = Person.PropertyState(validating: "Expects an int", property: .ageInYears) // nil | |
/* Transfer from one to another */ | |
var alice = Person(firstName: "Alice", middleName: nil, lastName: "Jones", ageInYears: 40) | |
var susan = Person(firstName: "Susan", middleName: nil, lastName: "Smith", ageInYears: 20) | |
// Transfer lastName "Jones" from alice to susan | |
alice[.lastName].set(&susan) | |
print(susan) // Person(firstName: "Susan", middleName: nil, lastName: "Jones", ageInYears: 20) | |
// Attempt to transfer get-only fullName | |
alice[.fullName].set(&susan) // Fatal error (not settable) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Protocols | |
protocol PropertyIdentifierProtocol { | |
var settable: Bool { get } | |
static var publicProperties: [Self] { get } | |
} | |
protocol PropertyStateProtocol { | |
associatedtype Target | |
associatedtype PropertyIdentifier : PropertyIdentifierProtocol | |
init(target: Target, property: PropertyIdentifier) | |
init?<T>(validating: T, property: PropertyIdentifier) | |
var property: PropertyIdentifier { get } | |
func set(inout target: Target) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Implementation automatically provided by compiler: | |
extension Person { | |
public enum Property : String, PropertyIdentifierProtocol { | |
case firstName = "firstName" | |
case middleName = "middleName" | |
case lastName = "lastName" | |
case ageInYears = "ageInYears" | |
case fullName = "fullName" | |
public var settable: Bool { | |
switch self { | |
case .firstName, .middleName, .lastName, .ageInYears: return true | |
case .fullName: return false | |
} | |
} | |
public static var publicProperties: [Property] { | |
return [.firstName, .middleName, .lastName, .ageInYears, .fullName] | |
} | |
} | |
public enum PropertyState : PropertyStateProtocol { | |
case firstName(String) | |
case middleName(String?) | |
case lastName(String) | |
case ageInYears(Int) | |
case fullName(String) | |
public typealias Target = Person | |
public typealias Identifier = Property | |
public init(target: Target, property: Property) { | |
switch property { | |
case .firstName: self = .firstName(target.firstName) | |
case .middleName: self = .middleName(target.middleName) | |
case .lastName: self = .lastName(target.lastName) | |
case .ageInYears: self = .ageInYears(target.ageInYears) | |
case .fullName: self = .fullName(target.fullName) | |
} | |
} | |
public init?<T>(validating: T, property: Property) { | |
switch validating { | |
case let string as String: | |
switch property { | |
case .firstName: self = .firstName(string) | |
case .middleName: self = .middleName(string) | |
case .lastName: self = .lastName(string) | |
default: return nil | |
} | |
case let integer as Int: | |
switch property { | |
case .ageInYears: self = .ageInYears(integer) | |
default: return nil | |
} | |
default: | |
return nil | |
} | |
} | |
public var property: Property { | |
switch self { | |
case .firstName: return .firstName | |
case .middleName: return .middleName | |
case .lastName: return .lastName | |
case .ageInYears: return .ageInYears | |
case .fullName: return .fullName | |
} | |
} | |
public func set(inout target: Target) { | |
switch self { | |
case let .firstName(value): target.firstName = value | |
case let .middleName(value): target.middleName = value | |
case let .lastName(value): target.lastName = value | |
case let .ageInYears(value): target.ageInYears = value | |
default: | |
fatalError("Property \(property) is not settable") | |
} | |
} | |
} | |
public subscript(property: Property) -> PropertyState { | |
return PropertyState(target: self, property: property) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, I read your gist with great interest. In the third file you say it is "Implementation automatically provided by compiler". Could you give more information about what this compiler is?