SwiftWhen
When
类似于switch
,但可以用作表达式。
let a = When(number)
.case(0) {"Invalid number"}
.case(1)
.case(2) {"Number is too low"}
.case(3) {"Number is correct"}
.case(4) {"Numbe is almost correct"}
.default("Number is too high")
When
必须以default
结束。这是因为这是一个安全的处理方法。事实上,when
返回一个optional
,所以您可以在case
或默认情况下返回nil。在第一个case
匹配后,When
将执行其块(将被称为 rechtigh der case
- case
表述右侧)。case
可能会省略块,在这种情况下,第一个在此后的action block
将被调用。这是一种组合多个case
的方法。如果没有找到action block
,则default
值将由When
提供。
Action block
是一个返回与在default
中提供的相同类型的任何代码块的块。可以返回action block
和/或default
的nil
。当可以使用它所使用的类型时使用When
。
let b = When<Int, String>(number)
.case(0) {"Invalid number"}
.case(1)
.case(2) {"Number is too low"}
.case(3) {"Number is correct"}
.case(4) {"Numbe is almost correct"}
.default("Number is too high")
与往常一样,When
返回一个optional
,所以结果是String?
。如果您想让When
返回String
而不是默认值,可以采取以下其中一种可能的解决方案。
let b1 = When(number)
.case(0) {"Invalid number"}
.case(1)
.case(2) {"Number is too low"}
.case(3) {"Number is correct"}
.case(4) {"Numbe is almost correct"}
.default(nil) ?? "Number is too high"
case
可以使用不仅是一个值但也条件块((Type) -> Bool
)。
let state = When<State, Int>(stateToTest)
.case({ $0 == .idle || $0 == .preparing || $0 == .ready}) { 0 }
.case({ $0 == .working || $0 == .pausing}) { 1 }
.case({ $0 == .finished }) {2}
.default(3)
请注意,具有值的case
只能用于Equatable
类型,而具有条件块的case
可用于任何类型。
可以组合case
的类型。
let b2 = When(number)
.case({$0 < 0}) {"Number should be > 0"}
.case(0) {"Invalid number"}
.case(1)
.case(2) {"Number is too low"}
.case(3) {"Number is correct"}
.case(4) {"Numbe is almost correct"}
.default(nil) ?? "Number is too high"
有时您没有很多情况,但是只有一个规则可以将一个类型的结果从一个类型的值中制作出来。在这种情况下,您可以使用when
- 这是一个全局函数,它可以作为语句使用并且可以内联使用。
let c = when(number) {
//Here could be any culculations
return $0 >= 10 ? "Big number" : "Small number"
}
2.0.0中的更多功能
func with<Type>(_ source: Type, handler: (Type) -> Void)
它可以用于
Optinal
有一个包含几个新方法的扩展
func takeWhen(_ handler: (Wrapped) -> Bool) -> Wrapped?
func `let`<Result>(_ handler: (Wrapped) -> Result?) -> Result?
func apply(_ handler: (Wrapped) -> Void) -> Wrapped?
func run(_ handler: (Wrapped) -> Void)
可以在项目测试中找到示例
class A1 {
var str: A2 = A2()
class A2 {
var str: A3 = A3()
class A3 {
var str: String = "A3"
}
}
}
func testWith() {
let a = A1()
with(a.str.str) {
XCTAssert($0.str == "A3")
}
}
func testLet() {
let a: A1? = A1()
let str = a.let {
$0.str.str.str
}
XCTAssert(str == "A3")
}
func testRunMethod() {
let a: A1? = A1()
a.run {
let str = $0.str.str.str
XCTAssert(str == "A3")
}
}
func testApply() {
let a: A1? = A1()
_ = a?.str.str.str
a.apply {
$0.str.str.str = "New A3"
}
a.map {
XCTAssert($0.str.str.str == "New A3")
}
a.apply {
$0.str.str.str = "first iteration"
}.apply {
$0.str.str.str = "second iteration"
}.apply {
$0.str.str.str = "third iteration"
}
a.map {
XCTAssert($0.str.str.str == "third iteration")
}
}
func testTakeWhen() {
var a: Int? = 2
if let validA = a.takeWhen({$0 > 1}) {
XCTAssert(validA == 2)
} else {
XCTAssert(false)
}
a = 1
if let _ = a.takeWhen({$0 > 1}) {
XCTAssert(false)
} else {
XCTAssert(a == 1)
}
a = nil
if let _ = a.takeWhen({$0 > 1}) {
XCTAssert(false)
} else {
XCTAssert(a == nil)
}
a = 100
if let _ = a.takeWhen({$0 > 1}) {
XCTAssert(a == 100)
} else {
XCTAssert(false)
}
if let validA = a.takeWhen({$0 > 1}).takeWhen({$0 == 100}) {
XCTAssert(validA == 100)
} else {
XCTAssert(false)
}
}
也可以使用不带参数的When
。
let value = 3
let result = When()
.case({ value == 1}) { 1 }
.case(100.2 == 100.3) { 2 }
.case({ value >= 3}) { 3 }
.case("str" == "str") { -100 }
.default(nil) ?? 0
查看2.0.0中的新功能,以了解如何在新的2.0.0语法中使用不带参数的When
。
操作符
SwiftWhen
提供了一种通过一些操作符将一个值转换为另一个类型的值的简短方式。这可能会更短,但更难阅读。
let result = state == .finished => { 0 } =>? {
state == .working || state == .pausing => { 1 } =>? {
state == .idle || state == .preparing || state == .ready => { 2 } =>! {
3
}
}
}
操作符的全面描述可以在项目Wiki页面找到:https://github.com/smakeev/SwiftWhen/wiki。但要理解这一点,你可能认为:=>
是一个if语句。(左边是条件,右边是then分支。=>?
的功能类似于else if
(并且它应该在其右边包含=>
)=>!
的功能类似于else
,并充当default
语句的作用。
这个操作符链的结果将是一个可选的Int
。如果你想得到一个Some
Int,你会发现不可能只需要在链的末尾添加??来解包。可能的解决方案可能是
var nonOptionalResult = 3
nonOptionalResult =? state == .finished => { 0 } =>? {
state == .working || state == .pausing => { 1 } =>? {
state == .idle || state == .preparing || state == .ready => { 2 }
}
}
在这里,=?
是一个操作符,它只有在右边的值不是nil时才会将右边的值赋给左边的局部变量,否则它不会改变它。默认结果在初始化过程中被分配给了nonOptionalResult
。
如何使用
SwiftWhen
源代码只包含一个源文件SwiftWhen.swift
,所以最简单的方法是将它直接复制到你的项目中。
另一种方法是将其作为子项目添加。
在这种情况下,你需要在想使用它的文件中添加导入。
import SwiftWhen
你可以使用cocoapods https://cocoapods.org.cn/pods/SomeWhen
pod "SomeWhen"
注意,你需要添加
import SomeWhen
2.0.0中的新功能
新的2种语法变体可用:Simple
let source = 5
let result = When(.simple, source) {
$0(0) => "Zero"
$0(1) => "One"
$0(2) => "Two"
$0(3) => "Three"
$0(false) => "Four"
$0(true) => {
return "Five"
}()
$0(6) => "Six"
$0(7) => "Seven"
$0(8) => "Eight"
$0(9) => "Nine"
}.default("default")
通过添加.simple
,你只需在$0
语句中提供情况,并在=>
之后提供相应的值。在有几个情况具有相同结果的情况下,你可以省略除了最后一个情况之外的所有情况后面的=>
。但在这种情况下,编译器会显示警告。要删除警告,你可以使用.skip
.simple
情况才适用。
注意:只有当您提供简单结果时,如果您需要调用函数,这是一个坏主意,因为所有函数都将被调用。
let a1 = 2
let b1 = When(.simple, a1) {
$0(0) => "Zero"
$0(1) => "One"
$0(2) => .skip
$0(3) => .skip
$0(4) => .skip
$0(5) => "Five"
$0(6) => "Six"
$0(7) => "Seven"
$0(8) => "Eight"
$0(9) => "Nine"
}.default(nil) ?? "Unknown"
新语法的第二种变体不包含 simple
关键字。
let b1 = When(a) {
$0.case(0) => "Zero"
$0.case(1) => "One"
$0.case(2) => "Two"
$0.case(3) => "Three"
$0.case(4)
$0.case(5) { "Five" }
$0.case(6) => "Six"
$0.case(7) => "Seven"
$0.case(8) => "Eight"
$0.case(9) => "Nine"
}.default(nil) ?? "Unknown"
在这个变体中,您还可以使用 =>
提供简单结果。您还可以直接提供闭包。如在本例(5)中所示。
两种语法的变体都支持无参数的 When
。
let result = When {
$0.case(3 > 5)
.case({$0 == false})
.case(6 > 20)
.case(false) { "No way" }
$0.case(true) {"true"}
}.default(nil) ?? "Default"
let result = When(.simple) {
$0(3 > 5) => "1"
$0(6 > 20) => "2"
$0(false) => "3"
$0(true) => "true"
}.default(nil) ?? "Default"
旧语法也得到支持,并且可以与新语法结合使用。
case
正确时,闭包才会被调用。
在2.0.1版中,您还可以在 => 之后使用闭包。只有当 以下是一些示例
let testState = TestState.idle
let stateInt = When(.simple, testState) {
$0(TestState.idle) => { 0 }
$0(TestState.preparing) => { nil }
$0(TestState.ready) => { 2 }
$0(TestState.working) => { 3 }
$0(TestState.pausing) => { 4 }
$0(TestState.finished) => { 5 }
$0(TestState.wrong) => { 6 }
}
.default(nil) ?? 0
XCTAssert(stateInt == 0)
let stateInt1 = When(testState) {
$0.case(TestState.idle) => { 0 }
$0.case(TestState.preparing) => { nil }
$0.case(TestState.ready) => { 2 }
$0.case(TestState.working) => { 3 }
$0.case(TestState.pausing) => { 4 }
$0.case(TestState.finished) => { 5 }
$0.case(TestState.wrong) => { 6 }
}
.default(nil) ?? 0
XCTAssert(stateInt1 == 0)
let stateInt2 = When(.simple) {
$0({testState == TestState.idle}) => { 0 }
$0({testState == TestState.preparing}) => { nil }
$0({testState == TestState.ready}) => { 2 }
$0({testState == TestState.working}) => { 3 }
$0({testState == TestState.pausing}) => { 4 }
$0({testState == TestState.finished}) => { 5 }
$0({testState == TestState.wrong}) => { 6 }
}
.default(nil) ?? 0
XCTAssert(stateInt2 == 0)
let stateInt3 = When {
$0.case({testState == TestState.idle}) => { 0 }
$0.case({testState == TestState.preparing}) => { nil }
$0.case({testState == TestState.ready}) => { 2 }
$0.case({testState == TestState.working}) => { 3 }
$0.case({testState == TestState.pausing}) => { 4 }
$0.case({testState == TestState.finished}) => { 5 }
$0.case({testState == TestState.wrong}) => { 6 }
}
.default(nil) ?? 0
XCTAssert(stateInt3 == 0)
在2.0.2版中,您可以在默认情况下使用闭包。如果没有任何情况工作,闭包才会被执行。
.else
而不是 .default
现在,您还可以使用 let a: Int = 5
let b = When(a)
.case(0) {"0"}
.case(1) {"1"}
.default {
if a < 5 {
return "less then 5"
} else {
return " >= 5"
}
}
XCTAssert(b == " >= 5")
let c = When<Int, String>(a) {
$0.case(0) => "0"
$0.case(1) => "1"
}
.else {
if a < 5 {
return "less then 5"
} else {
return " >= 5"
}
}
XCTAssert(c == " >= 5")