I'm confused about behavior of subscribe method in RxSwift.
This is sample code.
let observer1 = PublishSubject<String>()
let observer2 = PublishSubject<String?>()
let observable1 = Observable.just("")
let observable2 = observable1.map { $0 }
_ = observable1.subscribe(observer1) // #1. OK
_ = observable1.subscribe(observer2) // #2. Error
_ = observable2.subscribe(observer2) // #3. Error
_ = observable1.map{ $0 }.subscribe(observer2) // #4. OK
I understand that #2 and #3 get an error.
Because the observer is a nullable-string type, it is strictly different from the type that the observable holds.
But I can not understand #4.
If directly subscribe of the mapped observable, it did not get an error.
As shown in #3, the return value of the mapped observable1 was Observable.
I look forward to reply.
This is because .map { $0 } actually returns a Observable<String?> in the fourth case!
Ww can cast the return value to a Observable<String>:
_ = (observable1.map{ $0 } as Observable<String>).subscribe(observer2)
It stops working! This implies that the value returned by map without the cast must be different. And there's only one possibility - Observable<String?>. $0 can't be implicitly convertible to other types.
The compiler sees that you are calling subscribe(Observable<String?>) and infers the return type of map to be Observable<String?> because only then can subscribe be called successfully.
But if you don't give the compiler enough context to figure out the type that map should return, like you did in the let observable2 = ... line, then the compiler thinks you want a Observable<String>.
Basically, the compiler is being smart.
In the delegate method of UIImagePickerViewController, I want to test for the media type.
This worked in Swift 3:
switch info[UIImagePickerControllerMediaType] as? NSString {
case .some(kUTTypeImage):
//...
}
But in Swift 4, it throws an error:
Expression pattern of type 'CFString' cannot match values of type 'NSString'
I changed it to this without error, but the type conversion doesn't look elegant:
switch info[UIImagePickerControllerMediaType] as? String {
case .some(kUTTypeImage as NSString as String):
//...
}
I tried to shorten it:
switch info[UIImagePickerControllerMediaType] as? NSString {
case .some(kUTTypeImage as NSString):
// ...
}
But this throws the error again:
Expression pattern of type 'CFString' cannot match values of type
'NSString'
a) Why does the shorter version throw an error but the longer version doesn't?
b) Is there a more elegant (shorter) way to write this?
You should cast the dictionary element to CFString.
let mediaType = info[UIImagePickerControllerMediaType] as! CFString
switch mediaType {
case kUTTypeImage:
//
default: break
}
Strangely enough, if I try to conditionally unwrap mediaType, Xcode will complain:
Conditional downcast to CoreFoundation type 'CFString' will always succeed
...and if I try to use simply as CFString, as it "will always succeed":
'Any?' is not convertible to 'CFString'; did you mean to use 'as!' to force downcast?
Strange, seems like the compiler is a little confused. Anyway, it's one of the few instances where force unwrapping is the most elegant solution.
So I'm following a tutorial on udemy and have this piece of code.
let itemsObject = UserDefaults.standard.object(forKey: "items")
var items:[String]
if let tempItems = itemsObject as? [String]
{
//
items = tempItems
items.append(textfield.text!)
}
I understand the itemsObject and items array but I'm wondering why I'm not able to bypass the tempItems constant altogether like...
var items:[String]
if items = itemsObject as? [String]
{
items.append(textfield.text!)
}
I don't understand the purpose of the let tempItems constant.
let itemsObject = UserDefaults.standard.object(forKey: "items")
itemsObject is now of type Any? So we need to do 2 things: cast it and check if it's nil
if let tempItems = itemsObject as? [String]
is a shortcut for:
let tempItems1 = itemsObject as? [String] // cast to [String]?
if tempItems1 != nil {
let tempItems2 = tempItems1! // we know its not nil, so force unwrap it to [String]
...
}
Hint:
[String]? is a string array that can be nil
[String] is a string array that can't be nil
Optional binding is value-binding pattern matching, which is not equivalent to assignment
Consider the following simplified example:
let foo: Any = 1
if let bar = foo as? Int { /* ... */ }
So what are we doing in the last row here? We attempt to conditionally convert an Any instance (foo) to the type Int. The result of such an conditional type conversion (as?) is an Optional:
foo as? Int // type: Optional<Int>
Moving on, we try to optionally bind the possibly successful conversion to an immutable concrete instance of type Int (the bar property). See optional binding in the language guide for details.
Now, the way we perform optional binding is simply syntactic sugar for value-binding pattern matching, in the context of (given successful pattern match) attempting to bind an associated value of an enum case to a property. The syntactic sugar of optional binding is available to the extremly common Optional<Wrapped> enum type, whereas for other enum type, we need to use the common value-binding pattern syntax
if case let .anEnumCase(bindAssociatedValueToMe) = someEnumInstance { /* ... */ }
// alternatively
if case .anEnumCase(let bindAssociatedValueToMe) = someEnumInstance { /* ... */ }
I.e., the following two (three) are equivalent
// make use of syntactic sugar available for `Optional` enum
if let bar = foo as? Int { /* .. */ }
// or use the explicit value-binding pattern available to any
// enum with a case that holds and associated value
if case .some(let bar) = foo as? Int { /* .. */ }
// (for the latter: alternatively)
if case let .some(bar) = foo as? Int { /* .. */ }
And, to add some extra confusion to the matter, we also have another special syntactic sugar available to the special enum Optional<Wrapped> (the .some(let bindWrappedToThis) case, specifically), such that also the following is equivalent to the two above:
// using the sugar '_?' instead of '.some(_)', here replacing
// '... let .some(bar)' with '... let bar?'
if case let bar? = foo as? Int { /* .. */ }
The key to understanding the above is to understand the following:
An attempted type conversion, using as?, will result in an Optional<Wrapped> instance with the Wrapped type being the target type of the type conversion. I.e., someInstance as? SomeType will result in an instance of Optional<SomeType>, that may be nil (.none), or may be wrapping a concrete instance of SomeType (.some(someInstanceOfSomeType)).
The type Optional<Wrapped> in Swift is, when leaving out all the special neatness/sugar, an enumeration,
Finally, that value-binding pattern matching is not the same as assignment.
1) is self-explanatory by its thorough description. To understand 2) & 3), we may study the following simple example, showing a custom "optional"-like enum with a case that holds a generic associated value, which, for a given instance of the enum, we try to conditionally bind to a property:
enum MyOptional<Wrapped> {
case none
case some(Wrapped)
}
// instantiate with a case with an associated value
let foo = MyOptional.some(1) // MyOptional<Int>.some(1)
// use value-binding pattern matching to conditionally
// access the associated value of the 'some(...)' case,
// given that 'foo' is '.some' (and not '.none')
if case .some(let bar) = foo { /* .. */ }
Notice the value-binding in the last line above, and compare to the non-sugared optional binding in the previous code snippet above.
Finally, note that you have means other than optional binding to access and perform operations on the (possible existing) wrapped value of an optional. E.g. using the map(_:) method of Optional, at the cost of clarity, we could condense your example (as well as make items immutable) in your top code snippet to the following:
let items = (UserDefaults.standard.object(forKey: "items") as? [String])
.map { $0 + [textfield.text!] } ?? []
Which, in a single expressions, make use of the following:
conditional type conversion of the UserDefaults value to type [String]
in case of successful type conversion, append textField.text! to the concrete [String] value and assign to items
in case of unsuccessful type conversion, use the nil coalescing operator ?? to supply and empty array, [], as a default value to items.
I'm trying to step through an [AnyObject] array from a completion block and cast the items as Strings so that I can then put them in a tableView. However, when I try to append the individual values of the array I get this error: Cast from '()' to unrelated type 'String' always fails. Here is the code:
client.historyForChannel(ids, start: nil, end: nil, withCompletion: { (result, status) -> Void in
if status == nil {
if result!.data.messages.count > 0 {
let historyMessages = result!.data.messages as? [String]
for value in historyMessages!{
self.messagesArray.append(value) as? String //error
}
}
}
})
If it helps, I'm using PubNub to create/store messages in my Swift app.
Thanks!
When you wrote
self.messagesArray.append(value) as? String
you probably meant
self.messagesArray.append(value as? String)
although
self.messagesArray.append(value)
should suffice because historyMessages is already of type [String]!.
The error is saying that you are casting the result of self.messagesArray.append(value) (which is Void because append does not return anything) to String, which does always fail.
As an aside, your code uses way more exclamation points than it should. You should be using guard-let to make sure your variables are non-nil.
I'm using Xcode 6 Beta 4. I have this weird situation where I cannot figure out how to appropriately test for optionals.
If I have an optional xyz, is the correct way to test:
if (xyz) // Do something
or
if (xyz != nil) // Do something
The documents say to do it the first way, but I've found that sometimes, the second way is required, and doesn't generate a compiler error, but other times, the second way generates a compiler error.
My specific example is using the GData XML parser bridged to swift:
let xml = GDataXMLDocument(
XMLString: responseBody,
options: 0,
error: &xmlError);
if (xmlError != nil)
Here, if I just did:
if xmlError
it would always return true. However, if I do:
if (xmlError != nil)
then it works (as how it works in Objective-C).
Is there something with the GData XML and the way it treats optionals that I am missing?
In Xcode Beta 5, they no longer let you do:
var xyz : NSString?
if xyz {
// Do something using `xyz`.
}
This produces an error:
does not conform to protocol 'BooleanType.Protocol'
You have to use one of these forms:
if xyz != nil {
// Do something using `xyz`.
}
if let xy = xyz {
// Do something using `xy`.
}
To add to the other answers, instead of assigning to a differently named variable inside of an if condition:
var a: Int? = 5
if let b = a {
// do something
}
you can reuse the same variable name like this:
var a: Int? = 5
if let a = a {
// do something
}
This might help you avoid running out of creative variable names...
This takes advantage of variable shadowing that is supported in Swift.
Swift 3.0, 4.0
There are mainly two ways of checking optional for nil. Here are examples with comparison between them
1. if let
if let is the most basic way to check optional for nil. Other conditions can be appended to this nil check, separated by comma. The variable must not be nil to move for the next condition. If only nil check is required, remove extra conditions in the following code.
Other than that, if x is not nil, the if closure will be executed and x_val will be available inside. Otherwise the else closure is triggered.
if let x_val = x, x_val > 5 {
//x_val available on this scope
} else {
}
2. guard let
guard let can do similar things. It's main purpose is to make it logically more reasonable. It's like saying Make sure the variable is not nil, otherwise stop the function. guard let can also do extra condition checking as if let.
The differences are that the unwrapped value will be available on same scope as guard let, as shown in the comment below. This also leads to the point that in else closure, the program has to exit the current scope, by return, break, etc.
guard let x_val = x, x_val > 5 else {
return
}
//x_val available on this scope
One of the most direct ways to use optionals is the following:
Assuming xyz is of optional type, like Int? for example.
if let possXYZ = xyz {
// do something with possXYZ (the unwrapped value of xyz)
} else {
// do something now that we know xyz is .None
}
This way you can both test if xyz contains a value and if so, immediately work with that value.
With regards to your compiler error, the type UInt8 is not optional (note no '?') and therefore cannot be converted to nil. Make sure the variable you're working with is an optional before you treat it like one.
From swift programming guide
If Statements and Forced Unwrapping
You can use an if statement to find out whether an optional contains a
value. If an optional does have a value, it evaluates to true; if it
has no value at all, it evaluates to false.
So the best way to do this is
// swift > 3
if xyz != nil {}
and if you are using the xyz in if statement.Than you can unwrap xyz in if statement in constant variable .So you do not need to unwrap every place in if statement where xyz is used.
if let yourConstant = xyz {
//use youtConstant you do not need to unwrap `xyz`
}
This convention is suggested by apple and it will be followed by devlopers.
Although you must still either explicitly compare an optional with nil or use optional binding to additionally extract its value (i.e. optionals are not implicitly converted into Boolean values), it's worth noting that Swift 2 has added the guard statement to help avoid the pyramid of doom when working with multiple optional values.
In other words, your options now include explicitly checking for nil:
if xyz != nil {
// Do something with xyz
}
Optional binding:
if let xyz = xyz {
// Do something with xyz
// (Note that we can reuse the same variable name)
}
And guard statements:
guard let xyz = xyz else {
// Handle failure and then exit this code block
// e.g. by calling return, break, continue, or throw
return
}
// Do something with xyz, which is now guaranteed to be non-nil
Note how ordinary optional binding can lead to greater indentation when there is more than one optional value:
if let abc = abc {
if let xyz = xyz {
// Do something with abc and xyz
}
}
You can avoid this nesting with guard statements:
guard let abc = abc else {
// Handle failure and then exit this code block
return
}
guard let xyz = xyz else {
// Handle failure and then exit this code block
return
}
// Do something with abc and xyz
Swift 5 Protocol Extension
Here is an approach using protocol extension so that you can easily inline an optional nil check:
import Foundation
public extension Optional {
var isNil: Bool {
guard case Optional.none = self else {
return false
}
return true
}
var isSome: Bool {
return !self.isNil
}
}
Usage
var myValue: String?
if myValue.isNil {
// do something
}
if myValue.isSome {
// do something
}
One option that hasn't specifically been covered is using Swift's ignored value syntax:
if let _ = xyz {
// something that should only happen if xyz is not nil
}
I like this since checking for nil feels out of place in a modern language like Swift. I think the reason it feels out of place is that nil is basically a sentinel value. We've done away with sentinels pretty much everywhere else in modern programming so nil feels like it should go too.
Instead of if, ternary operator might come handy when you want to get a value based on whether something is nil:
func f(x: String?) -> String {
return x == nil ? "empty" : "non-empty"
}
Another approach besides using if or guard statements to do the optional binding is to extend Optional with:
extension Optional {
func ifValue(_ valueHandler: (Wrapped) -> Void) {
switch self {
case .some(let wrapped): valueHandler(wrapped)
default: break
}
}
}
ifValue receives a closure and calls it with the value as an argument when the optional is not nil. It is used this way:
var helloString: String? = "Hello, World!"
helloString.ifValue {
print($0) // prints "Hello, World!"
}
helloString = nil
helloString.ifValue {
print($0) // This code never runs
}
You should probably use an if or guard however as those are the most conventional (thus familiar) approaches used by Swift programmers.
Optional
Also you can use Nil-Coalescing Operator
The nil-coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.
let value = optionalValue ?? defaultValue
If optionalValue is nil, it automatically assigns value to defaultValue
Now you can do in swift the following thing which allows you to regain a little bit of the objective-c if nil else
if textfieldDate.text?.isEmpty ?? true {
}
var xyz : NSDictionary?
// case 1:
xyz = ["1":"one"]
// case 2: (empty dictionary)
xyz = NSDictionary()
// case 3: do nothing
if xyz { NSLog("xyz is not nil.") }
else { NSLog("xyz is nil.") }
This test worked as expected in all cases.
BTW, you do not need the brackets ().
If you have conditional and would like to unwrap and compare, how about taking advantage of the short-circuit evaluation of compound boolean expression as in
if xyz != nil && xyz! == "some non-nil value" {
}
Granted, this is not as readable as some of the other suggested posts, but gets the job done and somewhat succinct than the other suggested solutions.
If someone is also try to find to work with dictionaries and try to work with Optional(nil).
let example : [Int:Double?] = [2: 0.5]
let test = example[0]
You will end up with the type Double??.
To continue on your code, just use coalescing to get around it.
let example : [Int:Double?] = [2: 0.5]
let test = example[0] ?? nil
Now you just have Double?
This is totally logical, but I searched the wrong thing, maybe it helps someone else.
Since Swift 5.7:
if let xyz {
// Do something using `xyz` (`xyz` is not optional here)
} else {
// `xyz` was nil
}