Struct variable initialize with setter - swift

I'm new to Swift and I want to be able to write a property setter, which will also be used as a constructor when initializing:
struct Person {
private var name: String {
get {
return self.name;
}
set {
self.name = name;
}
}
}
var Murad = Person(name: "Murad");
When I run this code error return this error
argument passed to call that takes no arguments

The error occurs because the property is a computed property and it's private (can only be changed within the class), so for the compiler there is no (memberwise) initializer and only the default initializer Person() without parameter can be used.
You are in luck that you get this error message otherwise when you would run the code you will run into an infinite loop (which causes an overflow crash).
If you want a constant just declare the struct member as let
struct Person {
let name: String
}
let murad = Person(name: "Murad")
print(murad)

Related

#propertyWrapper for Optional type: wrappedValue initialization error for automatically assigned nil value

I faced strange error when usign propertyWrapper feature. Sample code below:
#propertyWrapper
struct Wrapper<Value> {
private var parameter: Int?
var wrappedValue: Value
init(wrappedValue: Value, parameter: Int? = 0) {
self.wrappedValue = wrappedValue
self.parameter = parameter
}
}
class WrapperTest {
#Wrapper var valueImplicitNil: Double? // OK
#Wrapper var valueExplicitNil: Double? = nil // OK
//#Wrapper(parameter: 1) var valueWithParamImplicitNil: Double? // NOK (Missing argument for parameter 'wrappedValue' in call)
#Wrapper(parameter: 1) var valueWithParamExplicitNil: Double? = nil // OK
}
For valueImplicitNil, Swift automatically assigns nil to it, and then propertyWrapper initializer automatically assigns nil to first init parameter wrappedValue - both behaviours clearly described in Swift documentation.
For some reason it doesn't do the same for valueWithParamImplicitNil variable. Any ideas, why is it so?
EDIT: As advised, I reported an issue SR-14411 in bugs.swift.org, and am using workaround proposed below.
Here property wrapper is a struct, therefore all its properties must be initialized when an instance is created. In your example, no value is given, so the compiler emits an error.
Your property wrapper struct takes a non-optional wrappedValue - it can’t be initialised without a value. If you don’t provide one you get a compiler error as you can see.
If you want a property wrapper that doesn’t require an initial wrapper value then you need to make wrappedValue optional.
As others have said in the comments, this looks like a compiler bug. Basically, the compiler should be able to fill in the first parameter for the initializer (wrappedValue) in the second case also, since clearly it can handle the default value for nil properties if no arguments are passed to the property declaration.
So, if #Wrapper var valueImplicitNil: Double? gets expanded by the compiler to
private var _valueImplicitNil = Wrapper<Double?>(wrappedValue: nil)
var valueImplicitNil: Double? {
get { return _valueImplicitNil.wrappedValue }
set { _valueImplicitNil.wrappedValue = newValue }
}
, so it should be the case for #Wrapper(parameter: 1) var valueWithParamImplicitNil: Double?, which should expand to:
private var _valueImplicitNil = Wrapper<Double?>(wrappedValue: nil, parameter: 1)
var valueImplicitNil: Double? {
get { return _valueImplicitNil.wrappedValue }
set { _valueImplicitNil.wrappedValue = newValue }
}
A workaround until this gets fixed in the compiler is to add another initializer that has only the second argument:
init<V>(parameter: Int? = 0) where Value == V? {
self.init(wrappedValue: nil, parameter: parameter)
}
At least this is the approach AppStorage took, so it seems that the Apple engineers ran into the same problem, but found a way to work around this limitation.

How do I use an existing property in a property wrapper when self hasn't been initialized? (SwiftUI)

I have a struct with two variables inside property wrappers. One of the variables is supposed to be computed from the other. When I try to do this, I get the following error:
Cannot use instance member 'name' within property initializer; property initializers run before 'self' is available.
I tried assigning a temporary value to these variables, and then re-assigning them within a custom init() function, but that doesn't seem to work ether. I made a simplified version of the code to see if I could isolate the issue.
import SwiftUI
struct Person {
#State var name: String = ""
#State var nameTag: NameTag = NameTag(words: "")
init(name: String) {
// not changing name and nameTag
self.name = name
nameTag = NameTag(words: "Hi, my name is \(name).")
}
}
class NameTag {
var words: String
init(words: String) {
self.words = words
}
}
var me = Person(name: "Myself")
// still set to initial values
me.name
me.nameTag.words
I noticed that when I changed nameTag to an #ObservedObject, rather than #State, it was able to be re-assigned correctly. Although I don't believe I can change name to #ObservedObject. Could anyone tell me what I'm doing wrong?
To use property wrappers in initializers, you use the variable names with preceding underscores.
And with State, you use init(initialValue:).
struct Person {
#State var name: String
#State var nameTag: NameTag
init(name: String) {
_name = .init(initialValue: name)
_nameTag = .init( initialValue: .init(words: name) )
}
}
Here's what a #State property really looks like, as your tear down levels of syntactic sugar:
name
_name.wrappedValue
$name.wrappedValue
_name.projectedValue.wrappedValue
You can't use the underscore-name outside of the initial type definition.

Understanding struct initialiser if it contain private property

I want to understand how initialiser work if struct contains private properties. I have following code:
struct Doctor {
var name: String
var location: String
private var currentPatient = "No one"
}
let drJones = Doctor(name: "Esther Jones", location: "Bristol")
This throws an error:
Cannot invoke initializer for type 'Doctor' with an argument list of
type '(name: String, location: String)'
My Assumption is: Default Memeberwise initialiser contains private property which can't be called from outside.
But I am confused by following code:
struct Doctor {
private var currentPatient = "No one"
}
let drJones = Doctor()
How this is working?, it is not throwing any error.
You can't use default memberwise initialiser for assigning struct's property with private access level modifier.
Your second example works, because you gave your property default value so there is no need to assign it while you're initalizing it.
If you need to assign your private property using initializer, you have to write your own
init(name: String, location: String, currentPatient: String) {
self.name = name
self.location = location
self.currentPatient = currentPatient
}

Swift protocol settable property through a read-only property [duplicate]

This question already has an answer here:
Swift: Failed to assign value to a property of protocol?
(1 answer)
Closed 6 years ago.
Can someone please tell me why Swift has to call the setter of a property when it's only being used to access an object (a protocol) in order to set one of its properties? This first example shows the error I get if I don't declare the indirect object as settable:
protocol AProtocol {
var name: String { get set }
}
class AnImplementation: AProtocol {
var name = ""
}
class AParent {
var test = AnImplementation()
}
class AChild {
var parent: AParent!
var test: AProtocol {
get { return parent.test }
// Note: Not settable
}
}
var parent = AParent()
var child = AChild()
child.parent = parent
child.test.name = "Hello world!" // Error: Cannot assign to property : 'test' is a get-only property
print(child.test.name)
If I give it a setter, it compiles and works but it calls the setter:
protocol AProtocol {
var name: String { get set }
}
class AnImplementation: AProtocol {
var name = ""
}
class AParent {
var test = AnImplementation()
}
class AChild {
var parent: AParent!
var test: AProtocol {
get { return parent.test }
set(newTest) { print("Shouldn't be here!") }
}
}
var parent = AParent()
var child = AChild()
child.parent = parent
child.test.name = "Hello world!"
print(child.test.name)
Output is:
Shouldn't be here!
Hello world!
I'm not sure what I'm not understanding here. I assume I can just give it an empty setter, but I'd like to understand the reason for it.
Any information is much appreciated!
Change your protocol declaration to this:
protocol AProtocol:class {
var name: String { get set }
}
Otherwise, it is taken by default as a value type. Changing a value type's property replaces the value type instance (as shown by the setter observer). And you can't do that if the reference is a let reference.
This is probably caused by the fact that the compiler doesn't know whether AChild.test is a class or a value type. With classes there is no problem but with value types assigning to name would also create an assignment to test (value-copy behavior). Marking APProtocol as class protocol will fix the problem.
To expand, when the compiler is not sure whether test is a value or a class type, it will use the following rewrite of child.test.name = "Hello world!":
var tmp = child.test
tmp.test = "Hello world!"
child.test = tmp
because that will work for both class and value types.

how to create a singleton in swift with init variables

I am trying to create a singleton class in swift but I am getting an error
"cannot create a single-element tuple with an element label"
i am not getting it.
class GroupObject {
// we want the group object to be a singleton
var name: String
var id: Int
var groupJsonObject: JSON
init(groupJsonObject: JSON){
self.groupJsonObject = groupJsonObject
self.id = groupJsonObject["id"].int!
self.name = groupJsonObject["name"].string!
}
class var sharedInstance : GroupObject {
struct Static {
static let instance : GroupObject = GroupObject(groupJsonObject: JSON) // this is the problem line.
}
return Static.instance
}
}
The problem is that you cannot pass a parameter to the singleton. Your singleton implementation doesn't know to what JSON refers.
If you want this to be a singleton, you'd have to initialize the groupJsonObject separately from the initialization of the shared instance. For example:
class GroupObject {
var name: String!
var id: Int!
var groupJsonObject: JSON! {
didSet {
id = groupJsonObject["id"].int!
name = groupJsonObject["name"].string!
}
}
static let sharedInstance = GroupObject() // btw, this is a more concise syntax for declaring a singleton
}
And then, when you want to initialize those properties, you could do:
GroupObject.sharedInstance.groupJsonObject = json
If your "singleton" is supposed to hold some data passed to it on instantiation, how will it get that data? Where/when is it available?
I think you don't actually want a singleton at all; you want an instance created with your JSON data to be accessible from different points in your application. In that case, pick some "master controller", create it there, then pass it along to other controllers as needed.