Init a computed variable - swift

I'm going to create a new class, and in this class there's a computed variable; so I'm looking for a way to init this variable:
import UIKit
class Squadra: NSCoder, NSCoding
{
var nomeSquadra: String
var numeroCoriSquadra: Int
var coloreSquadra: String
var immagineSquadra: String
var sottotitoloSquadra: String
{
get
{
return "I migliori cori: \(nomeSquadra)"
}
}
init(nome: String, numero: Int, colore: String, immagine: String, sottotitolo: String)
{
nomeSquadra = nome
coloreSquadra = colore
numeroCoriSquadra = numero
immagineSquadra = immagine
sottotitoloSquadra = sottotitolo
}
}
obviously with this line of code Xcode gives my a compile error (because the var is a get only property).
I think that i have to use a set to make the var writable, but I don't know how to operate because I don't know exactly how get and set work.

Either remove sottotitoloSquadra = sottotitolo or assign to a different variable. Even if the assignment worked, you never actually use the value that comes in as sottotitolo for anything.

I can't see useful behavior while you use independent property nomeSquadra and trying to have setter for sottotitoloSquadra at the same time. Maybe better to use hidden support property for computed variable in your case?
private var _sottotitoloSquadra: String
var sottotitoloSquadra: String
{
get
{
return "I migliori cori: \(_sottotitoloSquadra)"
}
set
{
_sottotitoloSquadra = newValue
}
}
init(nome: String, numero: Int, colore: String, immagine: String, sottotitolo: String)
{
//...
_sottotitoloSquadra = sottotitolo
}

If I understand you class correctly, you want to use the variable's default string when the init() did not provide a value (I'm just guessing though).
So if the team doesn't have a specific subTitle, you would make one up from the team's name.
I also understand that you don't want that property to be modifiable after the object is instantiated.
If that is the case, (I assume you would get an empty string for sottotitolo), you can define a private variable to hold the provided title and expose it using a computed variable. The default value (made up title) can be returned by that computed variable if a title was not supplied on the init().
class Squadra
{
var nomeSquadra: String
var numeroCoriSquadra: Int
var coloreSquadra: String
var immagineSquadra: String
private var _sottotitoloSquadra = ""
var sottotitoloSquadra: String
{
return _sottotitoloSquadra == ""
? "I migliori cori: \(nomeSquadra)"
: _sottotitoloSquadra
}
init(nome: String, numero: Int, colore: String, immagine: String, sottotitolo: String)
{
nomeSquadra = nome
coloreSquadra = colore
numeroCoriSquadra = numero
immagineSquadra = immagine
_sottotitoloSquadra = sottotitolo
}
}
Only your class, including its init() function, can modify the private variable holding the supplied sottotitolo. Outside of that source file, the private variable is not accessible at all.

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?

Swift property wrappers. Assign by reference

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?

self.name = name in Swift . I don't understand why this codes are needed

class person {
var name : String
init(name: String) {
self.name = name
}
}
I am learning Swift class chapter
I don't understand why init(name:String) self.name = name code is needed
what the purpose of this code is.
I declared var name: String
and again init(name: String), why is self.name = name needed?
what's different between just var name and self.name = name?
Look into something called variable scope. In your code - there are two "name variables - a class (or instance) variable and a "parameter in your init.
The latter - init(name:) only is in use within your initialization function, meaning the instance has no name without the self.name = name once you've created the instance.
The former is available - to the instance - as long as your instance of the class person is.
To explain further, try this. Change your code to be:
class Person {
var myName : String
var myOtherName : String
init(name: String) {
self.myName = name
}
}
In your app or view controller, do this:
let myPerson = Person(name: "john")
print(myPerson.myName) // prints "jihoon"
print(myPerson.myOtherName) // prints nothing
print(myPerson.name) // generates a build error because name doesn't exist
One last note - in Swift class names are capitalized, so the best name is Person, not person.
Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.
class person {
var name : String // undetrmined state
init(name: String) {
self.name = name
}
}
class person2 {
var name : String = "default value" // detrmined state
// Now these intializer are not optional. You can use these initializer or not.
init(name: String) {
self.name = name
}
init() {
}
}
// Aother example for optional stored property
class person3 {
var name : String? // detrmined state, because default value for optional is nil
// Now these intializer are not optional. You can use these initializer or not.
init(name: String) {
self.name = name
}
init() {
}
}
For more info read this one Apple Doc

Variable declaration with underscore

I have seen this in some videos on Youtube.
class Student {
private var _name: String!
private var _studentID: Int!
var name: String {
return _name
}
var studentID:Int {
return _studentID
}
init(name: String, studentID: Int) {
self._name = name
self._studentID = studentID
}
}
Any reason why they are doing this (adding _name and _studentID) instead of:
class Student {
private var name: String!
private var studentID: Int!
init(name: String, studentID: Int) {
self.name = name
self.studentID = studentID
}
}
Thank you very much.
The first examples are essentially creating properties that are publicly readable but privately writable.
The second set of code does not do the same thing as the first set.
The proper way to write this code is:
private (set) var name: String // no need for the !
private (set) var studentID: Int // no need for the !
init(name: String, studentID: Int) {
self.name = name
self.studentID = studentID
}
This makes the properties readable by outside users but only settable by the class. This is what the 1st set of code implements but in a much more verbose and needless manner.
The use of underscore is just a naming convention carried over from Objective-C when creating private instance variables.
Personally, I'd avoid videos and tutorials that use the 1st set of code.

Can I initialize a class with a variable which is defined by a function inside that class in Swift 2?

Is it possible that the variable which initializes my class is computed by a function inside that class?
I first initialize MyClass:
let MyClass = MyClass()
Second, this would be the code in the class:
class MyClass {
var myString = computeRandomString()
func computeRandomString() -> String {
piece of code
return(randomString)
}
}
Whenever I create an instance of MyClass I want the myString to be a randomString. In order for that I made a function within the same class.
There are two options.
First, if computeRandomString does not rely on knowing about anything about specific instances of this class, it could be converted to a class function, or simply moved outside of the class entirely.
For example:
class MyClass {
var myString = MyClass.computeRandomString()
class func computeRandomString() -> String {
return "random string"
}
}
or
func computeRandomString() -> String {
return "random string"
}
class MyClass {
var myString = computeRandomString()
}
The second option is to set it in the initializers (rather than giving it a default value), but you'll only be able to do this after all values (including this one) have been assigned a value.
For example:
class MyClass {
var myString: String
init() {
myString = ""
myString = computeRandomString()
}
func computeRandomString() -> String {
return "random string"
}
}
The reason we can't use an instance method to assign a default value for an instance variable is better explained by the warning generated when we try to use the instance method within an initializer before first giving it a value:
class MyClass {
var myString: String
init() {
myString = computeRandomString()
}
func computeRandomString() -> String {
return "random string"
}
}
On the myString =... line in init, we see the following error:
Use of 'self' in method call 'computeRandomString' before all stored properties are initialized.
This error, unfortunately, does not show up when we use it as the property's default value, as you're trying to do, but it does accurately describe the actual problem.
We cannot use self before our class is fully initialized, and that includes calling methods on self. And until all of our stored properties have valid values, our instance is not fully initialized, so we can never use an instance method to give a non-optional stored property its first value.
A possible solution is a lazy computed property.
The string is created when the property is accessed the first time
class MyClass {
lazy var computeRandomString : String = {
let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY0123456789"
let alphaLength = UInt32(alphabet.characters.count)
var randomString : String = ""
for _ in 0..<20 {
let random = Int(arc4random_uniform(alphaLength))
let index = alphabet.startIndex.advancedBy(random)
randomString += String(alphabet[index])
}
return randomString
}()
}
for _ in 0..<10 {
print(MyClass().computeRandomString)
}
You have to do it like this:
class MyClass {
var myString: String
init() {
self.myString = MyClass.computeRandomString()
}
static func computeRandomString() -> String {
piece of code
return(randomString)
}
}
This will set myString to the results of computeRandomString() on initialisation (creating) of a MyClass-object.
I changed computeRandomString() to static because otherwise it could not be used before initialisation has finished, thanks to #nhgrif.