Swift property wrappers. Assign by reference - swift

I would like to share a string between classes, and keep modifications made on that string synchronised.
I thought of doing a property wrapper like:
#propertyWrapper class StringHolder {
var wrappedValue: String?
init(wrappedValue: String?) {
self.wrappedValue = wrappedValue
}
}
and then use it:
#StringHolder var sharedString: String = "testString"
let anotherClass = AnotherClass()
anotherClass.sharedString = sharedString
anotherClass.sharedString = "modified"
//sharedString is still "testString"
The problem is that when assigning the sharedString, only the wrapped value is re-set.
Is it possible to have the same wrapper assigned by reference to two properties?

Related

Empty initializer for swift structures

Is it possible to initialize a swift struct without any values?
To summarize the idea, I'm creating a struct with two attributes, those attributes were filled when I called the init() method, as it should, but the problem is, I have a function inside this struct and I need to use it, in some cases, without filling the fields in the struct initializer.
I've tried adding the init(){} function, hoping that somehow it would work like the empty constructors in java, but I've got no success.
struct Quiz {
var pergunta: String
var resposta: Bool
init(pergunta: String, resposta: Bool) {
self.pergunta = pergunta
self.resposta = resposta
}
//Something like that
init(){}
func popularQuiz() -> Array<Quiz> {
...
}
}
Is there a way to do it or swift doesn't have the option to create a empty structure?
The big question is why do you want to have an empty Quiz? What does an empty Quiz mean?
Those questions aside, you can only create an initializer with no parameters if all of the properties can be given default values or if the properties are optional.
struct Quiz {
var pergunta: String = ""
var resposta: Bool = false
init(pergunta: String, resposta: Bool) {
self.pergunta = pergunta
self.resposta = resposta
}
init(){}
}
Now your empty init will work because all properties are fully initialized. But is that what you really want?
Another way to get the same result is to use default values on the parameters of the main initializer.
struct Quiz {
var pergunta: String
var resposta: Bool
init(pergunta: String = "", resposta: Bool = false) {
self.pergunta = pergunta
self.resposta = resposta
}
}
Now you can call:
Quiz()
Quiz(pergunta: "Some question", resposta: "Some Answer")
Quiz(pergunta: "Question with no answer")
Any of these still result in all properties being initialized.
The last option would be to make the properties optional instead of using special default values. But this adds more headache later.
struct Quiz {
var pergunta: String?
var resposta: Bool?
init(pergunta: String, resposta: Bool) {
self.pergunta = pergunta
self.resposta = resposta
}
init(){}
}
The empty init will leave the properties as nil. But now you need to properly handle the optional values everywhere. Again, is that what you really want?

Get marked variable name as default value for propertyWrapper

Lets say I have a property Wrapper to fetch Feature Flag from a service and assign to a Boolean if it is enabled of not
#propertyWrapper final class FeatureToggle {
var clientContainer: ConfigurableFeatureFlagClient
private let key: String
var wrappedValue: Bool {
FeatureFlag(clientContainer: clientContainer).isEnabled(key: key)
}
init(key: String, container: ConfigurableRemoteConfigClient = RemoteConfigClient.shared) {
self.clientContainer = container
self.key = key
}
}
// Test Class
class TestFeatureToggle {
#FeatureToggle(key: "isFeatureEnabled")
var isFeatureEnabled
}
I'm trying to Make this FeatureToggle Property Wrapper to assume that key default value will be the variable name marked with the property Wrapper, so if #FeatureToggle is marking var isFeatureEnabled so the key will automatically be isFeatureEnabled
Currently I tried using the #function as default value to get the class and use Mirror reflection to get the variable name of the class marked with the property Wrapper
#propertyWrapper final class FeatureToggle {
var clientContainer: ConfigurableRemoteConfigClient
private let key: String
var wrappedValue: Bool {
let factoryClass = Bundle.main.classNamed(key) as! NSObject.Type // Force Unwrap just to simplify the example
let factory = factoryClass.init()
let mirror = Mirror(reflecting: factory)
for child in mirror.children {
guard let featureToggle = child.value as? FeatureToggle else { continue }
let formattedValue = child.label?.dropFirst() // Drop the _ inserted by OBJC
return FeatureFlag(clientContainer: clientContainer).isEnabled(key: formattedValue)
}
return false
}
init(key: String = #function, container: ConfigurableRemoteConfigClient = RemoteConfigClient.shared) {
self.clientContainer = container
self.key = key
print("Self.key == \(self.key)") // It will print "TestFeatureToggle" using my class in previous code
} }
It works but i have to make the class TestFeatureToggle with #objc and makes it inherit from NSObject. Not to mention that this solution looks kind of ugly and hacky. My question is, is there an easier way without exposing to OBJC runtime or a more clean solution?

Property Wrappers vs Mirroring

Happy holidays!
I have a property wrapper that is used to define the key in a key-value pair.
#propertyWrapper
final public class KeyValuePair<T> {
public var wrappedValue: T?
private(set) var key: String
public init(key: String) {
self.key = key
}
}
EDIT:
And using the following protocol, to which all KV models should conform...
protocol Parametrized {
var toParameters: [String: Any] { get }
}
the property wrapper is used in the following fashion:
struct Person: Parametrized {
#KeyValuePair(key: "first_name") var firstName: String?
#KeyValuePair(key: "last_name") var lasteName: String?
#KeyValuePair(key: "age") var age: Int?
#KeyValuePair(key: "isSingle") var isSingle: Bool?
#KeyValuePair(key: "isOpenForDating") var isOpenForDating: Bool?
}
var missUniverse2022 = Person()
missUniverse2022.firstName = "Miss"
missUniverse2022.lastName = "Universe"
missUniverse2022.age = 23
missUniverse2022.isSingle = true // Rad <3
missUniverse2022.isOpenForDating = false // Sad :D
So, the whole idea is to generate a dictionary of type [String: Any] that holds the key-value pairs from that struct using Mirror.
The problem is that the following code is not working (I know why, but I can't figure out the workaround):
extension Parametrized {
var toParameters: [String: Any] {
var parameters = [String: Any]()
for child in Mirror(reflecting: self).children {
/***********************************************
Generic parameter 'T' could not be inferred in cast to 'KeyValuePair'
Explicitly specify the generic arguments to fix this issue
***********************************************/
guard let kvPair = child.value as? KeyValuePair else { continue }
parameters[kvPair.key] = kvPair.wrappedValue
}
return parameters
}
}
So, I turn to the experts here: how to use the mirror logic to get the k-v pairs and put them in a collection?
I really appreciate any help you can provide.
Thanks to the Swift community, I got my answer.
https://forums.swift.org/t/property-wrappers-with-generics-vs-mirroring/54325/4

Why not use a struct-based singleton in Swift

Why not use a struct-based singleton?
I created decodable struct-based singleton.
struct Person: Decodable {
static var shared = Person()
private(set) var name: String?
var age: Int?
private init() {
}
mutating func initData(from data: Data) {
if let person = try? JSONDecoder().decode(Person.self, from: data) {
self = person
}
}
}
init from other class like this:
Person.shared.initData(from: data)
and use parameters:
let name = Person.shared.name
Person.shared.name = "MyName" //error
Person.shared.age = 20
Is this the wrong way?
You can't use a struct fora singleton because struct is a value type so when you assign it to a variable you get a copy. This can be easily shown
struct Singleton {
static var shared = Singleton()
var value: Int
private init() {
value = 0
}
}
Singleton.shared.value = 1
var otherSingleton = Singleton.shared
otherSingleton.value = 2
Now if we print the value of both
print(Singleton.shared.value, otherSingleton.value)
we get
1 2
So otherSingleton is clearly a separate instance so now we have 2 singletons :)
But if we simply change the type of Singleton to class that is a reference type and then run the same code the result of the print is
2 2
since it is the same instance we have changed the value property for.

Is it possible to obtain a Swift type from a string?

I wonder if it's possible to obtain a Swift type dynamically. For example, say we have the following nested structs:
struct Constants {
struct BlockA {
static let kFirstConstantA = "firstConstantA"
static let kSecondConstantA = "secondConstantA"
}
struct BlockB {
static let kFirstConstantB = "firstConstantB"
static let kSecondConstantB = "secondConstantB"
}
struct BlockC {
static let kFirstConstantC = "firstConstantBC"
static let kSecondConstantC = "secondConstantC"
}
}
It's possible to get value from kSeconConstantC from a variable). Like:
let variableString = "BlockC"
let constantValue = Constants.variableString.kSecondConstantC
Something akin to NSClassFromString, maybe?
No, it's not possible yet (as a language feature, at least).
What you need is your own type registry. Even with a type registry, you wouldn't be able to get static constants unless you had a protocol for that:
var typeRegistry: [String: Any.Type] = [:]
func indexType(type: Any.Type)
{
typeRegistry[String(type)] = type
}
protocol Foo
{
static var bar: String { get set }
}
struct X: Foo
{
static var bar: String = "x-bar"
}
struct Y: Foo
{
static var bar: String = "y-bar"
}
indexType(X)
indexType(Y)
typeRegistry // ["X": X.Type, "Y": Y.Type]
(typeRegistry["X"] as! Foo.Type).bar // "x-bar"
(typeRegistry["Y"] as! Foo.Type).bar // "y-bar"
A type registry is something that registers your types using a custom Hashable type (say a String or an Int). You can then use this type registry to reference registered types using custom identifiers (a String, in this case).
Since Any.Type by itself isn't all that useful, I constructed an interface Foo through which I could access a static constant bar. Because I know that X.Type and Y.Type both conform to Foo.Type, I forced a cast and read the bar property.