I have a Firebase instance where I want to store a dictionary of values I want to store into firebase. I have looked on the documentation https://www.firebase.com/docs/ios/guide/saving-data.html as a reference but can't seem to get it to work. The following is my attempt:
//Declared above are the currentUser values as so:
var currentUserFirstName: String!
var currentUserLastName: String!
var currentUserObjectID: String!
var attendeesArray = ["objectID": currentUserObjectID, "name": currentUserFirstName + " " + currentUserLastName]
var eventRefChild = EventReference.childByAutoId()
eventRefChild.setValue([
"eventName":eventName.text,
"attendees": attendeesArray,
"eventCreator": currentUserFirstName
])
But I keep getting an error saying: Could not find an overload for '+' that accepts the supplied arguments when I try to do eventRefChild.setValue([... and I'm honestly not too sure why I am getting this issue. Any help would be appreciated!
EDIT: The variable EventReference is assigned as so: EventReference = Firebase(url:"<Insert Firebase URL>")
And inside currentUserFirstName and currentUserLastName are an individual's first and last name grabbed from Facebook so it would look something like Bob Smith respectively.
There is nothing wrong with your code. The issue is the values that are being loaded into
var currentUserFirstName: String!
var currentUserLastName: String!
As a test, I created a sample project with the following code, which is a duplicate of your posted code but with normal strings loaded into the var's:
var myRootRef = Firebase(url:"https://myproject.firebaseIO.com/")
var currentUserFirstName = "Test"
var currentUserLastName = "User"
var currentUserObjectID = "object ID"
var attendeesArray = ["objectID": currentUserObjectID, "name": currentUserFirstName + " " + currentUserLastName]
var eventRefChild = myRootRef.childByAutoId()
eventRefChild.setValue([
"eventName": "eventName",
"attendees": attendeesArray
])
the project compiled and runs correctly and the expected data is written to Firebase. Note that the eventName.text was replaced with a string as well but that doesn't affect the answer.
The investigation needs to turn to what's loaded in the var's, and the answer is that one of those var's, currentUserFirstName or currentUserLastName is being loaded with an OBJECT (class), not a string.
As a side note, why are the var's declared as implicitly unwrapped optionals (the !)
edit: adding additional info to deal with the optionals
if let actualString = currentUserFirstName {
println(actualString) //proceed working with the the optional string
}
else {
return // something bad happened! currentUserFirstName does not contain a string
}
To prevent code from errors when an optional contains no value, add the above code directly above the concatenation line of code. What happens here is the we are assigning the string in currentUserFirstName (optional var) to a actual string (a standard, non-optional var).
If the expression evaluates to true then we can proceed evaluating currentUserFirstName.
If it's false, then currentUserFirstName does not contain a string so handle the error elegantly.
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).
I had some older Swift code that used to compile and work where I was using the .append to build out a data structure dynamically. After upgrading to a few compiler versions newer I am getting the dreaded "Extra Argument ' ' in call" error. I reduced the code down to this:
struct EHSearch {
let EHcategory : String = ""
let EHname : String = ""
}
var myEHSearch = [EHSearch]()
// Call to dynamically append the results
// Extra argument: 'EHcategory' in call
myEHSearch.append(EHSearch(EHcategory: "Food", EHname: "Joes Crab Shack"))
I can't see anything so far in searching on what has changed to cause this one so seeking guidance here.
Because you have let in your struct.
Define your structure like this:
struct EHSearch {
var EHcategory : String = ""
var EHname : String = ""
}
If you have constants in your struct, you can not provide them initial value while creating new structure instances. The automatically-generated member-wise initializer doesn't accept let members as parameters of the initializer of struct.
It depends on your intentions with the struct's properties. Do you want them to be mutable or not?
If yes, then #sasquatch's answer will do.
If not, then you need to ensure a value is assigned to them only once. As you already do that in the struct declaration (the default values), you can't assign new values to them. But being a struct, they don't need to have default values - moreover, struct automatically receive a memberwise initializer. https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html
So here is the variant for immutable properties:
struct EHSearch {
let EHcategory : String
let EHname : String
}
var myEHSearch = [EHSearch]()
// Call to dynamically append the results
// Extra argument: 'EHcategory' in call
myEHSearch.append(EHSearch(EHcategory: "Food", EHname: "Joes Crab Shack"))
The "Extra Argument" error you're seeing is because the compiler already has values for the properties so it doesn't expect any new ones. Here is the "middle" way - one property has a default value whilst the other doesn't - which should make it clearer:
struct EHSearch {
let EHcategory : String = ""
let EHname : String
}
var myEHSearch = [EHSearch]()
// Call to dynamically append the results
// Extra argument: 'EHcategory' in call
myEHSearch.append(EHSearch(EHname: "Joes Crab Shack"))
I'm new to swift and I'm having some difficulties understanding on how to use (!) and (?)
As far as I know, we can use (?) when there are instances that a variable can be nil.
And use (!) when you are 100% sure that item is not nil.
1. Working Fine - Optionals
var john:String?
john = "Is my name"
println(john!)
2. Crashes on Runtime - ! must not be nil - means this is correct
var john:String?
println(john!)
3. Works Fine
var dict: [String:AnyObject] = Dictionary()
dict["name"] = "John"
var str: String = dict["name"]! as String <--- Taking away the (!) says it's not convertible to String
4. Cannot Run/Build - for me it's similar to 1.
var dict: [String:AnyObject]? = Dictionary() ---error says does not have a member named 'subscript'
dict["name"] = "John"
var str: String = dict["name"]! as String
5. Unexpectedly found nil while unwrapping an optional value
var dict: [String:AnyObject] = Dictionary()
dict["name"]? = "John"
var str: String = dict["name"]! as String
Would be great if someone can help me understand these things. Thanks!
it is a bit misleading interpretation believing when an ! 'marks' an ivar then that 100% cannot be nil. it can be. you can say only, you got the value as already unwrapped, so you don't need to force unwrapping it again – but it can be nil.
try this example for instance:
var text: String! = "hello"
text = nil;
println(text)
it prints a nil for you.
the reason why your app can crash is you force unwrapping an optional which is nil, that is invalid operand.
#4
line-by-line:
var dict: [String:AnyObject]? = Dictionary() // from OP
your dict is an optional, let us see what you are doing here:
dict["name"] = "John" // from OP
var str: String = dict["name"]! as String // from OP
you have an optional dict and you'd like to use it somehow, you have two possible ways to do it:
(A) via optional chaining;
(B) via forced unwrapping;
(A)
dict?["name"] = "John" // optional chaining
it is quite straightforward, it assigns the new value for the key name if the dictionary is not nil, otherwise the chain generously falls and nothing happens in runtime.
in perspective of this line:
var str: String = dict!["name"]! as String // forcibly unwrapped
it crashes in runtime if either the dictionary or the value for the key was nil (as per the first paragraph says: invalid operand to force unwrapping a nil), but the str would be John if the dictionary and the key both do valid objects.
(B)
dict!["name"] = "John" // forcibly unwrapped
it works like a charm and assigns the new value for the key name if the dict exists; but if the dict was nil, that is a termination point in runtime (aka crash), because nil cannot be unwrapped forcibly (see above).
#5
line-by-line:
var dict: [String:AnyObject] = Dictionary() // from OP
your dict is not optional and not even nil, but the dictionary is literally empty, so no key does exist in it, including the name.
dict["name"]? = "John" // from OP
var str: String = dict["name"]! as String // from OP
the optional chaining always falls when any of the element of the chain falls – therefore no new value will be assigned in your code, but the falling happens gracefully, so you bypass the first line about assigning the new value, but the app crashes in the second line because the value does not exists and you try to force unwrapping it (see above about invalid operand).
so, you need to drop the optional chaining from the first line, if you want to assign a new value for a non-existing key:
dict["name"] = "John"
the optional chaining is useful if you would not like to change the original dictionary with adding a new key/value, but you would like to override an existing one only:
dict["name"] = "John"
dict["name"]? = "Jack"
in that case the new value will be Jack, because the optional chaining won't fall as the key name is already existing with a different value, so it can be and will be overridden; but:
dict["name"] = nil
dict["name"]? = "Jack"
the optional chaining will falls and no new value is assigned here for the key.
NOTE: there would be many other things and ideas which can be told about the concept. the original documentation is available on Apple site under section Swift Resources.
Difference between String?,String! and String
I am using this code :
var myString:String? = nil
var myString1:String = ""
var myString2:String! = nil
println(myString2)
Here it's giving nil in myString2 instead of run-time error.
There are better explanations out there, but the simple version is:
String is a String. It holds a String value.
String? is an Optional String. It can hold a String value or nil. You can unwrap the optional and immediately try to access the String value by using ! like this:
var str: String? = nil
str!.length
That code will result in a runtime error because str is nil. A safer way to get the value of the optional is to use an if let:
var str: String? = nil
if let myStr = str{
myStr.length
}
That has the same functionality as the above code, but won't crash on nil values.
String! is an implicitly unwrapped optional. It works that same way as a regular optional but it is assumed to be non-nil, so when you call it it tries to access the value like it was a regular optional with ! after.
The reason println works on all these types is because it can take Strings, String Optionals, and nil values and handles them all.
var str: String? = "hello world"
println(str)
println(str!)
println(nil)
All should work.
#connor's answer is correct.
var Amy:Wife? // Amy is your girlfriend, she may or may not be your wife.
var Amy:Wife // Amy is your wife.
var Amy:Wife! // Amy will be your wife, no matter you like it or not.
While reading the The Swift Programming Language, I came across this snippet:
You can use if and let together to work with values that might be
missing. These values are represented as optionals. An optional value
either contains a value or contains nil to indicate that the value is
missing. Write a question mark (?) after the type of a value to mark
the value as optional.
// Snippet #1
var optionalString: String? = "Hello"
optionalString == nil
// Snippet #2
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Snippet #1 is clear enough, but what is happening in the Snippet #2? Can someone break it down and explain? Is it just an alternative to using an if - else block? what is the exact role of let in this case?
I did read this page, but still a little confused.
if let name = optionalName {
greeting = "Hello, \(name)"
}
This does two things:
it checks if optionalName has a value
if it does, it "unwraps" that value and assigns it to the String called name (which is only available inside of the conditional block).
Note that the type of name is String (not String?).
Without the let (i.e. with just if optionalName), it would still enter the block only if there is a value, but you'd have to manually/explicitly access the String as optionalName!.
// this line declares the variable optionalName which as a String optional which can contain either nil or a string.
//We have it store a string here
var optionalName: String? = "John Appleseed"
//this sets a variable greeting to hello. It is implicity a String. It is not an optional so it can never be nil
var greeting = "Hello!"
//now lets split this into two lines to make it easier. the first just copies optionalName into name. name is now a String optional as well.
let name = optionalName
//now this line checks if name is nil or has a value. if it has a value it executes the if block.
//You can only do this check on optionals. If you try using greeting in an if condition you will get an error
if name{
greeting = "Hello, \(name)"
}
String? is a boxed-type, variable optionalName either contains a String value or nothing(that is nil).
if let name = optionalName is an idiom, it unboxes the value out of optionalName and assign it to name. In the meanwhile, if the name is non-nil, the if branch is executed, otherwise the else branch is executed.