强制将单个参数传递给多可选参数函数

时间:2017-01-10 14:14:25

标签: swift

考虑以下功能:

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

我目前的要求是completion:只接受data值或error值,但不能同时接受两者。一个必须为零。

很容易将它们保持为可选,然后展开并稍后检查它们的值,但我认为如果编译器能够通知开发人员他们不能同时设置它们会更好

从另一个方面来看,知道其中一个将总是设置为someValue会更有用。

通过这种方式,您可以保证获得errordata,并且永远不必担心处理nil的情况。< / p>

目前有办法做到这一点吗?

3 个答案:

答案 0 :(得分:8)

我建议你使用像这样的枚举

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

请查看原始答案更详细的Best way to handle errors from async closures in Swift 2?

答案 1 :(得分:2)

作为结果枚举的替代方法,您应该研究一个promises框架。其中有很多,但我喜欢PromiseKit

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

以下是它的使用方法。

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

答案 2 :(得分:1)

你想要一些不能做的Swift。特别是,据我所知,Swift没有联合类型。如果你想要这样的东西,你可能想看看功能性或功能性的语言。

Interlude:如果两种类型都是结构或类(即非协议),则可以使用此模式:

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

假设协议中没有其他实现者,您现在有一些接近联合类型的东西。您也可以切换大小写类型:

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!")
}

无论如何,我们可以使用带有关联值的枚举来伪造它,这可以(有限制地)用作穷人的联合类型。

定义这样的枚举:

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

现在,您可以根据自己的意愿使用DataOrError,并且(确切地)拥有DataError以及DataOrError?中的一个最多一个。< / p>

来电网站,您可以得到以下内容:

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!

设计说明:您可能正在寻找另一个方向的概括,特别是如果您想要与Error进行OR的许多不同类型。在这种情况下,显式包装器可能是一个很好的解决方案,即使你牺牲了很好的语法。

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。还有两种组合;在这里,您可以通过选择初始化程序来控制您想要的内容。

callee-site示例:

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!)")
}