迅捷cadjiao
I always try to understand the need of a tool I use in my day to day development life. What the benefits a tool brings or what problem it solves. So to explain the Swift.Result
benefits, I’d like to start with a problem first and then through implementing it we will explore its features.
我一直试图理解在日常开发生活中使用的工具的需求。 工具带来的好处或解决的问题。 因此,为了解释Swift.Result
好处,我想先从一个问题开始,然后通过实施它,我们将探索其功能。
To better explain the idea I will use the next use case:
为了更好地解释这个想法,我将使用下一个用例:
- Read currently logged-in user 读取当前登录的用户
- Using user ID get a category of Services available for the user 使用用户ID获取用户可用的服务类别
- Fetch services of the category 提取类别的服务
- Reload table view 重新加载表格视图
I create a User service that talks to our local storage DB.
我创建一个与我们的本地存储数据库对话的用户服务。
Note: For the simplicity of my explanation I assume that DB works with sync requests. However the same logic can be applied for async requests.
注意 :为了简化说明,我假设DB使用同步请求。 但是,可以将相同的逻辑应用于异步请求。
Now we will use this object in our ViewController
现在我们将在ViewController中使用该对象
Service returns
User
as an optional, that doesn’t tell us if there was an error or a user is not logged in.服务将“
User
作为可选返回,它不会告诉我们是否有错误或用户未登录。- Service returns Optional Category object. The same as in the point 1, was it an error or category is not found? 服务返回可选的Category对象。 与第1点相同,是否未找到错误或类别?
Service returns Optional array of Items. In this case, when nothing reload
TableView
is not called, was it an error, was category not found or it’s just an empty array?.服务返回可选的项数组。 在这种情况下,当没有调用reload
TableView
时,是否是一个错误,找不到类别还是只是一个空数组?
Note: If you’re interested in problems that appear when we use optionals as returned or passed argument in functions, please check my article.
注意:如果您对我们在函数中使用可选值作为返回或传递的参数时出现的问题感兴趣,请查看我的文章 。
As I showed in my article about optionals, Optional<>
is enum and basically represents monads. So using the same idea we could create our own Either monad
正如我在有关可选对象的文章中所展示的那样, Optional<>
是枚举,基本上代表monads 。 因此,使用相同的想法,我们可以创建自己的Either monad
Now wait! If we check definition of Swift.Result, we can notice the resemblance
现在等等! 如果我们检查Swift.Result的定义,我们会注意到相似之处
Now I’ll re-write our UserDataBaseService
现在,我将重写我们的UserDataBaseService
This can tell us the error thus helping the client to react properly on the result.
这可以告诉我们错误,从而帮助客户对结果做出正确的React。
As you might notice loadItems
function became cleaner and we have common entry point for processing errors. However you didn’t come here to read the article to see 3 functions with switch
in every one of them, right?
您可能会注意到loadItems
函数变得更加loadItems
,我们有处理错误的通用入口点。 但是,您并不是来这里阅读文章的,以查看其中每个switch
都有3个功能,对吗?
I might sound here like a broken record, but: “if you’ve read my article about optionals” and since I’ve already mentioned above, we could see the similarity between Optionals and Result. So let’s see the open code for Swift.Result
and find useful functions map
and flatMap
我在这里听起来像是一个破记录,但是:“如果您已经阅读了有关可选参数的文章”,并且由于我已经在上面提到过,我们可以看到可选参数和结果之间的相似之处。 因此,让我们看一下Swift.Result
的开放代码,并找到有用的函数map
和flatMap
To summarize map
will check if the result is success, take out the value, apply transform function on the value and return new Result
with a new value inside it. It helps us to avoid using switch
or if case
pattern matching on the client side since it’s already done in the function.
要汇总map
将检查结果是否成功,取出值,在值上应用转换函数,然后返回包含新值的新Result
。 因为它已经在函数中完成,所以它可以帮助我们避免在客户端使用switch
或if case
模式匹配。
As for the flatMap
, it provides the same idea with the difference that transform function can return Result
.
至于flatMap
,它提供相同的想法,只是转换函数可以返回Result
。
Let’s see how we can apply this idea to the same code.
让我们看看如何将这个想法应用于相同的代码。
Note:
注意事项 :
if one of the functions (
currentUser, getCategory, items
) returns failure — no further functions will be performed.如果功能之一(
currentUser, getCategory, items
)返回失败,则将不再执行其他功能。notice how we pass functions (
service.getCategory
) without using the full syntax like:{ service.getCategory(for: $0) }
. This is a help from the compiler since it’s able to resolve the syntax by itself请注意,我们如何在不使用完整语法的情况下传递函数(
service.getCategory
):{ service.getCategory(for: $0) }
。 这是编译器的帮助,因为它能够自行解析语法
Hm… Isn’t it nice?! However I’d like to avoid using the switch and to do this I need to add two useful functions do
and onError
嗯...不好吗? 但是我想避免使用开关,为此,我需要添加两个有用的函数do
和onError
Here we got the final result using Swift.Result
在这里,我们使用Swift.Result得到了最终结果
Notice how much cleaner our code became by leveraging some of checks on the API provided by Swift language and the small extension that we implemented. The same approach can be applied to asyn request with complietion
注意,通过对Swift语言提供的API和我们实现的小型扩展进行一些检查,我们的代码变得更加简洁。 可以将相同的方法应用于带有异步的异步请求
结论 (Conclusion)
I’m encouraging you to explore more about Swift.Result and its features. there are a couple of functions that I haven’t covered, but they are really useful. Also, don’t afraid to experiment and add your own custom functions that will help you to leverage checks for the case
. Later on when you want to move to Combine or RxSwift or any functional / reactive programming it will be easier for you to transition.
我鼓励您进一步了解Swift.Result及其功能。 有一些功能我没有涉及,但是它们确实很有用。 另外,不要害怕尝试并添加自己的自定义函数,这些函数将帮助您利用对case
检查。 稍后,当您想转到Combine或RxSwift或任何功能/React式编程时,将更容易过渡。
大提示: (BIG NOTE:)
If you made so far I want to thank you and say one important thing. I put everything into a ViewController and used “fat” classes like Service
for the sake of easy explanation. Usually I separate the logic using different design patterns.
如果您到目前为止取得的成就,我要感谢您并说一句重要的话。 为了便于说明,我将所有内容都放入ViewController并使用了Service
类的“胖”类。 通常,我使用不同的设计模式来分离逻辑。
迅捷cadjiao