If let statement with multiple returns from function in swift - swift

Say I have a function
func function1() -> (result:Bool, data:String){
return(false, "false")
}
and I want to use the return value of the Bool in an if let statement so,
if let value = function1 {
//code
} else {
//code
}
How would I get this to work? I can't seem to see it in the swift docs, as it just aludes to it being a returned tuple, which you can access with dot notation, but only if you set the return to be a tuple first - so for example this would work
var value = function1()
if value.result {
////code
} else {
//code
}
works, but I'd like to fit it all into the actual if else if possible. Any ideas?

I would suggest the use of a computed property.
func someFunc() {
if let value = computedProperty where value.result {
print(value.data)
} else {
// Do something else
}
}
var computedProperty: (result: Bool, data: String)? {
return (true, "FooBar")
}
or using a function
func someFunc() {
if let value = anotherFunc() where value.result {
print(value.data)
} else {
// Do something else
}
}
func anotherFunc() -> (result: Bool, data: String)? {
return (true, "FooBar")
}
Hope this helps :)
Swift 2.1 Properties

You could pattern match with a switch statement.
switch function1() {
case (true, let string):
print(string)
default:
// else stuff here
}

With this type on if you need optional return type from your function like this:
func function1() -> (result:Bool, data:String)? {
return(false, "false")
}
if let value = function1() {
//code
} else {
//code
}

Related

Observe generic values with Combine

Take this case of a type constrained class Parameter, wrapping a value of given type.
Parameter conforms to the AnyParameter so it can be passed anywhere in the app without knowing the type. Parameters can be displayed in value cells AnyValueCell
How would you do to observe the change without having to know the underlying value type? It would be nice to avoid the code repetition in the value cell updateObserver function
Could AnyPublisher can be used here and how?
import UIKit
import Combine
print("Hello Playground")
protocol AnyParameter {
var anyValue: Any { get }
func set(value: Any)
}
protocol ParameterProtocol: AnyParameter {
associatedtype ValueType
var value: ValueType { get }
func set(value: ValueType)
}
public class Parameter<T>: ParameterProtocol {
typealias ValueType = T
#Published var value: T
var anyValue: Any { value }
init(value: T) {
self.value = value
}
func set(value: Any) {
guard let value = value as? T else { return }
set(value: value)
}
func set(value: T) {
self.value = value
}
}
public class AnyValueCell {
var parameter: AnyParameter {
didSet {
updateObserver()
}
}
var observer: AnyCancellable?
init(parameter: AnyParameter) {
self.parameter = parameter
updateObserver()
}
func updateObserver() {
observer?.cancel()
// This is the point of the question - How to make this generic?
// ---->
if let p = parameter as? Parameter<Int> {
observer = p.$value.sink() { value in
print("Update Cell -> \(value)")
}
return
}
if let p = parameter as? Parameter<Double> {
observer = p.$value.sink() { value in
print("Update Cell -> \(value)")
}
return
}
if let p = parameter as? Parameter<Bool> {
observer = p.$value.sink() { value in
print("Update Cell -> \(value)")
}
return
}
// <----
print("Wrong param type")
}
}
let intParam = Parameter<Int>(value: 42)
let doubleParam = Parameter<Double>(value: 3.14)
let boolParam = Parameter<Bool>(value: false)
var params: [AnyParameter] = [intParam, doubleParam, boolParam]
print ("--> Init Cells")
let cells: [AnyValueCell] = params.map { AnyValueCell(parameter: $0) }
print ("--> Change values")
intParam.set(value: 21)
doubleParam.set(value: 1.618)
boolParam.set(value: true)
Result, as expected:
Hello Playground
--> Init Cells
Update Cell -> 42
Update Cell -> 3.14
Update Cell -> false
--> Change values
Update Cell -> 21
Update Cell -> 1.618
Update Cell -> true
Add an anyValuePublisher property. You can (and maybe should) add it to AnyParameter, or you can define it in a separate protocol like this:
protocol AnyParameterPublishing: AnyParameter {
var anyValuePublisher: AnyPublisher<Any, Never> { get }
}
extension Parameter: AnyParameterPublishing {
var anyValuePublisher: AnyPublisher<Any, Never> {
return $value.map { $0 as Any }.eraseToAnyPublisher()
}
}
Then you can use it like this:
class AnyValueCell {
// ...
func updateObserver() {
guard let publishing = (parameter as? AnyParameterPublishing) else {
print("Wrong param type")
return
}
observer = publishing.anyValuePublisher
.sink { print("Update Cell -> \($0)") }
}
}

Why it cannot convert return expression of type?

I try to return constant result in test2 method but the compiler throws error. Why?
public enum TestResult<Value> {
case success(Value)
case failure(Error)
}
struct TestModel {
}
class Test {
func test1() -> TestResult<Any> {
let obj = TestModel()
return TestResult.success(obj)
}
func test2() -> TestResult<Any> {
let obj = TestModel()
let result = TestResult.success(obj)
return result
}
}
You need to convert TestModel to Any:
func test2() -> TestResult<Any> {
let obj: Any = TestModel()
let result = TestResult.success(obj)
return result
}

Swift: check return type of generic function

I know how to check type of named variable - if var is T. But can't find how to check supposed return type for generic function.
Live example, dealing with SwiftyJSON, ugly solution:
func getValue<T>(key: String) -> T? {
let result: T // so ugly approach...
if result is Bool {
return json[key].bool as? T
}
if result is Int {
return json[key].int as? T
}
if result is String {
return json[key].string as? T
}
fatalError("unsupported type \(result.dynamicType)")
}
Looking for more elegant approach.
This would work:
func getValue<T>(key: String) -> T? {
if T.self is Bool.Type {
return json[key].bool as? T
}
if T.self is Int.Type {
return json[key].int as? T
}
if T.self is String.Type {
return json[key].string as? T
}
fatalError("unsupported type \(T.self)")
}
But I'm not sure it's any more elegant than yours.
Overloading is something worth trying:
func getValue(key: String) -> Bool? {
return json[key].bool
}
func getValue(key: String) -> Int? {
return json[key].int
}
func getValue(key: String) -> String? {
return json[key].string
}
With this, you can find errors in compile time, rather than getting fatal errors in runtime.

I got many errors in my function swift 2

I have one project that write with swift 1 but now I run with Xcode 7.2 (swift 2)
but I don't know why I got many errors.
I can fix one problem in my previous question but I have big problem and I so tired.
this is my code :
public func generate() -> AnyGenerator <(String, JSON)> {
switch self.type {
case .Array:
let array_ = object as! [AnyObject]
var generate_ = array_.generate()
var index_: Int = 0
return AnyGenerator<(String, JSON)> {
if let element_: AnyObject = generate_.next() {
return ("\(index_++)", JSON(element_))
} else {
return nil
}
}
case .Dictionary:
let dictionary_ = object as! [String : AnyObject]
var generate_ = dictionary_.generate()
return AnyGenerator<(String, JSON)> {
if let (key_, value_) = generate_.next() {
return (key_, JSON(value_))
} else {
return nil
}
}
default:
return AnyGenerator<(String, JSON)> {
return nil
}
}
}
please guide me about these errors!!!
In Swift 2, the global function
public func anyGenerator<Element>(body: () -> Element?) -> AnyGenerator<Element>
is used to create a (type-erased) generator from a given
closure:
public func generate() -> AnyGenerator <(String, JSON)> {
// ...
return anyGenerator {
if let element_: AnyObject = generate_.next() {
return ("\(index_++)", JSON(element_))
} else {
return nil
}
}
}

Casting to generic optional in Swift

I'm fiddling around with generics in Swift and hit something I can't figure out: If I cast a value into the type of a generic parameter, the cast is not performed. If I try the same with static types, it works.
class SomeClass<T> {
init?() {
if let _ = 4 as? T {
println("should work")
} else {
return nil
}
}
}
if let _ = SomeClass<Int?>() {
println("not called")
}
if let _ = 4 as? Int? {
println("works")
}
Can anybody explain this behavior? Shouldn't be both cases equivalent?
Update
The above example is simplified to the max. The following example illustrates the need for a cast a little better
class SomeClass<T> {
init?(v: [String: AnyObject]) {
if let _ = v["k"] as? T? {
print("should work")
} else {
print("does not")
return nil
}
}
}
if let _ = SomeClass<Int?>(v: ["k": 4]) {
print("not called")
}
if let _ = SomeClass<Int>(v: ["k": 4]) {
print("called")
}
2nd Update
After #matt made me learn about AnyObject and Any and #Darko pointed out in his comments how dictionaries make my example too complicated, here's my next refinement
class SomeClass<T> {
private var value: T!
init?<U>(param: U) {
if let casted = param as? T {
value = casted
} else {
return nil
}
}
}
if let _ = SomeClass<Int?>(param: Int(4)) {
println("not called")
}
if let _ = SomeClass<Int>(param: Int(4)) {
println("called")
}
if let _ = Int(4) as? Int? {
println("works")
}
if let _ = (Int(4) as Any) as? Int? {
println("Cannot downcast from Any to a more optional type 'Int?'")
}
I tried using init?(param: Any) before, but that yields the same problem illustrated in the last if which is discussed elsewhere.
So all it comes down to: Has anyone really been far as to ever cast anything to a generic optional type? In any case? I'm happy to accept any working example.
This is really not about generics at all; it's about AnyObject (and how casting works). Consider:
let d = ["k":1]
let ok = d["k"] is Int?
print (ok) // true
// but:
let d2 = d as [String:AnyObject]
let ok2 = d2["k"] is Int?
print (ok2) // false, though "is Int" succeeds
Since your initializer casts the dictionary up to [String:AnyObject] you are in this same boat.
This works as expected now - in Playgrounds as well as in projects. Retested in Swift 5.4:
class SomeClass<T> {
private var value: T
init?<U>(param: U) {
if let casted = param as? T {
value = casted
} else {
return nil
}
}
}
if let _ = SomeClass<Int?>(param: Int(4)) {
print("called")
}
if let _ = SomeClass<Int>(param: Int(4)) {
print("called")
}
which prints called two times as expected.
As far I can see the goal of your updated code is to find out if the passed parameter (the value of the dict) is of type T. So you are mis-using the as? cast to check the type. What you actually want is the "is" operator.
class SomeClass<T> {
init?(v: [String: AnyObject]) {
if v["k"] is T {
print("called if AnyObject is of type T")
} else {
print("called if AnyObject is not of type T")
return nil
}
}
}
if let _ = SomeClass<Int>(v: ["k": 4]) {
print("called")
}
if let _ = SomeClass<Int?>(v: ["k": 4]) {
print("not called")
}