Swift generic array to specific type - ios

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
}

Related

How to use swift's firstIndex to generate a new subarray?

For example, I have array let candidates=["1","0","a","b","c"] , and I want to return ["a","b","c"]
Here's the code:
if let head = candidates.firstIndex(of: "0") {
return candidates[head..<candidates.count]
}
But got error: No 'subscript' candidates produce the expected contextual result type '[String]'
Does your function expect to return type [String]?
candidates[head..<candidates.count] will return type ArraySlice so if you want to convert that to an array, you might need to do
return Array(candidates[head..<candidates.count])
One more small addition for completeness, since you want to return ["a","b","c"], you will need to start from the index after "0" so I would do:
return Array(candidates[head+1..<candidates.count])

Enum: Count and list all cases (from outside!)

I've got this enum (Xcode 10/Swift 5) in its own file ("MyEnum.swift"):
enum MyEnum: Int, CaseIterable {
case A = 0
case B = 1
case C = 2
case D = 3
case E = 4
//Each case needs its own number and description!
var description: String {
switch self {
case .A:
return "abc"
case .B:
return "bcd"
case .C:
return "cde"
case .D:
return "def"
case .E:
return "efg"
}
}
}
... and want to add the descriptions to a PickerView. I know how to set up the functions for the view but I'm now stuck on counting the enum cases and adding the descriptions.
According to the documentation and different questions, adding CaseIterable is supposed to make it possible to call
MyEnum.allCases.count
... but I can only access allCases from within the enum file. From outside (so from my ViewController class) I can only call MyEnum.AllCases, which hasn't got count and, to be honest, I'm not even sure what AllCases returns exactly (it's not a normal array you can use count on).
Adding this code (source) to the enum file at least makes it possible to count the cases:
static let count: Int = {
var max: Int = 0
while MyEnum(rawValue: max) != .none { max += 1 }
return max
}()
...but isn't there supposed to be an easier way to do this with Swift 4.2+?
How do I get a list of the case descriptions to fill my PickerView with (so "abc", "bcd",... - preferably without hardcoding it)?
I tried in 1 file:
enum TestEnum: CaseIterable {
case test1
case test2
}
in another file of another class I wrote:
let count = TestEnum.allCases.count
And it works, but I noticed that when I was typing "allCases" wasn't shown
I manually needed to write it
allCases is declared public, so you should be able to access it from another file:
/// A collection of all values of this type.
public static var allCases: Self.AllCases { get }
like this:
let descriptions = MyEnum.allCases.map { $0.description }
Try cleaning the project and rebuild.
Also, if something is not in Xcode's autocomplete list, it doesn't mean that you can't access that thing. Xcode's complete list has a ton of bugs according to my own experience.
For the sake of completeness and in case allCases doesn't work at all (even after a restart) add this to the enum file:
static let allValues = [MyEnum.A, MyEnum.B, MyEnum.C, MyEnum.D, MyEnum.E]
It's hardcoded but at least it gives you access to everything:
count: MyEnum.allValues.count
description (e.g. "abc"): MyEnum.allValues[0].description
rawValue (e.g. 0):MyEnum.allValues[0].rawValue
The same issue happened to me. What ended up working is restarting xcode.

iOS RxSwift - how to use Amb Operator?

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)

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?

Ambiguous error when attempting to filter values using enum

I have a filter that I am trying to use to compare one value to another. Here is the enum that I am using:
enum SomeEnum: String {
case first = "Hey"
case second = "There"
case third = "Peace"
static let values = [first, second, third]
func pickOne() -> String {
switch self {
case .first:
return "value 1"
case .second:
return "value 2"
case .third:
return "value 3"
}
}
Here is where I am attempting to filter and find matching values:
array.append(SomeEnum.values.filter({$0.rawValue == anotherArray["id"] as! String}))
I end up getting an ambiguous error:
Cannot convert value of type '[SomeEnum]' to expected argument type 'String'
Any ideas?
The problem is, that SomeEnum.values return type is [SomeEnum] and not String.
And the append function expects the parameter to be String, instead it is [SomeEnum].
This is, what you need to change:
Change append to appendContentsOf, since filter function returns an array, and not a single value
Change the [SomeEnum] to [String] since you are adding it to a [String] array, like this.
This is the fix:
array.appendContentsOf(SomeEnum.values.filter({ $0.rawValue == "SomeString" }).map({ $0.PickOne() }))

Resources