SomeWhen 2.0.2

SomeWhen 2.0.2

Sergey Makeev维护。



SomeWhen 2.0.2

  • Sergey Makeev

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和/或defaultnil。当可以使用它所使用的类型时使用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"

旧语法也得到支持,并且可以与新语法结合使用。

在2.0.1版中,您还可以在 => 之后使用闭包。只有当 case 正确时,闭包才会被调用。

以下是一些示例

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