Probably I don't understand clearly Optional values:
class func albumsWithJSON(allResults: NSArray) -> [Album] {
var albums = [Album]()
if allResults.count>0 {
for result in allResults {
var name = result["trackName"] as? String
if name == nil {
name = result["collectionName"] as? String
}
var price = result["formattedPrice"] as? String
if price == nil {
price = result["collectionPrice"] as? String
if price == nil {
var priceFloat: Float? = result["collectionPrice"] as? Float
var nf: NSNumberFormatter = NSNumberFormatter()
nf.maximumFractionDigits = 2;
if priceFloat != nil {
price = "$"+nf.stringFromNumber(priceFloat)
}
}
}
let thumbnailURL = result["artworkUrl60"] as String
let imageURL = result["artworkUrl100"] as String
let artistURL = result["artistViewUrl"] as? String
var itemURL = result["collectionViewUrl"] as? String
if itemURL == nil {
itemURL = result["trackViewUrl"] as? String
}
var newAlbum = Album(name: name!, price: price!, thumbnailImageURL: thumbnailURL, largeImageURL: imageURL, itemURL: itemURL!, artistURL: artistURL!)
albums.append(newAlbum)
}
}
}
At this line I get "unexpectedly found nil while unwrapping an Optional value" error:
var newAlbum = Album(name: name!, price: price!, thumbnailImageURL: thumbnailURL, largeImageURL: imageURL, itemURL: itemURL!, artistURL: artistURL!)
Obviously some informations are missing from JSON, but how can I handle a missing value?
All that error means is that one of the values that you're passing into that function is nil. When you put the ! at the end of those values, it unwraps it. If the value is nil while unwrapping, it throws an exception.
The way to fix this is dependent on how important the values being nil is. If you cannot live with any one of those being nil, then you'll have to check for nil and do something. If you can have them be nil, then you'll want to make sure that the function accepts nil values.
To better understand optionals, you should read the docs: https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_483
Related
I'm a noob, bear with me:
func createEmployeeCode() -> String? {
let email = UserDefaults.standard.object(forKey: "email_Saved") as? String
let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String
if let emailString = email,
let employeeCodeString = employeeCode {
return (emailString+employeeCodeString)
}
return (emailString+employeeCodeString) //ERROR: Use of unresolved identifier 'employeeCodeString' & Use of unresolved identifier 'emailString'
}
I understand the reason the error shows is because I'm trying to return something that is in a different scope here, but how else can I get the function to return the 2 strings together without the "Optional[...]" tag?
Here's how I'd expect it to be done in a normal production app
(You wouldn't do any of this in a normal production app! But this is the "idiom" you're looking for.)
func createCodeIfPossible() -> String? {
guard let e = UserDefaults.standard.object(forKey: "email_Saved") else {
print("serious problem, there's no email saved")
// at this point the app is completely buggered, so give up
return ""
}
guard let c = UserDefaults.standard.object(forKey: "employeeCode_Saved") else {
print("serious problem, there's no code saved")
// at this point the app is completely buggered, so give up
return ""
}
return e + c
}
Do note that the return is largely meaningless - in the app in question, if one of the guards breaks you are "totally screwed". I'd probably just return a blank string (or more likely something like "name_error") since at this point the app architecture is hopelessly broken.
(Sidenote: use UserDefaults.standard.string(forKey:).)
The issue is that you can't know if those strings DO both exist or not--if they do, you already have a great if let that returns your answer. The question now is what do you want to do if one or both are nil? Maybe you'd like to return nil from the entire function. If so,
func createEmployeeCode() -> String? {
let email = UserDefaults.standard.string(forKey: "email_Saved")
let employeeCode = UserDefaults.standard.string(forKey: "employeeCode_Saved")
if let emailString = email,
let employeeCodeString = employeeCode {
return (emailString+employeeCodeString) //successful unwrapping, let's concatenate!
}
return nil //if one or both `if let`s fail, we end up here
}
Of course, you could do whatever you'd like in that "bad" case. Maybe you'd like to show whatever string you DO have. In that case:
func createEmployeeCode() -> String {
let email = UserDefaults.standard.string(forKey: "email_Saved")
let employeeCode = UserDefaults.standard.string(forKey: "employeeCode_Saved")
return (email ?? "") + (employeeCode ?? "") //this works in the "good" case, too, and uses the nil coalescing operator `??`
}
In this case, you can see that the return value is no longer optional. This is because even if neither string exists, it'll concatenate two empty strings. If this feels icky, you could keep your optional return value and do a quick check before returning:
if email == nil && employeeCode == nil { return nil }
func createEmployeeCode() -> String {
var finalString = String()
if let email = UserDefaults.standard.string(forKey: "email_Saved"), let employeeCode = UserDefaults.standard.string(forKey: "employeeCode_Saved") {
finalString = email + employeeCode
}
return finalString
}
When assign back the values from userDefaults you've been trying to get as an object instead of string
func createEmployeeCode() -> String? {
let email:String = UserDefaults.standard.string(forKey: "email_Saved") ?? ""
let employeeCode:String = UserDefaults.standard.string(forKey: "employeeCode_Saved") ?? ""
let emailString = "\(email)\(employeeCode)"
return emialString
}
There are different ways to solve this depending on what you're trying to achieve.
If you always want to create an employeeCode (even if the code will be empty):
Try using a "nil coalescing operator".
func createEmployeeCode() -> String {
let email = UserDefaults.standard.object(forKey: "email_Saved") as? String ?? ""
let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String ?? ""
return (email+employeeCode)
}
To explain what's happening here:
We're unwrapping email, if we don't find email then we default the value to an empty string, "".
We do the same with employeeCode.
This isn't a way I would solve every unwrap issue though, but it suits your usecase of email and employeeCode because you're always wanting to return something based on your original question. I've also changed the return type to non-optional.
If an employee code must always contain an email and and a code then we want to return nil if one of those isn't found.
Try using the guard statement. The guard statement is perfect for validation and very readable:
func createEmployeeCode() -> String? {
guard let email = UserDefaults.standard.object(forKey: "email_Saved") as? String else { return nil }
guard let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String else { return nil }
return (email+employeeCode)
}
Try this function:
func createEmployeeCode() -> String {
let email = UserDefaults.standard.object(forKey: "email_Saved") as? String
let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String
return [email, employeeCode].compactMap { $0 }.joined(separator: "")
}
It will return email or employeeCode or email+employeeCode in case one of them is nil or both are present, or empty String in case if both are missed out!
After I've updated my project I get this error:
Unexpectedly found nil while unwrapping an Optional value
class Search {
private var _username: String!
private var _userImg: String!
private var _userKey: String!
private var _userRef: DatabaseReference!
var currentUser = KeychainWrapper.standard.string(forKey: "uid")
var username: String {
return _username <- error
}
var userImg: String {
return _userImg
}
var userKey: String{
return _userKey
}
init(username: String, userImg: String) {
_username = username
_userImg = userImg
}
init(userKey: String, postData: Dictionary<String, AnyObject>) {
_userKey = userKey
if let username = postData["username"] as? String {
_username = username
}
if let userImg = postData["userImg"] as? String {
_userImg = userImg
}
_userRef = Database.database().reference().child("messages").child(_userKey)
}
}
It worked fine under Swift 3 and Firebase 3.0.2, but now, where everything is update, it crashes all the time. It's not a duplicate to any other question as it worked all before.
I am not sure I fully understand the question or what exactly is causing the crash (it's probably a missing child node) or what the use case is of the implicitly unwrapped class vars but in response to a comment, here's what I would do in Swift 4, Firebase 4
Leave your Search class as is except change the init to the following (this is shortened to provide context)
init(withSnap: DataSnapshot) {
_userKey = withSnap.key
let dict = withSnap.value as! [String: Any]
_username = dict["username"] as? String ?? "NO USER NAME!"
_userImg = dict["userImg"] as? String ?? "NO IMAGE"
}
and then the Firebase function to get a user (for example) would look like this
let userRef = self.ref.child("users").child("uid_0")
userRef.observeSingleEvent(of: .value, with: { snapshot in
let mySearch = Search(withSnap: snapshot)
print(mySearch.userKey, mySearch.username, mySearch.userImg)
})
You would need to add in the rest of the class code to assign _userRef etc.
The idea here is to provide default values to the required class properties in case one of the Firebase nodes didn't exist and results in nil. i.e. if uid_0 didn't have a Username child node your class would crash (which it is). With the code above, that property would be set to a default value.
And for thoroughness suppose a user node looks like this
users
uid_0: "some string" //the value here is a String, not a Dictionary
that would crash my code. To prevent that, add more error checking in the init
init(withSnap: DataSnapshot) {
_userKey = withSnap.key
if let dict = withSnap.value as? [String: Any] {
_username = dict["username"] as? String ?? "NO USER NAME!"
_userImg = dict["userImg"] as? String ?? "NO IMAGE"
} else {
_username = "No user data"
_userImg = "No user data"
}
}
I am a beginner at Swift and I am migrating an app to Swift 3.0
I keep having this error and I have no idea how to solve it.
"Cannot invoke 'value' with an argument list of type '(String)'"
It is displayed at nearly each line of this snippet.
Do you have any idea where it could come from?
Thanks a lot
if ((message as AnyObject).value("content") != nil){
stringContent = (message as AnyObject).value("content") as? String
}
if ((message as AnyObject).value("sender_uuid") != nil){
stringSenderUuid = (message as AnyObject).value("sender_uuid") as? String
}
if ((message as AnyObject).value("recipient_uuid") != nil){
stringRecipientUuid = (message as AnyObject).value("recipient_uuid") as! String
}
if ((message as AnyObject).value("date") != nil){
if let result_number = (message as AnyObject).value("date") as? NSNumber
{
stringDate = "\(result_number)"
}
else {
stringDate = (message as AnyObject).value("date") as! String
}
}
As requested here is more information about Messages
class Messages: Object {
dynamic var channel_name = ""
dynamic var content = ""
dynamic var sender_uuid = ""
dynamic var recipient_uuid = ""
dynamic var date = ""
dynamic var message_uuid = ""
override class func primaryKey() -> String? {
return "message_uuid"
} }
let message = Messages()
message.channel_name=channel_name
message.content=stringContent
message.sender_uuid = stringSenderUuid
message.recipient_uuid = stringRecipientUuid
message.date = stringDate
message.message_uuid = stringUuid
Here is even more information
// initialize with nothing
var stringContent = " "
var stringSenderUuid = " "
var stringRecipientUuid = " "
var stringDate = " "
var stringUuid = " "
// We check for existence in the dictionnary
if (message["content"] != nil){
stringContent = message["content"] as! String
}
if (message["sender_uuid"] != nil){
stringSenderUuid = message["sender_uuid"] as! String
}
if (message["recipient_uuid"] != nil){
stringRecipientUuid = message["recipient_uuid"] as! String
}
if (message["date"] != nil){
if let result_number = message.value(forKey: "date") as? NSNumber
{
stringDate = "\(result_number)"
}
else {
stringDate = message.value(forKey: "date") as! String
}
}
if (message["uuid"] != nil){
stringUuid = message["uuid"] as! String
}
You probably need to change them to (message as AnyObject).value(forKey:"···") (adding the forKey: label).
Do you know what kind of object message is supposed to be? Repeatedly casting it to AnyObject is odd. It would be cleaner to create a new variable - let messageObject = message as AnyObject and then call messageObject.value(forKey:"···"). (I suspect you really want to cast it to Dictionary or something like that, in which case you can do messageDictionary["···"] instead of calling value(forKey:).)
Also, in Swift you can do this to reduce redundancy even more:
if let content = messageObject.value(forKey:"content") as? String {
stringContent = content
}
If message is json dictionary then cast it to [String:Any] and then use subscript with it.
if let dic = message as? [String:Any] {
stringContent = dic["content"] as? String ?? ""
}
OR
If message is type of Messages then you can access its property directly using message.content and same way you can access other properties too.
stringContent = message.content
If you're getting this error in testing (e.g. using LLReactiveMatchers's sendValues()), try to make your type (struct/class) conform to Equatable.
I am on point where I gotta compare non optional value with nil. But I can't do it because Xcode says:
Comparing non-optional value of type 'Int' to nil always returns false
So I created Struct and then made variable: var products: [Product] = []
How I am able to compare it with nil?:
if products[indexPath.row].snusPortions == nil
{
cell.snusPortionsAmountLabel.text = "N/A"
}else
{
cell.snusPortionsAmountLabel.text = String(products[indexPath.row].snusPortions)
}
I've assigned values to them like this:
let ref = FIRDatabase.database().reference().child("Snuses").queryOrdered(byChild: "Brand").queryEqual(toValue: brandName)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
let enumerator = snapshot.children
while let thisProduct = enumerator.nextObject() as? FIRDataSnapshot
{
print(thisProduct.value) // So I may see what the data is like and know how to extract it
// Chances are you'd have to create a dictionary
let thisProductDict = thisProduct.value as! [String:AnyObject]
let productName = thisProductDict["Products"] as! String
let snusPortions = thisProductDict["PortionsCan"] as? Int
let productObject = Product(snusProductTitle: productName, snusNicotine: snusNicotine, snusPortions: snusPortions!, snusFlavor: snusFlavor, snusWeight: snusWeight!, snusShippingWeight: snusShippingWeight, snusProductImageURL: productURL)
self.products.append(productObject)
print(self.products)
}
self.tableView.reloadData()
}
})
This is Product struct:
struct Product {
var snusProductTitle: String
init()
{
snusProductTitle = ""
}
init(snusProductTitle: String){
self.snusProductTitle = snusProductTitle
}
}
While testing it says snusPortions is nil but I said to make it "N/A" if it is nil, why?
It sounds like you are confusing yourself between the local variable snusPortions and the Product property snusPortions.
In your Product definition, the property snusPortions is an Int. It can never be nil. Hence, in this code:
if products[indexPath.row].snusPortions == nil
... this Product's snusPortions will never be nil, and we will never set the text to "N/A".
Now let's look at your other code:
let snusPortions = thisProductDict["PortionsCan"] as? Int
This is a completely different snusPortions. It can be nil, namely, if thisProductDict lacks a "PortionsCan" key or if its value is not castable to Int.
I am trying to get data from parse.com and add the data into an array or dictionary. I keep getting errors does anyone know the best solution to get data from parse. Is it possible to return the dictionary and use it in another function?
fatal error: unexpectedly found nil while unwrapping an Optional value
func getQuote() {
var query = PFQuery(className: "Quote")
var quote = PFObject(className:"Quote")
var quoteDictionary: [String:String]!
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
var quoteText = object["quoteText"] as String
var quoteAuthor = object["quoteAuthor"] as String
quoteDictionary[quoteAuthor] = quoteText
println(quoteDictionary)
println(quoteAuthor)
println(quoteText)
}
}
} else {
println("Error")
}
}
}
Play it safe and use defaults:
let quoteText = object["quoteText"] as? String ?? ""
let quoteAuthor = object["quoteAuthor"] as? String ?? ""
This safely handles empty values.