Recently, I love to learn about components in iOS programming, then I can read a pretty good article about Custom Pattern Matching, now freely re-translate and share with everyone.
Custom Pattern Matching
Pattern matching is everywhere in Swift, you probably have used it a thousand times to deconstruct and bind values as in the case of switch
. Swift comes in a variety of templates that can be combined and used even switches
for short and cool code.
First, let’s look at pattern matching in a simple comparison test example:
1 2 3 4 5 6 7 8 9 | switch 80 { case 100: // case 80: // Matches, bởi vì 80 == 80 default: break } |
And there are several types that interact with each other, such as Ranges and associations like:
1 2 3 4 5 6 7 8 9 10 11 | switch 80 { case 0...20: break case 21...50: break case 51...100: //Matches, bởi vì 80 nằm trong khoảng 51...100 default: break } |
To be able to match within such a range is because ~=
pattern matching operator. This operator is not seen much in common projects, but it is used a lot internally within Swift and it is the same operator used in validating case statements.
In most cases the operator is a simple encapsulation for equality testing (like the Int example above) but Range
has a special setting for ~=
which allows them to have a custom mode:
1 2 3 4 5 6 7 | extension RangeExpression { @inlinable public static func ~= (pattern: Self, value: Bound) -> Bool { return pattern.contains(value) } } |
And we can overload it ~=
to write our own matching logic pattern.
For example to be able to match “eighty” with 80, all we need to do is add a version of the String
value match operator with Int
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | func ~= (pattern: String, value: Int) -> Bool { if pattern == "eighty" { return value == 80 } else if pattern == "not eighty" { return value != 80 } else { return false } } switch 80 { case "eighty": //Compiles and matches! case "not eighty": // default: break } |
In this particular case, the backend team wants to return an Int
or String
. And with custom pattern matching, you can use the more extensive type while still treating it like it’s mapped to a more specific enum type. This can be useful when you want to try out concepts without having to use certain types:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | enum WeekDay: Int { case sunday case monday case tuesday case wednesday case thursday case friday case saturday } func ~= (pattern: WeekDay, value: Int) -> Bool { return pattern.rawValue == value } // Server trả về: // { nextHoliday: { weekDay: 5 } } if case .friday? = nextHoliday?.weekDay { print("Woohoo!") } |
Creating option templates is a simple way to write cleaner code without too much effort. Because you can leverage cases
to get straight to something without necessarily adding other attributes to your styles – and make sure your lines of code don’t become harder to understand.
Hope after this article everyone will try overload and use the ~=
operator in cases like this.
This article I refer to from a quite famous blog https://swiftrocks.com/writing-custom-pattern-matching-rules-in-swift.html , in this there are many good articles, if I have the chance will take advantage of translating all these articles.