<I realize similar questions have already been asked, but they have very complex questions or insufficient answers (I'm a beginner with swift)>
I've included a very basic example that summarizes my issue
struct Greeting {
var name = "Bob"
var message = "Hi, " + name
}
var a = Test("John")
print(a.message)
I get the following error:
error: cannot use instance member 'name' within property initializer; property initializers run before 'self' is available
I've tried initializing the values, creating my best guess at lazy vars, and making the vars computed values. Any help would be appreciated!
You are using name before the struct is initialized. If you make message a computed properties it should work.
struct Greeting {
var name = "Bob"
var message: String {
"Hi, " + name
}
}
You are getting the error because in Swift you need to give variables some value before you can use them.
struct Greeting {
var name = "Bob" // <-- this variable is initialized with "Bob"
var message: String // <-- this var is not, you need to initialize it yourself
// which means you cannot use this before you give it a value
// a very common approach is to have your own initializer function init.
init(name: String, message: String) {
self.name = name // <-- this is initialized (again in our case)
self.message = "Hi " + name // this is now initialized the way you want it
}
// you can have many customized init, like this
init() {
// here because name is already initialized with Bob, it's ok
self.message = "Hi " + name
}
// if you don't have a init function, swift creates a basic one (or more) for you
// but it will not be the special one you wanted, like above.
}
So when Greeting is created the init function is called first. That is the place where you can customize the creation of Greeting. Before that you cannot use the variable "name" as you do in "var message = "Hi, " + name".
You can use it like this:
let greet = Greeting()
print(greet.message)
Related
Problem is the following compiler error:
"Instance member 'name' cannot be used on type 'Person'"
class Person {
var name: String = "Amir"
var myClosure = { (family: String) -> Void in
print(name + " " + family)
}
func myFunc(family: String) -> Void {
print(name + " " + family)
}
}
The code inside myFunc and myClosure are the same, but I have compiler error on myClosure definition.
As I know, basically closures and functions are the same. So what difference between them make the above limitation for closures?
The problem is that I'm trying to initialize a variable using an instance's variable while the instance itself has not been created yet.
So the problem is not related to closure nature. The same problem does exist in the following code where I'm trying to initialize nickname with name instance property.
class Person {
var name: String = "Amirreza"
var nickname: String = name
}
As compiler says, "property initializers run before 'self' is available", so in the above code the self.name is not available to initialize nickname and this is the source of the problem.
Here the solution is to use "lazy" keyword before nickname definition. The lazy keywork defers the initialization of the nickname property to when it is accessed for the first time. Means when we are sure that the instance has been created and name property is available.
To read more about lazy refer to https://docs.swift.org/swift-book/LanguageGuide/Properties.html
So the correct form of above code is:
class Person {
var name: String = "Amirreza"
lazy var nickname: String = name
}
And the correct form of the my main question code would be:
class Person {
var name: String = "Amirreza"
lazy var myClosure = { [weak self] (family: String) -> Void in
print(self!.name + " " + family)
}
func myFunc(family: String) -> Void {
print(self.name + " " + family)
}
}
Just note that we have to refer to properties inside closures with self explicitly like self.name. Meanwhile to avoid strong reference cycles we need to define a capture list in closure definition which in my example is [weak self].
By defining self as 'weak', self turns to an optional variable, so we have to unwrap it somehow which makes me to write self!.name in the closure.
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)
I am reading the documentation but cannot get something to work. I want to get the method or variable name in a class or struct. In Swift 3 you can apparently use #KeyPath. In Swift 4 it is easier. I cannot get either to work.
I want the string variable name to contain "Bar".
Here is my Playground code.
//: Playground - noun: a place where people can play
class Foo {
public var Bar:String {
get {
return "xxx"
}
}
}
var name = \Foo.Bar
print ("Member function name \(name) should be Bar")
In advance thank you for reading my question.
I suppose you want to get the name of a member at runtime?
Apparently, when you print a key path out, it does not print the name of the member. As an alternative, you can use a selector instead:
class Foo {
// needs to be exposed to Obj-C
#objc public var Bar:String {
get {
return "xxx"
}
}
}
var name = #selector(getter: Foo.Bar)
print ("Member function name \(name) should be Bar")
Prints:
Member function name Bar should be Bar
I was doing some research about the reasons we should use Get and Set for our properties.
I've noticed 3 main reasons for it
When you want to do/check something before you actually set the
property
When you want to have a property that you can only Get from it
(maybe for security purposes I guess? ), or give it different access
levels.
Hiding the internal representation of the property while exposing a
property using an alternative representation. (which for me doesn't
make a lot of sense since i can access it on the wrong place using
the Set function anyways)
The code below is a example of how you would implement Get and Set for properties in Swift, taking advantage of those 3 points I mentioned:
class Test
{
private var _testSet:String!
private var _testGetOnly:String
var testSet:String{
get{
return _testSet
}
set{
_testSet = newValue + "you forgot this string"
}
}
var testGetOnly:String!{
get{
return _testGetOnly
}
}
init(testSet:String, testGetOnly:String)
{
_testSet = testSet
_testGetOnly = testGetOnly
}
}
But this other example below also take advantage of those points mentioned but instead of using another computed property to return the private property value I just use the willSet and didSet observers
class Test
{
var testGet:String {
willSet{
fatalError("Operation not allowed")
}
}
var testWillSet:String!{
didSet{
self.testWillSet = self.testWillSet + "you forgot this string"
}
}
init(testGet:String, testWillSet:String)
{
self.testGet = testGet
self.testWillSet = testWillSet
}
}
So I'm curious to know what are the ADVANTAGES and DISADVANTAGES of each implementation.
Thanks in advance
Your question boils down to compile time vs. run time error. To address your 3 questions:
Yes, willCheck is your only option here
Readonly properties fall into 2 types: (a) those whose value derive from other properties, for example, their sum; and (b) those that you want to be able to change by yourself, but not by the users. The first type truly have no setter; the second type has a public getter and a private setter. The compiler can help you check for that and the program will not compile. If you throw a fatalError in didSet you get a runtime error and your application will crash.
There can be state objects that you don't want the user to freely mess with, and yes, you can completely hide those from the users.
Your code first example was too verbose in defining the backing variables - you don't need to do that. To illustrate these points:
class Test
{
// 1. Validate the new value
var mustBeginWithA: String = "A word" {
willSet {
if !newValue.hasPrefix("A") {
fatalError("This property must begin with the letter A")
}
}
}
// 2. A readonly property
var x: Int = 1
var y: Int = 2
var total: Int {
get { return x + y }
}
private(set) var greeting: String = "Hello world"
func changeGreeting() {
self.greeting = "Goodbye world" // Even for private property, you may still
// want to set it, just not allowing the user
// to do so
}
// 3. Hide implementation detail
private var person = ["firstName": "", "lastName": ""]
var firstName: String {
get { return person["firstName"]! }
set { person["firstName"] = newValue }
}
var lastName: String {
get { return person["lastName"]! }
set { person["lastName"] = newValue }
}
var fullName: String {
get { return self.firstName + " " + self.lastName }
set {
let components = newValue.componentsSeparatedByString(" ")
self.firstName = components[0]
self.lastName = components[1]
}
}
}
Usage:
let t = Test()
t.mustBeginWithA = "Bee" // runtime error
t.total = 30 // Won't compile
t.greeting = "Goodbye world" // Won't compile. The compiler does the check for you
// instead of a crash at run time
t.changeGreeting() // OK, greeting now changed to "Goodbye world"
t.firstName = "John" // Users have no idea that they are actually changing
t.lastName = "Smith" // a key in the dictionary and there's no way for them
// to access that dictionary
t.fullName = "Bart Simpsons" // You do not want the user to change the full name
// without making a corresponding change in the
// firstName and lastName. With a custome setter, you
// can update both firstName and lastName to maintain
// consistency
A note about private in Swift 2 vs. Swift 3: if you try this in a Swift 2 playground, you will find t.greeting = "Goodbye world" works just fine. This is because Swift 2 has a strange access level specifier: private means "only accessible within the current file". Separate the class definition and the sample code into different files and Xcode will complain. In Swift 3, that was changed to fileprivate which is both clearer and save the private keyword for something more similar to to Java and .NET
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.