Can you simultaneously define and instantiate implicit types in Swift? - swift

Just messing around with the language thinking of how I want to structure some UserDefaults that automatically generate keys based on the hierarchy. That got me wondering... Is it possible to simultaneously define, and instantiate a type, like this?
let myUserSettings = {
let formatting = {
var lastUsedFormat:String
}
}
let lastUsedFormat = myUserSettings.formatting.lastUsedFormat
Note: I can't use statics because I specifically need instancing so nested structs/classes with static members will not work for my case.
Here's the closest thing I could come up with, but I hate that I have to create initializers to set the members. I'm hoping for something a little less verbose.
class DefaultsScope {
init(_ userDefaults:UserDefaults){
self.userDefaults = userDefaults
}
let userDefaults:UserDefaults
func keyForSelf(property:String = #function) -> String {
return "\(String(reflecting: self)).\(property)"
}
}
let sharedDefaults = SharedDefaults(UserDefaults(suiteName: "A")!)
class SharedDefaults : DefaultsScope {
override init(_ userDefaults:UserDefaults){
formatting = Formatting(userDefaults)
misc = Misc(userDefaults)
super.init(userDefaults)
}
let formatting:Formatting
class Formatting:DefaultsScope {
let maxLastUsedFormats = 5
fileprivate(set) var lastUsedFormats:[String]{
get { return userDefaults.stringArray(forKey:keyForSelf()) ?? [] }
set { userDefaults.set(newValue, forKey:keyForSelf()) }
}
func appendFormat(_ format:String) -> [String] {
var updatedListOfFormats = Array<String>(lastUsedFormats.suffix(maxLastUsedFormats - 1))
updatedListOfFormats.append(format)
lastUsedFormats = updatedListOfFormats
return updatedListOfFormats
}
}
let misc:Misc
class Misc:DefaultsScope {
var someBool:Bool{
get { return userDefaults.bool(forKey:keyForSelf()) }
set { userDefaults.set(newValue, forKey:keyForSelf()) }
}
}
}
So is there a simpler way?

Disclaimer: this is, probably, just an abstract solution that should not be used in real life :)
enum x {
enum y {
static func success() {
print("Success")
}
}
}
x.y.success()
Update: Sorry, folks, I can't stop experimenting. This one looks pretty awful :)
let x2= [
"y2": [
"success": {
print("Success")
}
]
]
x2["y2"]?["success"]?()
Update 2: One more try, this time with tuples. And since tuples must have at least two values, I had to add some dummies in there. Also, tuples cannot have mutating functions.
let x3 = (
y3: (
success: {
print("Success")
},
failure: {
print("Failure")
}
),
z3: 0
)
x3.y3.success()

How about you try nesting some swift structs?
struct x {
struct y {
static func success() {
print("success")
}
}
}
x.y.success()

You cannot have that kind of structure but you cant access y from inside x, since y is only visible inside the scope of x and so is success inside the scope of y. There is no way that you can access them from outside
One other alternative is to have higher order function like so, which return closure which is callable.
let x = {
{
{
print("Success")
}
}
}
let y = x()
let success = y()
success()
or
x()()()
The real world usage of higher order function for userdefaults could be something like this,
typealias StringType = (String) -> ((String) -> Void)
typealias IntType = (String) -> ((Int) -> Void)
typealias BoolType = (String) -> ((Bool) -> Void)
typealias StringValue = (String) -> String?
typealias IntValue = (String) -> Int?
typealias BoolValue = (String) -> Bool?
func userDefaults<T>(_ defaults: UserDefaults) -> (String) -> ((T) -> Void) {
return { key in
return { value in
defaults.setValue(value, forKey: key)
}
}
}
func getDefaultsValue<T>(_ defaults: UserDefaults) -> (String) -> T? {
return { key in
return defaults.value(forKey: key) as? T
}
}
let setStringDefaults: StringType = userDefaults(.standard)
setStringDefaults("Name")("Jack Jones")
setStringDefaults("Address")("Australia")
let setIntDefaults: IntType = userDefaults(.standard)
setIntDefaults("Age")(35)
setIntDefaults("Salary")(2000)
let setBoolDefaults: BoolType = userDefaults(.standard)
setBoolDefaults("Married")(false)
setBoolDefaults("Employed")(true)
let getStringValue: StringValue = getDefaultsValue(.standard)
let name = getStringValue("Name")
let address = getStringValue("Address")
let getIntValue: IntValue = getDefaultsValue(.standard)
let age = getIntValue("Age")
let salary = getIntValue("Salary")
let getBoolValue: BoolValue = getDefaultsValue(.standard)
let married = getBoolValue("Married")
let employed = getBoolValue("Employed")
I am not sure if you like the pattern, but it has some good use cases as you can see from below, setStringDefaults you can set strings value to string key and all of them are typesafe.
You can extend this for your use case. But, you could use struct as well and use imperative code, which could be easier to understand. I see beauty in this as well.

Ok, I think I've figured it out. This first class can go in some common library that you use for all your apps.
class SettingsScopeBase {
private init(){}
static func getKey(setting:String = #function) -> String {
return "\(String(reflecting:self)).\(setting)"
}
}
The next part is a pair of classes:
The 'Scoping' class where you define which user defaults instance to use (along with anything else you may want to specify for this particular settings instance)
The actual hierarchy that defines your settings
Here's the first. I'm setting this up for my shared settings between my application and it's extension:
class SharedSettingsScope : SettingsScopeBase{
static let defaults = UserDefaults(suiteName: "group.com.myco.myappgroup")!
}
And finally, here's how you 'set up' your hierarchy as well as how you implement the properties' bodies.
class SharedSettings:SharedSettingsScope{
class Formatting:SharedSettingsScope{
static var groupsOnWhitespaceOnlyLines:Bool{
get { return defaults.bool(forKey: getKey()) }
set { defaults.set(newValue, forKey: getKey()) }
}
}
}
And here's how you use them...
let x = SharedSettings.Formatting.groupsOnWhitespaceOnlyLines
// x = false
SharedSettings.Formatting.groupsOnWhitespaceOnlyLines = true
let y = SharedSettings.Formatting.groupsOnWhitespaceOnlyLines
// y = true
I'm going to see if I can refine/optimize it a little more, but this is pretty close to where I want to be. No hard-coded strings, keys defined by the hierarchy where they're used, and only setting the specific UserDefaults instance in one place.

Related

How to use type-erase in Swift

Story:
I have some layouts.
A layout have a pattern and keys. The layout can make message from these.
Each patterns have maximum number of keys.
That is my code to expression templates.
protocol LayoutPattern {
static var numberOfKeys: Int { get }
static func make(with keys: [String]) -> String
}
struct Pattern1: LayoutPattern {
static let numberOfKeys: Int = 1
static func make(with keys: [String]) -> String {
return "Pattern 1:" + keys.joined(separator: ",")
}
let value1: String
}
struct Pattern2: LayoutPattern {
static let numberOfKeys: Int = 2
static func make(with keys: [String]) -> String {
return "Pattern 2:" + keys.joined(separator: ",")
}
let value1: String
let value2: String
}
protocol LayoutProtocol {
associatedtype Pattern: LayoutPattern
var keys: [String] { get }
func make() -> String
}
struct Layout<T: LayoutPattern>: LayoutProtocol {
typealias Pattern = T
let keys: [String]
init(keys: [String]) {
assert(keys.count == Pattern.numberOfKeys)
self.keys = keys
}
func make() -> String {
return Pattern.make(with: keys)
}
}
let t1 = Layout<Pattern1>(keys: ["key1"])
t1.make() // Pattern 1: key1
let t2 = Layout<Pattern2>(keys: ["key1", "key2"])
t2.make() // Pattern 2: Key1,Key2
This is valid code.
But I can't write that:
class MyNote {
let layout: LayoutProtocol
}
I know that I should use a technique called type-erase like AnyPokemon!
I wrote that:
struct AnyLayout<T: LayoutPattern>: LayoutProtocol {
typealias Pattern = T
let keys: [String]
private let _make: () -> String
init<U: LayoutProtocol>(_ layout: U) where T == U.Pattern {
self.keys = layout.keys
self._make = { layout.make() }
}
func make() -> String {
_make()
}
}
let anyLayout = AnyLayout(Layout<Pattern2>(keys: ["key1", "key2"]))
anyLayout.make() // Pattern 2: Key1,Key2
This can be executed. But MyNote class can't still have a property as AnyLayout.
What should I do?
The issue is the addition of the associatedtype. It isn't doing any work here. Nothing relies on it. Remove it, and the issue goes away. Don't add associatedtypes until you have a specific requirement for them.
As a rule, if you think you need type-erasure, first ask if your protocol is designed correctly. There are definitely times that type erasers are needed, but they're far rarer than people expect.
If you have an algorithm that relies on Pattern, then show that, and we can discuss the way to build that. (There are many techniques, including using multiple protocols.)
It's also worth asking whether Layout needs to be generic here. Do you want Layout<Pattern1> to be a different type than Layout<Pattern2>? The fact that you're then trying to type-erase it suggests you don't. In that case, there's no reason for the extra generic layers. In your example, Layout isn't really doing any work. Again, you can probably just get rid of it. Let each pattern be its own thing and let Layout be a protocol that binds them with make():
protocol Layout {
func make() -> String
}
struct Pattern1: Layout {
let key: String
func make() -> String {
return "Pattern 1:" + key
}
}
struct Pattern2: Layout {
let keys: [String]
init(key1: String, key2: String) {
keys = [key1, key2]
}
func make() -> String {
return "Pattern 2:" + keys.joined(separator: ",")
}
}
let t1 = Pattern1(key: "key1")
t1.make() // Pattern 1: key1
let t2 = Pattern2(key1: "key1", key2: "key2")
t2.make() // Pattern 2: Key1,Key2
class MyNote {
let layout: Layout
init(layout: Layout) {
self.layout = layout
}
}
let note = MyNote(layout: t1)
This lets you make your Pattern initializers much stronger types. The need for an assert means you're not letting the types do the work. With the above design, you can't pass the wrong number of keys.

Comparing Two Protocol Instances for Equality in Swift

Here's the deal,
I'm writing an SDK, and I want to declare observers as protocols, instead of classes or structs (It's sort of an "Observer/Delegate" hybrid).
I want to be able to compare two arguments that are passed in as protocol references, as opposed to the concrete classes/structs they actually are, IRL.
I know that the "easy" way to get comparison is to constrain the protocols to Hashable or Equatable, but I want to avoid burdening the user (It's an SDK).
Here's a little playground with what I mean:
protocol A {
func AFunc() -> String
}
class APrime: A {
func AFunc() -> String { "I AM GROOT" }
}
let variableA = APrime()
let variableB = APrime()
func compareTypes(_ inA: A, _ inB: A) -> String {
// if inA == inB {
// return ""
// }
return "not "
}
print("A is \(compareTypes(variableA, variableB))B.")
print("A is \(compareTypes(variableA, variableA))A.")
The raunchy bit is the commented-out section in compareTypes(_: A, _: A). I need to figure out how to compare them without going into "Hacksylvania," which I could do by doing something like comparing addresses of the AFunc() in each instance.
The expected output is:
A is not B.
A is A.
Any ideas for a more "swifty" approach? I must be missing the forest for the trees.
Just to add some closure to this, here is how I solve this:
protocol A {
var uuid: Int { get } // This is the secret sauce. It will contain a unique UUID, associated with the instance.
func AFunc() -> String
}
class APrime: A {
let uuid: Int = Int.random(in: 0..<1000) // The UUID is initialized with the instance.
func AFunc() -> String { "I AM GROOT" }
}
let variableA = APrime()
let variableB = APrime()
let variableC = variableA
func compareTypes(_ inA: A, _ inB: A) -> String {
if inA.uuid == inB.uuid { // We compare UUIDs.
return ""
}
return "not "
}
print("C is \(compareTypes(variableC, variableB))B.")
print("C is \(compareTypes(variableC, variableA))A.")
The "uuid" variable is usually an actual UUID type, but I didn't want to import Foundation in the example, so I just did a simple rand. It gets the point across.
This outputs:
C is not B.
C is A.
And there is another way (that I also use, sometimes):
protocol B {
func BFunc() -> String
func amIThisOne(_ instanceToCompare: B) -> Bool // This is an identity comparator
}
class BPrime: B {
func BFunc() -> String { "I AM GROOT'S BROTHER" }
// We compare ourselves against the other instance, assuming it can be cast to our own type.
func amIThisOne(_ inInstanceToCompare: B) -> Bool {
guard let instanceToCompare = inInstanceToCompare as? Self else { return false }
return self === instanceToCompare
}
}
let variableD = BPrime()
let variableE = BPrime()
let variableF = variableD
print("D is \(variableE.amIThisOne(variableD) ? "" : "not ")E.")
print("D is \(variableD.amIThisOne(variableF) ? "" : "not ")F.")
Which outputs:
D is not E.
D is F.
This allows a more programmatic way of comparing the instances.
HOW NOT TO DO IT
And then, of course, if we have control of the instances, we can truly do the Equatable thing (This requires that the playground import Foundation):
protocol C: Equatable {
func CFunc() -> String
}
class CPrime: C {
// This is actually not what I want, as I want to compare protocols, not conforming classes.
static func == (lhs: CPrime, rhs: CPrime) -> Bool {
guard let lhs = lhs as? Self else { return false }
guard let rhs = rhs as? Self else { return false }
return lhs === rhs
}
func CFunc() -> String { "I AM GROOT'S UDDER BROTHER" }
}
let variableG = CPrime()
let variableH = CPrime()
let variableI = variableG
print("G is \(variableG == variableH ? "" : "not ")H.")
print("G is \(variableI == variableG ? "" : "not ")I.")
Which outputs:
G is not H.
G is I.

Get all key paths from a struct in Swift 4

Let's say I have that struct:
struct MyStruct {
let x: Bool
let y: Bool
}
In Swift 4 we can now access it's properties with the myStruct[keyPath: \MyStruct.x] interface.
What I need is a way to access all it's key paths, something like:
extension MyStruct {
static func getAllKeyPaths() -> [WritableKeyPath<MyStruct, Bool>] {
return [
\MyStruct.x,
\MyStruct.y
]
}
}
But, obviously, without me having to manually declare every property in an array.
How can I achieve that?
DISCLAIMER:
Please note that the following code is for educational purpose only and it should not be used in a real application, and might contains a lot of bugs/strange behaviors if KeyPath are used this way.
Answer:
I don't know if your question is still relevant today, but the challenge was fun :)
This is actually possible using the mirroring API.
The KeyPath API currently doesn't allow us to initialize a new KeyPath from a string, but it does support dictionary "parsing".
The idea here is to build a dictionary that will describe the struct using the mirroring API, then iterate over the key to build the KeyPath array.
Swift 4.2 playground:
protocol KeyPathListable {
// require empty init as the implementation use the mirroring API, which require
// to be used on an instance. So we need to be able to create a new instance of the
// type.
init()
var _keyPathReadableFormat: [String: Any] { get }
static var allKeyPaths: [KeyPath<Foo, Any?>] { get }
}
extension KeyPathListable {
var _keyPathReadableFormat: [String: Any] {
let mirror = Mirror(reflecting: self)
var description: [String: Any] = [:]
for case let (label?, value) in mirror.children {
description[label] = value
}
return description
}
static var allKeyPaths: [KeyPath<Self, Any?>] {
var keyPaths: [KeyPath<Self, Any?>] = []
let instance = Self()
for (key, _) in instance._keyPathReadableFormat {
keyPaths.append(\Self._keyPathReadableFormat[key])
}
return keyPaths
}
}
struct Foo: KeyPathListable {
var x: Int
var y: Int
}
extension Foo {
// Custom init inside an extension to keep auto generated `init(x:, y:)`
init() {
x = 0
y = 0
}
}
let xKey = Foo.allKeyPaths[0]
let yKey = Foo.allKeyPaths[1]
var foo = Foo(x: 10, y: 20)
let x = foo[keyPath: xKey]!
let y = foo[keyPath: yKey]!
print(x)
print(y)
Note that the printed output is not always in the same order (probably because of the mirroring API, but not so sure about that).
After modifying rraphael's answer I asked about this on the Swift forums.
It is possible, discussion here:
Getting KeyPaths to members automatically using Mirror
Also, the Swift for TensorFlow team has this already built in to Swift for TensorFlow, which may make its way to pure swift:
Dynamic property iteration using key paths
I propose my solution. It has the advantage of dealing correctly with #Published values when using the Combine framework.
For the sake of clarity, it is a simplified version of what I have really. In the full version, I pass some options to the Mirror.allKeyPaths() function to change behaviour ( To enumerate structs and/or classes properties in sub-dictionaries for example ).
The first Mirror extension propose some functions to simplify properties enumeration.
The second extension implements the keyPaths dictionaries creation, replacing
#Published properties by correct name and value
The last part is the KeyPathIterable protocol, that add enumeration
capability to associated object
swift
// MARK: - Convenience extensions
extension String {
/// Returns string without first character
var byRemovingFirstCharacter: String {
guard count > 1 else { return "" }
return String(suffix(count-1))
}
}
// MARK: - Mirror convenience extension
extension Mirror {
/// Iterates through all children
static func forEachProperty(of object: Any, doClosure: (String, Any)->Void) {
for (property, value) in Mirror(reflecting: object).children where property != nil {
doClosure(property!, value)
}
}
/// Executes closure if property named 'property' is found
///
/// Returns true if property was found
#discardableResult static func withProperty(_ property: String, of object: Any, doClosure: (String, Any)->Void) -> Bool {
for (property, value) in Mirror(reflecting: object).children where property == property {
doClosure(property!, value)
return true
}
return false
}
/// Utility function to determine if a value is marked #Published
static func isValuePublished(_ value: Any) -> Bool {
let valueTypeAsString = String(describing: type(of: value))
let prefix = valueTypeAsString.prefix { $0 != "<" }
return prefix == "Published"
}
}
// MARK: - Mirror extension to return any object properties as [Property, Value] dictionary
extension Mirror {
/// Returns objects properties as a dictionary [property: value]
static func allKeyPaths(for object: Any) -> [String: Any] {
var out = [String: Any]()
Mirror.forEachProperty(of: object) { property, value in
// If value is of type Published<Some>, we transform to 'regular' property label and value
if Self.isValuePublished(value) {
Mirror.withProperty("value", of: value) { _, subValue in
out[property.byRemovingFirstCharacter] = subValue
}
} else {
out[property] = value
}
}
return out
}
}
// MARK: - KeyPathIterable protocol
protocol KeyPathIterable {
}
extension KeyPathIterable {
/// Returns all object properties
var allKeyPaths: [String: Any] {
return Mirror.allKeyPaths(for: self)
}
}

Using reflection to set object properties without using setValue forKey

In Swift it's not possible use .setValue(..., forKey: ...)
nullable type fields like Int?
properties that have an enum as it's type
an Array of nullable objects like [MyObject?]
There is one workaround for this and that is by overriding the setValue forUndefinedKey method in the object itself.
Since I'm writing a general object mapper based on reflection. See EVReflection I would like to minimize this kind of manual mapping as much as possible.
Is there an other way to set those properties automatically?
The workaround can be found in a unit test in my library here
This is the code:
class WorkaroundsTests: XCTestCase {
func testWorkarounds() {
let json:String = "{\"nullableType\": 1,\"status\": 0, \"list\": [ {\"nullableType\": 2}, {\"nullableType\": 3}] }"
let status = Testobject(json: json)
XCTAssertTrue(status.nullableType == 1, "the nullableType should be 1")
XCTAssertTrue(status.status == .NotOK, "the status should be NotOK")
XCTAssertTrue(status.list.count == 2, "the list should have 2 items")
if status.list.count == 2 {
XCTAssertTrue(status.list[0]?.nullableType == 2, "the first item in the list should have nullableType 2")
XCTAssertTrue(status.list[1]?.nullableType == 3, "the second item in the list should have nullableType 3")
}
}
}
class Testobject: EVObject {
enum StatusType: Int {
case NotOK = 0
case OK
}
var nullableType: Int?
var status: StatusType = .OK
var list: [Testobject?] = []
override func setValue(value: AnyObject!, forUndefinedKey key: String) {
switch key {
case "nullableType":
nullableType = value as? Int
case "status":
if let rawValue = value as? Int {
status = StatusType(rawValue: rawValue)!
}
case "list":
if let list = value as? NSArray {
self.list = []
for item in list {
self.list.append(item as? Testobject)
}
}
default:
NSLog("---> setValue for key '\(key)' should be handled.")
}
}
}
I found a way around this when I was looking to solve a similar problem - that KVO can't set the value of a pure Swift protocol field. The protocol has to be marked #objc, which caused too much pain in my code base.
The workaround is to look up the Ivar using the objective C runtime, get the field offset, and set the value using a pointer.
This code works in a playground in Swift 2.2:
import Foundation
class MyClass
{
var myInt: Int?
}
let instance = MyClass()
// Look up the ivar, and it's offset
let ivar: Ivar = class_getInstanceVariable(instance.dynamicType, "myInt")
let fieldOffset = ivar_getOffset(ivar)
// Pointer arithmetic to get a pointer to the field
let pointerToInstance = unsafeAddressOf(instance)
let pointerToField = UnsafeMutablePointer<Int?>(pointerToInstance + fieldOffset)
// Set the value using the pointer
pointerToField.memory = 42
assert(instance.myInt == 42)
Notes:
This is probably pretty fragile, you really shouldn't use this.
But maybe it could live in a thoroughly tested and updated reflection library until Swift gets a proper reflection API.
It's not that far away from what Mirror does internally, see the code in Reflection.mm, around here: https://github.com/apple/swift/blob/swift-2.2-branch/stdlib/public/runtime/Reflection.mm#L719
The same technique applies to the other types that KVO rejects, but you need to be careful to use the right UnsafeMutablePointer type. Particularly with protocol vars, which are 40 or 16 bytes, unlike a simple class optional which is 8 bytes (64 bit). See Mike Ash on the topic of Swift memory layout: https://mikeash.com/pyblog/friday-qa-2014-08-01-exploring-swift-memory-layout-part-ii.html
Edit: There is now a framework called Runtime at https://github.com/wickwirew/Runtime which provides a pure Swift model of the Swift 4+ memory layout, allowing it to safely calculate the equivalent of ivar_getOffset without invoking the Obj C runtime. This allows setting properties like this:
let info = try typeInfo(of: User.self)
let property = try info.property(named: "username")
try property.set(value: "newUsername", on: &user)
This is probably a good way forward until the equivalent capability becomes part of Swift itself.
Swift 5
To set and get properties values with pure swift types you can use internal ReflectionMirror.swift approach with shared functions:
swift_reflectionMirror_recursiveCount
swift_reflectionMirror_recursiveChildMetadata
swift_reflectionMirror_recursiveChildOffset
The idea is to gain info about an each property of an object and then set a value to a needed one by its pointer offset.
There is example code with KeyValueCoding protocol for Swift that implements setValue(_ value: Any?, forKey key: String) method:
typealias NameFreeFunc = #convention(c) (UnsafePointer<CChar>?) -> Void
struct FieldReflectionMetadata {
let name: UnsafePointer<CChar>? = nil
let freeFunc: NameFreeFunc? = nil
let isStrong: Bool = false
let isVar: Bool = false
}
#_silgen_name("swift_reflectionMirror_recursiveCount")
fileprivate func swift_reflectionMirror_recursiveCount(_: Any.Type) -> Int
#_silgen_name("swift_reflectionMirror_recursiveChildMetadata")
fileprivate func swift_reflectionMirror_recursiveChildMetadata(
_: Any.Type
, index: Int
, fieldMetadata: UnsafeMutablePointer<FieldReflectionMetadata>
) -> Any.Type
#_silgen_name("swift_reflectionMirror_recursiveChildOffset")
fileprivate func swift_reflectionMirror_recursiveChildOffset(_: Any.Type, index: Int) -> Int
protocol Accessors {}
extension Accessors {
static func set(value: Any?, pointer: UnsafeMutableRawPointer) {
if let value = value as? Self {
pointer.assumingMemoryBound(to: self).pointee = value
}
}
}
struct ProtocolTypeContainer {
let type: Any.Type
let witnessTable = 0
var accessors: Accessors.Type {
unsafeBitCast(self, to: Accessors.Type.self)
}
}
protocol KeyValueCoding {
}
extension KeyValueCoding {
private mutating func withPointer<Result>(displayStyle: Mirror.DisplayStyle, _ body: (UnsafeMutableRawPointer) throws -> Result) throws -> Result {
switch displayStyle {
case .struct:
return try withUnsafePointer(to: &self) {
let pointer = UnsafeMutableRawPointer(mutating: $0)
return try body(pointer)
}
case .class:
return try withUnsafePointer(to: &self) {
try $0.withMemoryRebound(to: UnsafeMutableRawPointer.self, capacity: 1) {
try body($0.pointee)
}
}
default:
fatalError("Unsupported type")
}
}
public mutating func setValue(_ value: Any?, forKey key: String) {
let mirror = Mirror(reflecting: self)
guard let displayStyle = mirror.displayStyle
, displayStyle == .class || displayStyle == .struct
else {
return
}
let type = type(of: self)
let count = swift_reflectionMirror_recursiveCount(type)
for i in 0..<count {
var field = FieldReflectionMetadata()
let childType = swift_reflectionMirror_recursiveChildMetadata(type, index: i, fieldMetadata: &field)
defer { field.freeFunc?(field.name) }
guard let name = field.name.flatMap({ String(validatingUTF8: $0) }),
name == key
else {
continue
}
let clildOffset = swift_reflectionMirror_recursiveChildOffset(type, index: i)
try? withPointer(displayStyle: displayStyle) { pointer in
let valuePointer = pointer.advanced(by: clildOffset)
let container = ProtocolTypeContainer(type: childType)
container.accessors.set(value: value, pointer: valuePointer)
}
break
}
}
}
This approach works with both class and struct and supports optional, enum and inherited(for classes) properties:
// Class
enum UserType {
case admin
case guest
case none
}
class User: KeyValueCoding {
let id = 0
let name = "John"
let birthday: Date? = nil
let type: UserType = .none
}
var user = User()
user.setValue(12345, forKey: "id")
user.setValue("Bob", forKey: "name")
user.setValue(Date(), forKey: "birthday")
user.setValue(UserType.admin, forKey: "type")
print(user.id, user.name, user.birthday!, user.type)
// Outputs: 12345 Bob 2022-04-22 10:41:10 +0000 admin
// Struct
struct Book: KeyValueCoding {
let id = 0
let title = "Swift"
let info: String? = nil
}
var book = Book()
book.setValue(56789, forKey: "id")
book.setValue("ObjC", forKey: "title")
book.setValue("Development", forKey: "info")
print(book.id, book.title, book.info!)
// Outputs: 56789 ObjC Development
if you are afraid to use #_silgen_name for shared functions you can access to it dynamically with dlsym e.g.: dlsym(RTLD_DEFAULT, "swift_reflectionMirror_recursiveCount") etc.
UPDATE
There is a swift package (https://github.com/ikhvorost/KeyValueCoding) with full implementation of KeyValueCoding protocol for pure Swift and it supports: get/set values to any property by a key, subscript, get a metadata type, list of properties and more.
Unfortunately, this is impossible to do in Swift.
KVC is an Objective-C thing. Pure Swift optionals (combination of Int and Optional) do not work with KVC. The best thing to do with Int? would be to replace with NSNumber? and KVC will work. This is because NSNumber is still an Objective-C class. This is a sad limitation of the type system.
For your enums though, there is still hope. This will not, however, reduce the amount of coding that you would have to do, but it is much cleaner and at its best, mimics the KVC.
Create a protocol called Settable
protocol Settable {
mutating func setValue(value:String)
}
Have your enum confirm to the protocol
enum Types : Settable {
case FirstType, SecondType, ThirdType
mutating func setValue(value: String) {
if value == ".FirstType" {
self = .FirstType
} else if value == ".SecondType" {
self = .SecondType
} else if value == ".ThirdType" {
self = .ThirdType
} else {
fatalError("The value \(value) is not settable to this enum")
}
}
}
Create a method: setEnumValue(value:value, forKey key:Any)
setEnumValue(value:String forKey key:Any) {
if key == "types" {
self.types.setValue(value)
} else {
fatalError("No variable found with name \(key)")
}
}
You can now call self.setEnumValue(".FirstType",forKey:"types")

Is there a better way to do dependency injection in Swift than this ?

New to swift, I was trying to create a service registry:
class ServiceRegistry {
static var instance = ServiceRegistry()
private var registry = [String:AnyObject]()
private init(){}
func register<T>(key:T, value:AnyObject) {
self.registry["\(T.self)"] = value
}
func get<T>(_:T) -> AnyObject? {
return registry["\(T.self)"]
}
}
but is not super friendly:
Register:
ServiceRegistry.instance.register(CacheServiceProtocol.self, value:ImageCacheService())
Retrieve:
if let cache = ServiceRegistry.instance.get(CacheServiceProtocol) as? CacheServiceProtocol { ... }
Any better way ? It would be useful to get rid of the as? CacheServiceProtocol in the if let ...
Swinject is a dependency injection framework for Swift. In your case, you can use it without the cast with as?.
Register:
let container = Container()
container.register(CacheServiceProtocol.self) { _ in ImageCacheService() }
Retrieve:
let cache = container.resolve(CacheServiceProtocol.self)!
Here cache is inferred as CacheServiceProtocol type. The resolve method returns nil if the specified type is not registered. We know CacheServiceProtocol is already registered, so the force-unwrap with ! is used.
UPDATE
I didn't exactly answer to the question. An implementation to remove the cast is storing factory closures instead of values in the registry. Here is the example. I also modified the type of key.
class ServiceRegistry {
static var instance = ServiceRegistry()
private var registry = [String:Any]()
private init(){}
func register<T>(key:T.Type, factory: () -> T) {
self.registry["\(T.self)"] = factory
}
func get<T>(_:T.Type) -> T? {
let factory = registry["\(T.self)"] as? () -> T
return factory.map { $0() }
}
}
Register:
ServiceRegistry.instance.register(CacheServiceProtocol.self) {
return ImageCacheService()
}
Retrieve:
// The type of cache is CacheServiceProtocol? without a cast.
let cache = ServiceRegistry.instance.get(CacheServiceProtocol.self)
Using #autoclosure might be also good.
I see your attempt to implement Service Locator design pattern. It's not Dependency Injection itself, but these two patterns actually can supplement each other.
I did implement a Service Locator in Swift 2 as well and I'm pretty happy with the result. Take a look at my code here: ServiceLocator.swift (ready to use) or BasicServiceLocator.swift and LazyServiceLocator.swift (with usage examples).
Here is the basic concept:
protocol ServiceLocator {
func getService<T>(type: T.Type) -> T?
func getService<T>() -> T?
}
extension ServiceLocator {
func getService<T>() -> T? {
return getService(T)
}
}
func typeName(some: Any) -> String {
return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)"
}
final class BasicServiceLocator: ServiceLocator {
// Service registry
private lazy var reg: Dictionary<String, Any> = [:]
func addService<T>(instance: T) {
let key = typeName(T)
reg[key] = instance
//print("Service added: \(key) / \(typeName(service))")
}
func getService<T>(type: T.Type) -> T? {
return reg[typeName(T)] as? T
}
}
And demonstration:
// Services declaration
protocol S1 {
func f1() -> String
}
protocol S2 {
func f2() -> String
}
// Services imlementation
class S1Impl: S1 {
func f1() -> String {
return "S1 OK"
}
}
class S2Impl: S2 {
func f2() -> String {
return "S2 OK"
}
}
// Service Locator initialization
let sl: ServiceLocator = {
let sl = BasicServiceLocator()
sl.addService(S1Impl() as S1)
sl.addService(S2Impl() as S2)
return sl
}()
// Test run
let s1 = sl.getService(S1)
let s2: S2? = sl.getService(S2)
print(s1?.f1() ?? "S1 NOT FOUND") // S1 OK
print(s2?.f2() ?? "S2 NOT FOUND") // S2 OK
As one of the other posters pointed out, the Service Locator pattern is not actually DI. Some would even go so far as to say it's an anti-pattern.
As a general answer to your question - I do believe first class DI is a better way to accomplish the above. My suggestion would be to use Typhoon but there are several other DI libs available for Swift such as Cleanse which looks very promising.