Exposing dictionary in Swift property wrappers - swift

I have an internal dictionary that I don't want to expose to the user. Instead, I expose only certain values using properties, like this:
public var artist: String? {
get {
return items["artist"]
}
set {
items["artist"] = newValue
}
}
//...so on for another 20 or so items
As you can imagine, this ends up getting repeated quite a lot. I was thinking that property wrappers would be a nice way to clean this up - however, it's not possible to pass items directly to the wrapper, since property wrappers are created before init (so self would not be accessible).
Is there a way around this, or is this just one of the limitations of propertyWrappers?

You could build a generic solution. I did one, but you can probably improve it:
class PropertyWrapper {
private var items: [String: Any] = ["artist": "some dude"]
enum Key: String {
case artist
}
func getItem<T: Any>(key: Key) -> T {
guard let item = items[key.rawValue] as? T else {
preconditionFailure("wrong type asked for")
}
return item
}
func setItem(value: Any, key: Key) {
items[key.rawValue] = value
}
}
class GetValueClass {
func getValue() {
let wrapper = PropertyWrapper()
let value: String = wrapper.getItem(key: .artist)
}
}
class SetValueClass {
func setValue() {
let wrapper = PropertyWrapper()
wrapper.setItem(value: "some", key: .artist)
}
}

Related

Swift, help iterating over keyPaths and setting values

I've found that it's a little easier to explain what I'm doing by giving too much context for why I'm trying to do it, sorry.
I'm currently trying to add an encryption service to my project. It's nothing that'll get published I think though and I've mostly got it working. The problem that I'm having is that I have my model like this
struct Entity {
// All types are examples though I think they will be all optionals.
var prop1: String?
var prop2: Int?
var prop3: Bool?
var encryptedData: [Keypath:EncryptedData]
static var encryptableKeyPaths: [WritableKeyPath<Entity, Any?>]
}
As an example for what's happening, I can get the encryptionService to take in prop1, create an EncryptedData and put it in the encryptedData dictionary. I can even get the keyPath for the property. I can encrypt all the data and decrypt it just fine and get all the values properly, so I don't need help with that. But I'm struggling with 3 issues.
Getting the KeyPaths to be WritableKeyPaths so I can write to them with the values I need.
Setting the properties to nil once the values are encrypted so I'm not storing extra data.
Setting the properties to their values once their decrypted.
All three of these issues seem to revolve around making the KeyPaths into WritableKeyPaths.
This is the closest attempt I've gotten so far. You can copy the following code right into a playground and run it and it should work. Except it'll crash at the end. There are a couple of issues here, I'm losing the type safety as I have to make all the property types Initializable? which isn't great. Also, see that the values are permanently wrapped. I can't figure out how to prevent that. I had to mark Optional as conforming to Initializable to make this work. Lastly, the variable allStoredProperties doesn't let me write to them. I'm not sure how to properly convert it to WritableKeyPath from PartialKeyPath.
import UIKit
protocol Initializable {}
extension String: Initializable {}
extension Int: Initializable {}
extension Bool: Initializable {}
extension Optional: Initializable {}
protocol KeyPathIterable {
associatedtype Model
init()
static var allKeyPaths: [WritableKeyPath<Model, Initializable?>] { get }
}
extension KeyPathIterable {
var keyPathReadableFormat: [String: Initializable] {
var description: [String: Initializable] = [:]
let mirror = Mirror(reflecting: self)
for case let (label?, value) in mirror.children {
description[label] = (value as! Initializable)
}
return description
}
static var allStoredProperties: [PartialKeyPath<Self>] {
var members: [PartialKeyPath<Self>] = []
let instance = Self()
for (key, _) in instance.keyPathReadableFormat {
members.append(\Self.keyPathReadableFormat[key])
}
return members
}
static func setValue<Self: KeyPathIterable, T: Initializable>(on root: inout Self,
at keyPath: WritableKeyPath<Self, Initializable?>,
withValue value: T?) throws {
root[keyPath: keyPath] = value
}
}
struct Foo: KeyPathIterable {
typealias Model = Foo
var prop1: Initializable? // I want this to be String?
var prop2: Initializable? // I want this to be Int?
var prop3: Initializable? // I want this to be Bool?
init() {
self.prop1 = nil
self.prop2 = nil
self.prop3 = nil
}
static var allKeyPaths: [WritableKeyPath<Foo, Initializable?>] {
return [\Model.prop1, \Model.prop2, \Model.prop3]
}
}
var foo = Foo()
foo.prop1 = "Bar"
foo.prop2 = 1
foo.prop3 = true
print(foo.prop1 as Any)
let keyPath = \Foo.prop1
foo[keyPath: keyPath] = "Baz"
print(foo.prop1 as Any)
for path in Foo.allStoredProperties {
print("-=-=-")
print(path)
}
print("-=-=-=-=-=-=-=-")
do {
try Foo.setValue(on: &foo, at: keyPath, withValue: "BazBar" as Initializable?)
} catch {
print("Should never fail")
}
print(foo.prop1 as Any) // Returns Optional(Optional("BarBaz")) - I want this to be all the way unwrapped.
print("--------------")
let values1: [Initializable] = ["Hello World", 100, false]
do {
for (path, value) in zip(Foo.allKeyPaths, values1) {
try Foo.setValue(on: &foo,
at: path,
withValue: value as Initializable?)
}
} catch {
print("Success?")
}
print(foo.prop1 as Any)
print(foo.prop2 as Any)
print(foo.prop3 as Any)
print("----====----====----")
let values2: [Initializable] = ["Howdy", 0, false]
do {
for (path, value) in zip(Foo.allStoredProperties, values2) {
try Foo.setValue(on: &foo,
at: path as! WritableKeyPath<Foo, Initializable?>,
withValue: value as Initializable?)
}
} catch {
print("Always fails")
}
print("=-=-=-=-=-=-=-=")
print(foo)
I've looked all over google and youtube and everywhere and I can't seem to get this to work. I'm open to a different architecture if that work accomplish my goals better. Just a little frustrated. Thanks for your help.

Extend a Generic Type With Another Generic

Is it possible to extend a generic type with a where clause requirement containing another generic?
Basically something like this:
class WeakStore<Element: AnyObject> {
weak var value: Element?
init(_ value: Element) {
self.value = value
}
}
extension Dictionary where Value == WeakStore<Element> {
public subscript(weak key: Key) -> Element? {
get {
self[key].value
}
set {
if let exists = self[key] {
exists.value = newValue
} else {
self[key] = WeakStore(newValue)
}
}
}
}
To clarify, this does not build and provides numerous errors. Additionally, while what prompts this specific question IS trying to get a weak dictionary working, please stay on topic about how to get a generic type extended with another generic (if even possible) as it is something that applies well beyond this specific use case.
I don't believe it can be done quite this way. (I'm not actually certain why; it feels like it's just syntax and compiler features, but it's possible that there's something deeper I haven't discovered.)
I can, however, be done by adding a protocol:
public protocol WeakStoreType: AnyObject {
associatedtype Element: AnyObject
var value: Element? { get set }
init(_ value: Element)
}
extension WeakStore: WeakStoreType {}
This just wraps WeakStore into a protocol, and then conforms WeakStore to that protocol, replacing a generic parameter with an associatedtype.
There are a few mistakes in your Dictionary extension, but they can be fixed as follows:
public extension Dictionary where Value: WeakStoreType {
subscript(weak key: Key) -> Value.Element? {
get {
self[key]?.value
}
set {
if let exists = self[key] {
exists.value = newValue
} else if let newValue = newValue {
self[key] = Value(newValue)
} else {
self[key] = nil
}
}
}
}

Can you simultaneously define and instantiate implicit types in 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.

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)
}
}

Make Dictionary conform to value type protocol

This is simplified code that doesn't compile. Is there anyway to make this work in Swift? Thanks.
protocol Person {
var name:String { get }
var age:Int { get }
}
extension Dictionary : Person {
var name: String {
return self["name"] as String
}
var age: Int {
return self["Int"] as Int
}
}
Let me give some context to why I would want to do this.
Lets say I have some person data coming in over the wire as json. As I pass it through JSONSerialization I get a [String:AnyObject] Dictionary back.
So I would like to declare the JSON data interfaces in protocols, make the dictionary objects conform to the protocols and then extract the values from the dictionaries via typed properties, rather then via magic strings and casts. This way the client code would only know about protocol types even though they are implemented as dictionaries behind the curtain.
Not sure it's doable or a good idea, just wanted to try it. But compiler is giving me all sorts of trouble.
I understand you want to encapsulate the logic to link a json to it's model representation.
The suggested solution
First of all I am suggesting another way to achieve what you are looking for instead
Look at this Struct.
struct Person {
let name: String
let age: Int
init?(json: [String:Any]) {
guard let name = json["name"] as? String, age = json["age"] as? Int else { return nil }
self.name = name
self.age = age
}
}
The logic to extract data from the json is encapsulated into its initializer. And if the provided json is not valid the initialization fails. It's safe because it will never crash and it's easy to use.
The direct answer (don't do this at home!)
protocol Person {
var name: String? { get }
var age: Int? { get }
}
extension Dictionary : Person {
private var dictWithStringKeys: [String:Any] {
return reduce([String:Any]()) { (dict, elm) -> [String:Any] in
var dict = dict
if let key = elm.0 as? String {
dict[key] = elm.1
}
return dict
}
}
var name: String? {
return dictWithStringKeys["name"] as? String
}
var age: Int? {
return dictWithStringKeys["age"] as? Int
}
}