Force passing one parameter to a function with several optional parameters

Consider the following function:

func myFunction(completion: (data: Data?, error: Error?) -> ()) { }

My current requirement would be to completion:accept only the value dataor error, but not both. You need to be zero.

It's simple enough to just leave them as optional and then expand and check their values ​​later, but I think it would be better if the compiler could tell the developer that they cannot install both.

Looking at it from the other side, knowing that one of them will always be installed on someValue, will be even more useful.

In this way, you can guarantee that you will receive erroror data, and you will not have to worry about cases where they are both nil.

Is there any way to do this?

+4
source share
3 answers

You want something like Swift that it cannot do. In particular, as far as I know, Swift has no union types. If you want something like this, you can look at functional or functionally-inspired languages.


Interlude: if both types are structures or classes (i.e. not protocols), you can use this template:

protocol DataOrError {}
extension Data: DataOrError {}
extension ErrorClass: DataOrError {}

Assuming there are no other protocol developers, now you have something that fits the type of union. You can also switch between types:

switch doe {
    case let e as Error: print("Error \(e)")
    case let d as Data: print("Data \(d)")
    default: assert(false, "thou shalt not implement my dummy!")
}

In any case, we can fake it using enumerations with related values, which can (with restrictions) be used as a type of association of the poor.

Define the enumeration as follows:

enum DataOrError {
    case Data(Data)
    case Error(Error)
}

DataOrError, , , () Data Error DataOrError? .

Caller-site, - :

extension String: Error {} // low-effort errors, don't judge me!
func myFunction(completion: (DataOrError) -> ()) {
    completion(.Data(Data(bytes: [1,2,3,4,5])))
    completion(.Error("this won't work!"))
}

-:

var myCompletion = { (doe: DataOrError) in
    switch doe {
        case .Data(let d): print("Data \(d)")
        case .Error(let e): print("Error \(e)")
    }
}

myFunction(completion: myCompletion)
// > Data 5 bytes
// > Error this won't work!

. , , , OR Error. , .

struct TalkativeOptional<T> {
    private(set) var rawValue: T?
    private(set) var error: Error?

    init(value: T) {
        self.rawValue = value
    }

    init(error: Error) {
        self.error = error
    }
}

, nil. ; , , .

-:

func convert(_ number: String) -> TalkativeOptional<Int> {
    if let int = Int(number) {
        return TalkativeOptional(value: int)
    } else {
        return TalkativeOptional(error: "'\(number)' not a valid integer!")
    }
}

:

var a = convert("dsa2e2")

if let val = a.rawValue {
    print("a + 1 = \(val + 1)")
} else { // we know by design that error is set!
    print("ERROR: \(a.error!)")
}
+1

enum Result<T> {
    case Success(T)
    case Error(String, Int)
}

Swift 2?, .

+8

As an alternative to listing the result, you should study the structure of promises. There are several, but I like PromiseKit .

func myFunction() -> Promise<Data> { }

Here is how it is used.

obj.myFunction().then { data -> Void in
    // Use data, it guaranteed to be there.
} .catch { error in
    // Handle the error
}
+2
source

Source: https://habr.com/ru/post/1666380/


All Articles