Help me with Optional hell in Swift. How to return count of array for key "R". self.jsonObj can be null
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return (self.jsonObj["R"]! as? NSArray)?.count;
}
Let's take this a step at a time.
self.jsonObj may be nil so you need to treat it as an Optional:
self.jsonObj?["R"]
This will either return 1) nil if self.jsonObj is nil or if "R" is not a valid key or if the value associated with "R" is nil 2) an Optional wrapped object of some type. In other words, you have an Optional of some unknown type.
The next step is to find out if it is an NSArray:
(self.jsonObj?["R"] as? NSArray)
This will return an Optional of type NSArray? which could be nil for the above reasons or nil because the object in self.jsonObj was some other object type.
So now that you have an Optional NSArray, unwrap it if you can and call count:
(self.jsonObj?["R"] as? NSArray)?.count
This will call count if there is an NSArray or return nil for the above reasons. In other words, now you have an Int?
Finally you can use the nil coalescing operator to return the value or zero if you have nil at this point:
(self.jsonObj?["R"] as? NSArray)?.count ?? 0
I'm guessing that you'll want to return 0 if there's nothing in the array. In that case, try the Nil Coalescing Operator:
return (self.jsonObj?["R"] as? NSArray)?.count ?? 0;
Edit: As #vacawama's answer points out, it should be self.jsonObj?["R"] instead of self.jsonObj["R"]! in case self.jsonObj is nil.
assuming self.jsonObj is NSDictionary? or Dictionary<String, AnyObject>?
return self.jsonObj?["R"]?.count ?? 0
Related
I have a NSSize variable
var originalselectedimagesize:NSSize
if(originalselectedimagesize == nil)
{
}
I'm trying to check if NSSize is set ? But i keep getting the following warning.How can i check if the value of NSSize is changed?
h(aka 'CGSize') to 'nil' always returns false
Because from the declaration, NSSize is not an optional. So checking a non-optional value for nil always return false.
In case if you have the following, then you won't get the warning.
var originalselectedimagesize:NSSize?
if(originalselectedimagesize == nil)
{
}
You can declare originalselectedimagesize as NSSize? (Optional type of NSSize), and set it to nil. Then, you can check if it has value like this:
var originalselectedimagesize: NSSize? = nil
// what ever...
// check for value
if let size = originalselectedimagesize {
}
I'm trying to migrate an objc project to swift3. I'm not sure how can I compare an array to nil. I have found this topic, but that was 2 years ago and the swift's syntax has changed a lot.
If I have a code like this in swift:
let variable = something as? NSArray
if variable == nil {
// do something
}
It won't let me to compare this variable with nil, causing an error "comparing this variable, always returns false". I have tried comparing variable.description with " ", but does it do the same thing?
By "something" i meant:
var variable = dict.object(forKey: someString) as! NSArray
The main thing I wanted to do with this was:
var variable = dict.object(forKey: someString) as! NSArray
if variable == nil {
//create
}
else {
// append
}
That's what the optional unwrapping syntax is for. You can combine the unwrapping and cast into one if statement:
if let variable = something as? NSArray {
// variable is not nil and is an NSArray
// Now you can do something with it.
} else {
// Either something is nil or it is not able to be cast as an NSArray
// Handle this case.
}
I should also mention that if you don't need to use something in Objective-C, then you should use the Swift-native array type. This can be declared like this:
let someArray = ["string1", "string2"]
This line indicates that variable is and must be an NSArray. If dict.object(forKey: someString) is not an NSArray, this will cause a crash
var variable = dict.object(forKey: someString) as! NSArray
// ^
// This exclamation mark means you are certain this is an NSArray
// Also, because there is no question mark after NSArray, this variable
// is not optional. It cannot be nil
However, you then use
if variable == nil {
And this is where the warning comes from. The variable can never be nil, because the variable is not optional
What you probably want is:
if let variable = dict.object(forKey:someString) as? NSArray
This will return false if:
dict.object(forKey:someString) returns a nil object
the object returned is not an NSArray
After this variable is now a non-optional NSArray. It is guaranteed to be an NSArray and is guaranteed to not be nil. You can use it without unwrapping it. e.g.
if let variable = dict.object(forKey:someString) as? NSArray {
for element in variable {
}
}
else {
//The dict doesn't contain the object yet. `variable` is nil
//Create a new array and add it to dict
let newArray = ["First Value"]
dict[someString] = newArray
}
let variable = something as? NSArray
With this declaration, variable will be an optional type (NSArray?) and never nil. This is because casting with as? returns an optional value that either contains the successfully casted object or nothing. You can see this by alt-clicking the variable name in Xcode.
If you want to know whether it contains a value, you need to use the if let syntax:
if let variable = variable {
// variable is guaranteed to be an NSArray here.
}
You can also use this format with guard-else:
guard let variable = something as? NSArray else {
// your variable is nil. Do something if needed
}
// your variable is available in this scope. Do something when variable contains Array
So I'm tryin gto make the following code work under swift 3, but no matter what I try I just cause new errors. I can't seem to figure out how to cast the dataArray object into anything that will pass. (Original dev didn't type it, and it's always set via notification objects, making tracing it's actual data type down... difficult; best I can tell it's just a dictionary generated from server JSON via parsing)
var dataArray:NSMutableArray = []
func foo(_ notification: Notification)
{
if let id = notification.object as? Int
{
for dataOut in dataArray where Int(dataOut["id"] as! Int) == id {
self.performSegue(withIdentifier: "fooSegue", sender: dataOut)
return;
}
}
}
Trying to compile this produces a syntax error about Type 'NSFastEnumerationIterator.Element' (aka 'Any') has no subscript members.
Is there a necessary reason why dataArray is of type NSMutableArray? If I were receiving some kind of Array object which I assert to contain elements of a Dictionary type, I would do the following:
if let id = id as? Int,
let data = dataArray as NSArray as? [[String:Any]]
{
for element in data where Int(element["id"] as! Int) == id
{
self.performSegue(withIdentifier: "fooSegue", sender: element)
return
}
}
Edited to cast dataArray from NSMutableArray to NSArray to [[String:Any]] and subscript element instead of data.
I'm new to Swift and is trying out the beginner's project of building a calculator. I understand that "display.text" returns an optional string and the string value inside of it has to be unwrapped with "!" before it can be used.
However, I have noticed "display.text" only needs to be unwrapped once and then it can be used multiple times without unwrapping it again. Is this true for Swift optional values? Where could I find some guidelines regarding to the matter?
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var display: UILabel!
var userIsInTheMiddleOfTypingANumber = false
#IBAction func appendDigit(sender: UIButton) {
let digit = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber {
display.text = display.text! + digit
} else {
display.text = digit
userIsInTheMiddleOfTypingANumber = true
}
}
Two standard options for unwrapping an optional where it can be used again:
1) if let
if let unwrappedValue = someOptional {
// unwrappedValue can be used only inside this block of code
}
2) guard
guard let unwrappedValue = someOptional else { return }
/// unwrappedValue can be used as long as it is in scope
Try this.
guard let displayText = display.text else {
// handle the case when optional has nil value
}
// In this scope you can use non-nil value of display.text as displayText.
print("displayText = \(displayText)")
This is how you can use optional values after unwrapping once. Another simpler way would be to just use instead of guard-let.
if let displayText = display.text else {
// safely unwrapped
}
Hope that helped!
Check this link for more help.
You should generally avoid to forcibly unwrap optionals (with operator !), as this will yield a runtime exception in case the optional contains nil. Below follows some techniques to handle unwrapping of optionals.
Optional binding
Note that the only way you can "unwrap it once and then use it multiple times" if if you unwrap and assign it to another non-optional variable to the same inherent type.
This is what is done when using optional binding:
/* Example setup */
let display: UILabel = UILabel()
let digit = "1"
/* optional binding using if-let:
_assign_ unwrapped value (if non-nil) to 'unwrapped' */
if let unwrappedText = display.text {
// 'unwrapped' resides in scope inside of the if-let block
display.text = unwrappedText + digit
}
else {
display.text = digit
}
/* optional binding using guard-let-else:
_assign_ unwrapped value (if non-nil) to 'unwrapped' */
func foo(disp: UILabel, _ dig: String) {
guard let unwrappedText = display.text else {
display.text = digit
return
}
// 'unwrapped' resides in scope outside of the guard-let-else block
display.text = unwrappedText + digit
}
foo(display, digit)
Nil coalescing operator
If you don't want to explicitly assign the unwrapped value using conditional binding, you can make use of the nil coalescing operator for safe unwrapping.
/* nil coalescing operator */
display.text = (display.text ?? "") + digit
Now, you could, however, use the nil coalescing operator in a semi-optional-binding fashion; assign the unwrapped value of an optional or some default value if the optional is nil:
let metaUnwrapped = display.text ?? ""
Immutable metaUnwrapped would be available in its scope, and contain the value of display.text (at assignment), if non-nil, or the default value "", if display.text was nil at assignment. You could use metaUnwrapped in the same fashion as immutable unwrapped in the optional binding examples above:
display.text = metaUnwrapped + digit
Optional chaining
This is slightly off-base w.r.t. your question, but since we're on the subject of optionals and unwrapping, I might as well mention optional chaining.
Optional chaining can be used to access properties of some optional property, given that the optional property is not nil. As an example, say you want to count the number of characters in the display.text, but naturally only if the optional .text property is non-nil. In this case, optional chaining combined with the nil coalescing operator could be a proper method of choice:
let numCharacters = display.text?.characters.count ?? 0
/* if text != nil, returns character count; otherwise, by
nil coalescing operator, returns 0 /*
I'm new in the swift2 world and I currently struggle with a simple function :
// Get all moves for the first category
func getMuscles() -> BodyPart {
let bpart:BodyPart?
if let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext {
do{
let fetchRequest = NSFetchRequest(entityName: "BodyPart")
let fetchResults = try managedObjectContext.executeFetchRequest(fetchRequest) as! [BodyPart]
bpart = fetchResults[0]
} catch let error as NSError {
print(error)
bpart = nil
}
}
return bpart
}
How can I solve this issue ? And what are the 'best-practices' in swift2 for defining a function ?
Thank you
EDIT 1
I've tried to change the signature of the method, but error is still here :
The question you should be asking yourself is whether getMuscles() must always return an object or if it's fine for it to return a nil.
By changing the method signature to func getMuscles() -> BodyPart?,
you're basically stating that a nil might be returned from that method,
thus solving your immediate compile time issue.
In that particular context, because you're fetching objects from CoreData,
it might be wise to allow getMuscles() to return a nil.
The way you define your functions (if they return optionals ?, or not) entirely depends on the calling code.
Change your method signature to :
func getMuscles() -> BodyPart?
But be careful while unwrapping the return value when the this function is being called.
Just return:
func getMuscles() -> BodyPart? { }
Thats nothing to do with SWIFT2.. The return type is expecting some value BodyPart not an optional value BodyPart?...But you are returning a optional value bpart
func getMuscles() -> BodyPart {
let bpart:BodyPart?
....
return bpart
}
If you want to return bpart as it is you need to create the return type as optional
func getMuscles() -> BodyPart? {
let bpart:BodyPart?
....
return bpart
}
or if you want to just return the value try this
func getMuscles() -> BodyPart {
let bpart:BodyPart = ()//initialize here dont make optional
....
return bpart
}