I created this class for my object City
class City: NSObject {
var _name:String = ""
var name:String {
get {
return _name
}
set (newVal) {
_name = newVal
}
}
}
then when I create my object I do:
var city:City!
city.name = "London" //crash here
println("name city is\(city.name)");
it crash when I set the name with message "fatal error: unexpectedly found nil while unwrapping an Optional value"
This is not actually an answer (see other answers for a solution, such as #Greg's and #zelib's), but an attempt to fix some mistakes I see in your code
No need to create computed + stored property (unless you have a reason for that):
class City: NSObject {
var name: String = ""
}
If you inherit from NSObject, you automatically lose all swift features - avoid it (unless you have a reason for that)
class City {
var name: String = ""
}
You are using an empty string as absence of value - swift provides optionals for that
class City {
var name: String?
}
Alternative to 3., a city without a name wouldn't make much sense, so you probably want each instance to have a name. Use non optional property and an initializer:
class City {
var name: String
init(name: String) {
self.name = name
}
}
Avoid implicitly unwrapped optionals (unless you have a reason for that):
var city: City
Just like any other object oriented programming language, and object should be initialized before accessing it.
Like:
var city:City
This is just reference of the object. So, actual memory is not created here. You need to create actual object for City Class.
Fix it by adding following statement:
city = City()
You haven't initialised the city variable and when you trying to use it it crash.
initialise it first before you use it:
city = City()
city.name = "London"
You are getting error because you are not initializing your city variable instead you just implicitly unwrap it without initializing at any stage. To initialize it you must use the following code
var city:City = City()
You have to call the init method. So you would do it like this :
var city:City=City() //Calls init and creates an instance
city.name="foo"
If you don't define an init method(it's always a good practice that you do), the default init method is called.
Related
I have a swift array of struct and I am unable edit the first property, whereas I am able edit the first property with an array of class.
In order to edit the first object of the struct array, I have to do [0] then .first
I know structs are valued by type, class are value by reference. But I don't understand the different behavior. Can someone explain?
class PersonObj {
var name = "Dheearj"
}
struct Person {
var name = "Dheearj"
mutating func update(name: String){
self.name = name
}
}
var array = [Person(),Person()]
array[0].update(name:"dheeraj")
array[0].name = "yuuu"
array.first?.name = "dddddd" <--- "Error Here"
var array1 = [PersonObj(),PersonObj()]
array1.first!.name = "ttt"
print(array1.first?.name ?? "")
print(array.first?.name ?? "")
print(array.count)
Screenshot of the error message:
Mutating a struct stored within some other property behaves as though you've copied out the value, modified it, and overwrote it back into place.
Take this line for example: (I replaced the optional chaining with force unwrapping, for simplicity)
array.first!.name = "dddddd"
It behaves as though you did:
var tmp = array.first!
tmp.name = "dddddd"
array.first = tmp
It's easy to see what that doesn't work. Array.first, is a get-only property (it doesn't have a setter).
The case for classses works because the value stored in the array is a reference to the object, and the reference isn't changing (only the values within the object it refers to, which the array doesn't know or care about).
Sorry if the title is rather confusing, but I'm curious to know the difference between these two lines:
var title = String()
var title: String
Is one being initialized and one only be declared? Which is more correct?
For example, if I have a struct should I use one of the other?
So the reason I ask this is because I'm learning about how to grab some JSON from a url and then display it in my app. One of the new ways of doing so is using Decodable. So, I have a struct in a model class like so:
struct Videos: Decodable {
var title = String()
var number_of_views : Int
var thumbnail_image_name: String
var channel: Channel
var duration: Int
}
In another class I have this:
URLSession.shared.dataTask(with: url){(data,response,error) in
if(error != nil){
print(error!)
return
}
guard let data = data else { return }
do{
self.Videos2 = try JSONDecoder().decode([Videos].self, from: data)
//self.collectionView?.reloadData()
}catch let jsonErr{
print(jsonErr)
}
}.resume()
So, should I declare or initialize the variables in my struct? I'm assuming I should just declare them like so:
var title: String?
Would that be the correct syntax in my struct?
UPDATE:
I understand this question was more broad then I originally proposed it to be. I'm sorry about that, but thank you so much for all your great answers that clarified a lot up for me.
The difference is that : defines the type of your variable, whereas = assigns an actual value to the variable.
So:
var title = String()
This calls the initializer of the String type, creating a new String instance. It then assigns this value to title. The type of title is inferred to be String because you're assigning an object of type String to it; however, you could also write this line explicitly as:
var title: String = String()
This would mean you are declaring a title variable of type String, and assigning a new String to it.
var title: String
This simply says you're defining a variable of type String. However, you are not assigning a value to it. You will need to assign something to this variable before you use it, or you will get a compile error (and if this is a property rather than just a variable, you'll need to assign it before you get to the end of your type's init() method, unless it's optional with ? after it, in which case it gets implicitly initialized to nil).
EDIT: For your example, I'd probably declare all the variables using let and :, assuming that your JSON provides values for all of those properties. The initializer generated by Decodable should then set all the properties when you create the object. So, something like:
struct Videos: Decodable {
let title: String
let number_of_views : Int
let thumbnail_image_name: String
let channel: Int
let duration: Int
}
This initializes a value
var title = String()
This declares a value but does not initialize it
var title: String
If you attempt to use the latter, such as print(title), you will get a compiler error stating Variable 'title' used before being initialized
It does not matter whether the value is a class or a struct.
The = operator is the assignment operator, it assigns a value to the object on the left of the =
Typically, class or struct properties are declared but not initialized until the init() is called. A simple class might be
class MyClass {
let myProperty: String
init(aString: String) {
self.myProperty = aString
}
}
Whereas inside the scope of a function you may declare a local variable that only lives inside the scope of the function.
func doSomethingToAString(aString: String) -> String {
let extraString = "Something"
let amendedString = aString + extraString
return amendedString
}
In your specific example, the struct synthesizes an initializer that will allow you to initialize the struct with all the values needed to fill your properties. The initializer generated by Decodable should then set all the properties when you create a Videos struct, you will do it something like:
let aVideos = Videos(title: "My Title", number_of_views: 0, thumbnail_image_name: "ImageName", channel: Channel(), duration: 10)
Is one being initialized and one only be declared?
Yes, meaning that the declared cannot be used. If you tried to set a value for it, you would get a compile-time error:
variable 'title' passed by reference before being initialized
Which is more correct?
There is no rule of thumb to determine which is more correct, that would be depends on is there a need to initialize title directly.
On another hand, when it comes to declare properties for a class, saying var title = String() means that you are give title an initial value ("") which means that you are able to create an instance of this class directly, example:
class Foo {
var title = String()
}
let myFoo = Foo()
However, if title declared as var title: String, you will have to implement the init for Foo:
class Foo {
var title: String
init(title: String) {
self.title = title
}
}
let myFoo = Foo(title: "")
Also, you have an option to declare it as lazy:
lazy var title = String()
which means:
A lazy stored property is a property whose initial value is not
calculated until the first time it is used. You indicate a lazy stored
property by writing the lazy modifier before its declaration.
Properties - Lazy Stored Properties
I'm experiencing slightly unusual behaviour when attempting to use computed properties to access linked Objects in a Realm Object subclass.
final class Patient: Object {
dynamic var name: String = ""
var parameters = List<Parameter>()
}
final class Parameter: Object {
dynamic var name: String = ""
dynamic var patient: Patient? {
return LinkingObjects(fromType: Patient.self, property: "parameters").first
}
}
The patient property on the Parameter class returns nil but, if you replace the code with the following, we get the expected behaviour:
var p = LinkingObjects(fromType: Patient.self, property: "parameters")
var q: Patient? {
return p.first
}
I suspect this is something to do with Realm's internal representation of LinkingObject. The code I used originally was referenced in a previous StackOverflow question and was accepted as a functional solution thus I guess it worked then so perhaps something has changed? Xcode 7, Swift 2.2
When Realm added the ability to query inverse relationships, that became the syntax to specify them. See https://realm.io/news/realm-objc-swift-0.100.0/ and https://realm.io/docs/swift/latest/#inverse-relationships for details.
I have a small problem in swift. Let's say I have a class called Pet.
Pet has a variable for name and noise, created like so:
class Pet
{
var name : String = ""
var canMakeNoise : Bool = true
}
Now, when I call initialise the class creating let's say a cat, I can easily do it like so:
var cat: Pet()
cat.name = "Garfield"
cat.canMakeNoise = false
This works smoothly, however when trying to pass it in directly using literal values like so:
let cat : Pet("Garfield",true)
or
let cat : Pet(name:"Garfield",canMakeNoise:true)
I get this error:
Swift Compile Error - Extra Argument in call
Why is that? How can I fix it? Thanks in advance.
If you want to add arguments to the initializer than you need to specify a new init function instead of relying on the default one. Here's how you'd do it in your case:
class Pet {
var name : String = ""
var canMakeNoise : Bool = true
init( name : String, canMakeNoise : Bool ) {
self.name = name
self.canMakeNoise = canMakeNoise
}
}
var kitty = Pet(name: "Cat", canMakeNoise: true)
Since you use default values for your variables, xCode does not find a need for initializer. So when you create a new instance of the class it will be a Pet with name = ”” and canMakeNoice = true.
If you want to change these default values you will need to supply a init method (and you can than remove these default values)
class Pet{
var name:String
var canMakeNoise:Bool
init(name:String, canMakeNoise:Bool){
self.name = name
self.canMakeNoise = canMakeNoise
}
convenience init (name:String){
self.name = name
self.init(name:name, canMakeNoise: true)
}
}
I’ve supplied two init methods here. The first one takes 2 arguments:
var cat: Pet = Pet(name: "Kitten", canMakeNoise: true)
This will create a Pet with the supplied name and the supplied canMakeNoise.
If you want to you can also supply a convenience init This is a shortened init. In this case used to be able to make another instance of the Pet class.
var dog: Pet = Pet(name: "Doggy")
As you see we don’t supply the canMakeNoise property here cause the convenience initializer does that for us (and uses true as the canMakeNoice)
I'm trying to figure out the best pattern to work with objects in Swift.
i think i got it right with the initializers, both convenience and default... but what happen with the class factory methods?
I tried to create a simple class Person and a subclass Student, with few properties and methods. is it the most correct way to do it?
class Person{
var _name: String
var _surname: String
var _dateOfBirthday: String
var _phoneNumb: [String]
init(name:String, surname:String, dateOfBirthday:String, phone:[String]){
self._name = name
self._surname = surname
self._dateOfBirthday = dateOfBirthday
self._phoneNumb = phone
}
convenience init() {
self.init(name:"",surname:"",dateOfBirthday:"", phone:[])
}
convenience init(name:String){
self.init(name:name,surname:"",dateOfBirthday:"", phone:[])
}
}
class Student:Person{
var _studentId:Int
init(name: String, surname: String, dateOfBirthday: String, phone: [String], id:Int) {
self._studentId = id
super.init(name: "", surname: "", dateOfBirthday: "", phone: [])
}
convenience init(){
self.init(name: "", surname: "", dateOfBirthday: "", phone: [], id:0)
}
convenience init(name:String){
self.init(name:name,surname:"",dateOfBirthday:"", phone:[], id:0)
}
}
what if i want to add a class factory method? would it be something like this or i'm doing it wrong?
class func Person() -> Person {
var x = Person()
x._telephoneNumber = [String]() // is this needed? or i can initialize it later?
return x
}
class func PersonWithName(name:String) -> Person {
var x = Person(name:name, surname:"", dateOfBirthday:"", telephoneNumber:[])
return x
}
is this correct? why would it be better to use the init instead of the class factory?
is this correct? why would it be better to use the init instead of the class factory?
Why would you create a "class factory" if you can use init? init is idiomatic Swift way of creating new objects of a class.
Adding convenience initializers is the right choice in most cases when you want to add a shortcut to class's main (designated) initializer. However, in your case, they are completely unnecessary, because Swift supports default argument values.
Just define your initializer like so:
init(name:String = "", surname:String = "", dateOfBirthday:String = "", phone:[String] = []) { ... }
This way, you can invoke it as Person() or Person(name: "Andrew") or with any other combination of arguments.
EDIT:
As a side note, prefixing instance variables with an underscore generally doesn't seem to be idiomatic Swift. It's okay to omit the underscore and use self. to disambiguate between local and instance variables:
self.name = name
self.surname = surname
self.dateOfBirthday = dateOfBirthday
self.phoneNumb = phone
Prior to the recent Xcode 6.1 and Swift 1.1, it was necessary to use factory pattern if construction could fail because init() could not return optionals. This was also why many cocoa/objective-c libraries imported had factory methods.
With the release of Xcode 6.1 and Swift 1.1 and the support for init() that can return optionals, you should use the init() pattern with convenience initializers. With this release, Apple also changed their cocoa/objective-c imports to use the init() -> T? pattern over the factory method.
See https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html for the release notes.