iOS RxSwift - how to use Amb Operator? - ios

I'm looking for example of Amb() operator for RxSwift.
I have two observables - started and stopped and I want to create code which would execute if one of them emits, while ignoring emissions from the other one.
let started = viewModel.networkAPI.filter { result in
switch result {
case .success:
return true
case .failure:
return false
}
}
let stopped = viewModel.networkAPI.filter { result in
switch result {
case .success:
return true
case .failure:
return false
}
}
I tried:
Observable<Bool>.amb([started, stopped])
//error - Ambiguous reference to member 'amb'
//Found this candidate (RxSwift.ObservableType)
//Found this candidate (RxSwift.ObservableType)
and:
started.amb(stopped)
//error - Generic parameter 'O2' could not be inferred
How do I properly use RxSwift AMB operator to pick one of two observables to emit a value first?

The problem isn't in the code you posted. My guess is that your Result type is generic and the started and stopped observables are filtering off of different types of Result. In essence, started and stopped have different types and therefore, amb won't work with them.
You can test this by doing let foo = [started, stopped] and then testing the type of foo. You will probably get the error:
Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
//convert both to the same emission type,
//then apply .amb after one of them
let stoppedBoolType = stopped.map { _ -> Bool in
return false
}
started.map{ _ -> Bool in
return true
}.amb(stoppedBoolType)

Related

confused with the functionality of `return` in swift

I am confused with return in Swift. I understand it's used to return the value in a function, if used like this:
func double(value: int) -> Int {
return value * 2
}
But I often just see return being used, like in a guard statement in an optional binding like this:
guard let value = value else (
print ("nothing")
return
}
So what is the purpose of having just return in the guard statement like this? Actually, I often see this not only in guard statements when unwrapping optional values. I always find this problem when writing code, when I want to use an optional string from a dictionary.
let info = ["name": "sarah", "hometown": "sydney"]
class UserInfo {
func getTheName() -> String {
guard let name = info["name"] else { return }
return name
}
}
// Compile time error: "Non-void function should return a value"
I get this error even though I have written return name. Xcode still complains that I have not returned a value. Is it because of the return in the guard statement?
So, could you please tell me the purpose of return in Swift? It is confusing for me.
return without any argument returns Void. This form of the return statement can only be used with a function that returns Void.
Once the return statement executes, the function exits and no more code in your function executes. Since you have a return in the guard statement, the second return name won't be executed (it couldn't anyway since it wouldn't have a name to return), which is why you get a compiler error; the compiler looks at all of the paths that your function could take to return something and ensures that all of those paths return what the function signature says it will.
The function in your question states that it returns a String, so you can't simply say return in the guard statement as that returns Void, violating the contract expressed by your function signature.
You could return a default value that isn't Void:
func getTheName () -> String {
guard let name = info["name"] else {
return ""
}
return name
}
This could be written much more succinctly using the nil-coalescing operator; return info["name"] ?? ""
You can also use return in a function that returns Void (or has no explicit return type, in which case it is implicitly understood to return Void)
So you could have a function like:
func maybePrint(theMessage: String?) -> Void {
guard let msg = theMessage else {
return
}
print(msg)
}
You're on the right track.
In your guard statement inside getTheName(), the 'return' keyword will try to exit the function itself if the guard fails. But the function requires you to return a String and as such you get the compiler error.
Here is a portion of another SO answer to a similar question:
guard forces you to exit the scope using a control transfer statement.
There are 4 available to you:
return and throw both exit the function/method continue can be used
within loops (while/for/repeat-while) break can be used in loops
(while/for/repeat-while) to exit the immediate scope. Specifying a
label to break to will allow you to exit multiple scopes at once (e.g.
breaking out of nested loop structure). When using a label, break can
also be used in if scopes. Additionally, you may exit the scope by
calling a function that returns Never, such as fatalError.
Stack Overflow: If the Swift 'guard' statement must exit scope, what is the definition of scope?

Swift generic array to specific type

I am writing a function that needs to return an array of items. In the function there will be some logic that will determine the type of the items I want to return from the function. I started something like this:
func testFunc<T>(option:Int) -> [T] {
var result:[T] = []
switch option {
case 1:
let sampleString:String = "hello"
result.append(sampleString)
case 2:
let sampleInt:Int = 23
result.append(sampleInt)
default:
return result
}
return result
}
This gives me the following errors:
"cannot invoke append with an argument list of type '(String)',
and
"cannot invoke append with an argument list of type '(Int)'
It makes sense, but, I am trying to figure out how to solve my problem. When I call the function I don't know what type will be in the array returned, but the function will know how to determine the type before it start appending items to the array.
How can I accomplish this in Swift?
Swift does not support variables with multiple types. Using a generic T makes the function generic so the return type can be chosen by the caller.
You should rethink your design.
If you really want to return multiple result types based on a parameter, there are a few workarounds:
1: Use an Any array.
var result:[Any] = []
2: Use an enum with associated values
enum TestFuncResult {
case string(String)
case int(Int)
}
func testFunc(option: Int) -> [TestFuncResult] {
var result:[TestFuncResult] = []
switch option {
case 1:
let sampleString:String = "hello"
result.append(.string(sampleString))
case 2:
let sampleInt:Int = 23
result.append(.int(sampleInt))
default:
return result
}
return result
}

Incorrect try/catch behavior in Xcode 7 beta 3's Swift 2?

I have a problem with some Swift 2 code, compiled using Xcode 7 beta 3.
I have a class (see below) that has an initializer that takes a function, f, which can throw. If f does not throw, then a member variable (self.result) should be set to an instance of an enum that wraps the value that f returns. If f does throw, then self.result should be set to an instance of the enum that indicates the value is absent. At the end of the initializer, self.result should not be nil. I have checked the case where the f does not throw, and the behavior is correct. However, in the case that f does throw, self.result is nil at the end of the initializer (the assert is triggered). If I single-step in the debugger, I see that self.result seems to instantaneously be set and then flashes back to being nil.
(Note: You might suggest I represent the absence of a result as nil, rather than wrapping in the enum. However, I need to model the scenario in which the result of f has not been computed yet, has been computed successfully, or an attemopt has been made to compute the result but it has failed. Hence the enum.)
Have I misunderstood how Swift 2's error handling works? Or, is the compiler/debugger etc. behaving incorrectly?
Thanks in advance.
internal enum Result<T> {
case Value(T)
case None
}
public final class MyClass<T> {
internal var result: Result<T>? = nil
private init(f: () throws -> T) {
let queueName = “some.string”
let queue = dispatch_queue_create(queueName, DISPATCH_QUEUE_CONCURRENT)
dispatch_async(queue) {
do {
let value = try f()
self.result = .Value(value)
}
catch {
self.result = .None
}
assert(self.result != nil, "Result must have value before block returns.")
}
}
}
In
self.result = .None
the left-hand side is an optional Result, therefore .None on
the right-hand side is inferred as Optional.None, and the statement
is equivalent to
self.result = nil
What you probably meant is
self.result = Result.None
and then the assertion does not fail anymore. Alternatively,
use a different enumeration value, e.g. case NoValue in your custom
type.

.rangeOfString() with switch in Swift

I want to check if my input has a rangeOfString must with a lot of different strings to check.
basically this if statement but using a switch to check a big list of different strings
if (input.rangeOfString("lol") != nil) {
println("yes")
}
I've tried doing this but it isn't working.
switch input {
case rangeOfString("lol"):
println("lol")
case rangeOfString("dw"):
println("dw")
default:
println("Default")
}
You misunderstood the idea of a switch statement. It checks the value of one expression against multiple possible values, and optionally lets you access associated values of an enumeration. It does not let you pass a single value to multiple expressions listed as switch cases*, and pick whichever provides the first match.
You need to make a chain of if-elses to make your code work:
if (input.rangeOfString("lol") != nil) {
println("lol")
} else if (input.rangeOfString("dw") != nil) {
println("dw")
} else {
println("Default")
}
I would like to check if a word is being used in the input and I have like 50 words I'd like to check.
Then the switch is not a good alternative either. Make a container of the words you wish to search, and use filter to find all matches:
let input = "hahalolblah"
let words = ["lol", "blah", "hello"];
let matches = words.filter {input.rangeOfString($0) != nil}
println(matches) // Produces [lol, blah]
* It turns out that switch lets you pass one expression to an override of the ~= operator, along with values coming from cases of the switch. See this answer for details.
While the others answers are probably right about if being a better way to go, you can do something like this via heroic abuse of the ~= operator:
import Foundation
struct Substring {
let substr: String
init(_ substr: String) { self.substr = substr }
}
func ~=(substr: Substring, str: String) -> Bool {
return str.rangeOfString(substr.substr) != nil
}
let input = "contains wat"
switch input {
case Substring("lol"), Substring("wat"):
println("huh?") // this one is picked
case Substring("dw"):
println("dw")
// you can also mix and match
case "Explicit full string":
println("matches whole string")
default:
println("Default")
}
Switch statements in Swift can be extended via overloading of the ~= operator. So for example, the reason this works:
switch 2.5 {
case 0...5: println("between nought and five")
default: println("not in range")
}
is because there is a definition of the ~= operator that matches any kind of Comparable type to an interval:
func ~=<I : IntervalType>(pattern: I, value: I.Bound) -> Bool
Here, by creating a new type of Substring, I've created a way to match strings to substrings.
In theory you could skip the creation of the Substring type and do the following:
func ~=(substr: String, str: String) -> Bool {
return str.rangeOfString(str) != nil
}
let input = "contains lol"
switch input {
case "lol":
println("lol")
case "dw":
println("dw")
default:
println("Default")
}
This would work, but would be a bad idea because now you'll have changed the way switches on strings work universally so that partial matches are always true, which could lead to some unpleasant and unexpected behaviour elsewhere in your code.
You can also do something like:
switch true {
case myString.rangeOfString("lol") != nil:
print("lol");
case myString.rangeOfString("dw") != nil:
print("dw");
default:
print("Default")
}

Apple Swift: Type Casting Generics

I'm writing some Swift code where I have an array containing a generic type:
let _data: Array<T> = T[]()
Later in my code I need to determine the type stored in the array. I tried using the type casting technique described in the documentation (although it was not used for generics).
switch self._data {
case let doubleData as Array<Double>:
// Do something with doubleData
case let floatData as Array<Float>:
// Do something with floatData
default:
return nil // If the data type is unknown return nil
}
The above switch statement results in the following error upon compilation:
While emitting IR SIL function #_TFC19Adder_Example___Mac6Matrix9transposeUS_7Element__fGS0_Q__FT_GSqGS0_Q___
for 'transpose' at /code.viperscience/Adder/src/Adder
Library/Matrix.swift:45:3 :0: error: unable to execute
command: Segmentation fault: 11 :0: error: swift frontend
command failed due to signal (use -v to see invocation) Command
/Applications/Xcode6-Beta2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift
failed with exit code 254
Anybody know how I can cast my generic data to its actual type in order to take specific action?
In swift, as operator is something like dynamic_cast in C++, which can be used to down cast an object.
Say you have an object a of type A, and you can write let a as B only when type B is identical to type A, or B is a sub-class of A.
In your case, apparently Array<T> cannot always be down cast to Array<Double> or Array<Float>, so compiler reports errors.
A simple fix is to convert to AnyObject first, and then downcast to Array<Double> or Array<Float>:
let anyData: AnyObject = self._data;
switch anyData {
case let doubleData as? Array<Double>: // use as? operator, instead of as,
// to avoid runtime exception
// Do something with doubleData
case let floatData as? Array<Float>:
// Do something with floatData
default:
return nil // If the data type is unknown return nil
Suppose you have an array of buttons:
let views: [NSView] = [NSButton(), NSButton(), NSButton()]
You can use these casts:
let viewsAreButtons = views is [NSButton] // returns true
let buttonsForSure = views as! [NSButton] // crashes if you are wrong
let buttonsMaybe = views as? [NSButton] // optionally set
If you try to use as in a switch case like below, it will not work. The compiler (Swift 1.2 Xcode 6.3b1) says: "Downcast pattern of type [NSButton] cannot be used."
switch views {
case let buttons as [NSButton]:
println("Buttons")
default:
println("something else")
}
Call it a limitation. File a radar with your use case. The Swift team really seams to be listening for feedback. If you really want to get it to work, you can define your own pattern matching operator. In this case it would be something like this:
struct ButtonArray { }
let isButtonArray = ButtonArray()
func ~=(pattern: ButtonArray, value: [NSView]) -> Bool {
return value is [NSButton]
}
Then this works:
switch views {
case isButtonArray:
println("Buttons") // This gets printed.
default:
println("something else")
}
Try it in a Playground. Hope it helps!

Resources