Tag: Reswift

ReSwift — Redux风格的单向数据流

ReSwift是Swift中redux哲学的实现。 如果您不熟悉Redux,那就是管理应用程序的状态。 这样它总是可以预测的。 让我们看一下下面的架构以更好地理解它。 存储 -这是应用程序整个状态所在的位置。 状态可以只是查看状态,甚至可以是数据库状态,应用程序状态等,具体取决于您要如何使用ReSwift。 我建议仅将其用于视图状态,因为ReSwift在主线程上执行。 视图 -视图部分是整个视图层或应用程序的外观 。 简而言之,视图是状态的函数,在任何时候它都只是应用程序状态的一个很好的表示。 动作 -动作是通过用户交互通过视图分派的,并且由商店使用,然后商店会更新应用程序的状态。 这里还有其他两个部分– 减速器 —减速器是纯函数。 他们消耗一个状态,一个动作并产生一个新状态。 中间件 -上面的图中显示了中间件,但是中间件在还原器之前起作用。 每个动作都必须经过中间件,并且它可以对该动作起作用。 它们对于事件记录和操作计划非常有用。 如果您能想到更多案例或想讨论中间件的用例,请在下面的评论中发表。 在普通的iOS应用程序中,ViewController倾向于承担比任何其他组件更多的责任。 随着屏幕复杂性的增加,在我们的视图控制器中维护干净的代码变得越来越困难。 您可以将其与用例联系在一起,在这些用例中,我们最终向视图控制器添加标志和枚举,以使其适用于不同的状态。 记住很难重现的错误,因为只有当您以特定组合执行某些操作时,这些错误才会浮现出来。 ViewController已经承担了很多责任。 跟踪标志,枚举和查看数据是它应该担心的最后一件事。 想象一下,每当您看到与视图相关的错误时,如果只有一种检查方法,这将是多么容易! 使用ReSwift时,View只是状态之上的皮肤。 这意味着,只要您发现视图有问题,就可以随时检查状态以检查不同的值。 同样,状态只能通过调度动作来更改,因此您可以轻松找出导致该状态的动作。 如果预测不是很简单,则可以使用中间件来记录所有已调度操作的日志。 我们知道iOS中出现了许多架构,例如MVVM,MVP,VIPER等。ReSwift以非常独特的方式将自己与这些架构区分开。 如果仔细观察,所有其他架构都将重点放在责任分离上。 ReSwift可帮助您管理应用程序的状态。 这也意味着ReSwift可以轻松地与同一应用程序中的所有这些架构共存。 基本原则 所有视图数据都应以某种状态存储。 在任何时间点-整个应用程序(如用户所见)都是该状态的“很好的表示”。 只能通过调度动作来修改状态。 对于每个视图控制器,只有一个类(通常是演示者)将侦听并响应状态更改。 与MVP并肩作战 代码示例 以下是图像库应用程序中的代码片段,展示了ReSwift框架中的语法片段。 有关完整的代码,请访问https://github.com/amreshkumar/ReSwiftDemo

滑动过渡和快速浏览– Christian Tietze –中

(在https://christiantietze.de/posts/2017/01/reswift-swipe-transitions/中阅读格式正确的故事) 在昨天的一次客户会议上,我们尝试找出当ReSwift是应用程序状态的唯一真实来源时,如何通过向左/向右滑动来为场景过渡设置动画。 应用进入什么状态? 您如何制作动画? 出于某种原因,转换百分比是否应该成为应用程序状态的一部分? (剧透:没有) 滑动首先具有挑战性,因为从视图控制器A到B的这种交互式过渡需要同时显示两者:滑动时,需要在视觉上“拖动” B。 添加自定义导航控件时,最终会得到一个主视图控制器,该控制器包含一个子视图控制器以显示实际的表视图(下图中的绿色框)。 这些表格视图应从左侧或右侧刷入并触发导航更改。 在此示例中,用户看到给定日期的数据。 她应该使用滑动和导航按钮自由导航到前一天和后一天,直到时间的开始或结束。 (或我们的数据限制,以先到者为准。) 让我们分析一下实现这一点。 单状态替换,无过渡 在没有过渡的静态世界中,只有导航栏的“上一个”和“下一个”按钮(以蓝色表示)会触发导航更改:您点击该按钮,请求来自服务器的新数据,也许您会显示一个加载指示器,然后替换UITableDataSource的内容。 现在,如果您使用ReSwift,则当前可见的数据集合将成为您应用状态的一部分。 保持简单,表格视图的单元格将显示文本。 状态看起来像这样: struct AppState:ReSwift.StateType { var内容:[String] } 想象一下,您有一些动作,减速器以及对导航更改做出的适当反应。 (这本身可能是一个挑战,并且可能是另一天的话题。提示:您将需要“更改日”操作来触发网络请求,并需要“替换数据”操作来更新内容。) 为了显示最新状态更改,您设置了一个Presenter,它是ReSwift.StoreSubscriber。 当您从服务器接收数据并替换AppState.contents时,将调用newState回调。 然后,此字符串数组将传递到用户界面进行显示。 我们将该方法称为updateView(linesOfText :)。 这是一个体系结构上的注释: updateView(linesOfText 🙂方法,我想要演示的调用者应该由主视图控制器公开。 反过来,这可以委派给其当前的子视图控制器,该控制器处理表的实际显示。 但是,从长远来看,将表示者(表示层(!)外部的服务对象)耦合到子视图控制器可能会对您造成伤害。 主视图控制器是整个组件的外壳,因此它负责公开可用的接口。 内部组件的数量和对其的委派是其他对象不关心的实现细节。 (您一会儿就会明白为什么。) 此设置非常简单。 AppState更改通过Presenter的流程,该Presenter会在必要时创建视图模型,然后将其传递到其视图组件。 结果,UITableView重新加载了新数据,您就完成了。 那是最准方法。 在添加交互式过渡之前,让我们首先使其具有更高的响应速度。 现在,每次点击按钮都会触发一个网络请求,该请求使用户的交互停止。 “走走走走”导航在孩子们中并不普遍,因此我们将在下一步中预取隔天的数据。 预取相邻天的数据 在表示层中,我想象情况会有所变化,如下所示: 从上面对简单方法的更改是: 主视图控制器具有3个子视图控制器,而不是1个。所有子视图控制器均已准备就绪,可以显示。 轻按按钮现在有两件事:像以前一样触发“更改日”导航操作, 并立即将正确的子视图控制器放在顶部。 为了使所有这些成为可能,Presenter组装了一个具有3个内容数组而不是1的ViewModel。 视图模型仍然非常简单: struct […]

在iOS应用中使用ReSwift进行路由(导航)

我一直在与ReSwift合作构建iOS应用。 ReSwift是iOS的redux版本。 这样做的想法是在全局存储区中拥有完整的视图状态。 这样,您的视图便成为状态的函数。 在给定状态的情况下,您知道视图的外观。 从您的角度来看,您将调度一个动作,这是更改状态的唯一方法。 然后,您的视图将根据状态信息重新呈现自己。 在这篇文章中,我想探讨一下我们如何使用ReSwift在我们的应用程序中构建导航流。 如果您想开始使用ReSwift,请仔细阅读ReSwift入门指南以及这篇出色的文章。 当我们研究如何在应用程序内进行路由并通过ReSwift驱动路由时,我们希望基于以下原则构建内容: 我们应该能够从应用程序中的任何位置调度动作,以从一个屏幕移动到任何其他屏幕。 我们想在情节提要上使用Segues来管理过渡。 我们也不想将自己限制为仅使用Segues进行过渡,如果需要进行程序化过渡,那应该是可能的。 所有路由都应从一个中心位置处理,以使其易于调试和维护 在任何时间点,如果我们查看状态,它都应该反映出我们当前所处的确切屏幕。 最好通过一个示例来说明如何使用上述原则对解决方案进行编码: 这是我们正在实施的应用程序: 在情节提要上,它是这样的: 上面的图像总结了解决方案。 您可以使用2个流程从一个屏幕切换到另一个屏幕。 过渡—这是最简单的流程。 这实质上是将一个屏幕推到导航控制器上,或者模态呈现一个屏幕。 在图中,这是从FirstTabViewController移到Top或BottomViewController的 重置屏幕层次结构-如果您位于屏幕层次结构内部深深嵌套的屏幕上,并且希望退出屏幕层次结构并转到全新的屏幕层次结构。 在图中,这是从BottomViewController移到SecondTabViewController的。 当您深入链接到应用程序时,这是最需要的。 您可能在应用程序中的任何位置,当您深入链接到应用程序时,您将必须重置屏幕层次结构,并根据深度链接的要求移至新屏幕。 我提供以上示例来说明如何实现重置屏幕层次结构。 理想情况下,您不应该像示例那样拥有用户流,因为这会扼杀用户体验。 所有路由均由AppRouter处理。 有一个由AppDelegate实例化的AppRouter实例,该实例订阅了NavigationState。 接下来,每个发生屏幕切换的视图控制器都具有一个附加的路由器。 当我们需要从一个屏幕移动到另一屏幕时,例如单击“顶部”按钮时,我们将触发类似以下的操作: 这里的Screen是一个枚举,其中包含应用程序上所有可能的屏幕,数据是一个字典,您可以用来传递新屏幕需要呈现的信息。 当转换动作被触发时,NavigationState会更改,并且新状态上有一个名为’transition’的标志设置为true。 此外,转换数据设置为我们要传入的字典。 导航状态更改后,接下来会发生什么? 下图说明了如何触发,进行转换并在显示的新屏幕中达到高潮。 AppRouter侦听对NavigationState的任何更改,然后检查是否设置了过渡标志,如果已设置,则获取当前屏幕的路由器。 在这种情况下,当前显示的屏幕是FirstTabViewController,该视图控制器的相应路由器是FirstTabRouter。 在FirstTabViewController的viewWillAppear中,我们创建FirstTabRouter的新实例,并将其添加到RouterRegistry中,RouterRegistry是[Screen:Router]类型的字典。 AppRouter从NavigationState获取当前屏幕,然后向RouterRegistry查询当前屏幕的路由器。 然后,它在该路由器上调用show()。 路由器现在要做的就是为该屏幕过渡执行segue。 一些解释所有这些的代码: executeSegueBlock需要一些解释。 当我们要求视图控制器执行segue时,视图控制器将调用prepareForSegue(),它允许您在呈现目标视图控制器之前对其进行配置。 我们在FirstViewRouter中所做的是将一个闭合块传递给executeSequeBlock()调用。 闭包在prepareForSegue()中执行。 这使您可以保持2个代码块,调用segue并准备新屏幕并拢,从而更易于理解和维护。 需要说明的另一点是重置屏幕层次结构。 有时,当您深入链接到应用程序时,或者如果您想要从一个屏幕的层次结构内部的一个屏幕过渡到整个新的屏幕层次结构,那么该怎么做? 例如,如果要从BottomButtonViewController移至SecondTabViewController。 涉及3个步骤: 我们需要关闭所有屏幕并重置为根视图控制器。 […]

类型擦除

下标不知道要打什么 前情提要 会想要打这篇是因为因为最近在看ReSwift跟Redux时发现ReSwift的源代码中有很多AnySubscriber,AnySubscription,SubscriptionBox之类奇怪的型态,而仔细看的话可以发现这些型态下面也有Subscriber,Subscription之类没有任何的型态。而造成的原因是什么呢?在解释前我们先来看一下例子吧! 会想要打这篇的另外一个原因是因为之前有看到跟Type Erasure有关的文章,但当时的我不太了解为什么要这样做,而最近看ReSwift时实在看太多type Erase了,于是决定把它搞懂。 先看个例子 只能这样了吗? 不,我才不要这样就放弃呢。 那我们只好把Pokemon这个形态给藏起来,不让swift编译器发现我们在干不法勾当。 如果我们不能使用Pokemon protocol当类型态,那我们能不能使用class将它包起来呢?可以!但应该怎么做呢?其实你在写swift时应有发现些东西叫做AnySequence,AnyIterator等等的东西,其实你仔细想想,Sequence是个协议,他不能当作型态使用,但有时我们在宣告阵列时他会是AnySequence型态。翻找一下swift源代码可以发现apple在实作这些wrapper的思路与实作方式,我们来看一下apple怎么写: ///一个类型擦除的序列。 /// ///`AnySequence`的实例将其操作转发到基础数据库 ///具有相同“元素”类型的序列,隐藏了 ///基础序列。 // @ _ versioned @_fixed_layout 公共结构AnySequence :序列{ ///创建一个新的序列,该序列将操作包装并转发到`base`。 @_inlineable public init (_ base:S) 哪里 S.Element ==元素{ self._box = _SequenceBox(_base:基本) … } 可以看到AnySequence是把Sequence的操作进一步的拉出来的wrapper而已。我们需要做的事是做个init,并且检查Sequence中的Element跟初始化时的S.Element是否为同一型态,然后把操作拉出来即可(apple使用另一层wrapper:box把比较杂乱的代码藏起来了,打开AnySequenceBox可以看到进一步的实作)。 那我们现在也来做一样的事吧: 抓到法律的突破后,我们就有了看似真的AnyPokemon型态了,于是我们很开心的把神奇宝贝农场改成: let pokemonRanch:[AnyPokemon] = [] 但别忘记AnyPokemon也算是某种程度的泛型型态,我们必须告诉他Power是什么: let pokemonRanch:[AnyPokemon ] = [] OK,这样写就没有error了。 但我们应该怎么把神奇宝贝放进去呢呢? let pokemon牧场:[AnyPokemon […]

一目了然

最近,我观看了一段有关iOS架构的视频,讲师在演讲中提到了一种名为ReSwift的架构。 几天后,这个名字再次出现在SwiftCoder播客的一集中。 因此,我决定进行一些研究并尝试找出答案。 介绍 ReSwift的GitHub页面引用了以下内容。 ReSwift是Swift中单向数据流架构的类似Redux的实现。 ReSwift可帮助您将应用程序组件的三个重要方面分开: 状态:在ReSwift应用中,整个应用状态都明确存储在数据结构中。 视图:在ReSwift应用程序中,当状态更改时,视图也会更新。 您的视图成为当前应用程序状态的简单可视化。 状态更改:在ReSwift应用中,您只能通过操作执行状态更改。 动作是描述状态变化的小数据。 ReSwift还依赖于一些原则: 商店以单个数据结构的形式存储整个应用程序状态。 只能通过将操作调度到商店来修改此状态。 每当商店中的状态发生变化时,商店都会通知所有观察者。 动作是描述状态变化的一种声明方式。 动作不包含任何代码,它们由商店使用并转发给减速器。 减速器将通过为每个动作实施不同的状态更改来处理这些动作。 Reducer提供了纯函数,这些函数基于当前操作和当前应用程序状态创建新的应用程序状态。 示范 我将演示如何在一个简单的项目中采用ReSwift。 该项目使用SWAPI来获取《星球大战》的电影,并在表格视图中显示它们。 以下是我的视图控制器的直观展示。 让我们开始定义我的State 。 因为我的项目非常简单,所以我只用一个enum来描述FilmsState 。 struct AppState: StateType { let filmsState: FilmsState } enum FilmsState { case loading case finished([Film]) } 其次,我创建三个不同的Action ,包括LoadingFilmsAction , SetFilmsAction和fetchFilms 。 由于影片的数据是从SWAPI获取的,并且此操作是异步的,因此fetchFilms实际上是ActionCreator而不是Action类型。 提取影片的数据完成后,我使用Store来调度SetFilmsAction以便更新表格视图。 此外,我编写了一个WebService类来处理网络操作,您可以在本文中找到更多详细信息。 struct LoadingFilmsAction: Action […]

Swift中Redux入门–第1部分

一种学习Swift中Redux入门的简单方法。 Redux是一种架构软件模式的实现,该模式优先考虑单向数据流。 它是由Flux架构(由Facebook开发)创建的,在应用程序的开发中已取得了长足的发展,并有望在其使用中带来巨大的优势。 它是诸如MVC,MVVM和Viper之类的其他架构模式的替代。 好处 Redux的巨大希望之一就是创建约束条件,以鼓励更有组织的软件开发和测试,因此,由于这些原因,最终会降低开发阶段的复杂性,并提供维护应用程序状态和高级调试的工具。 。 本文介绍了一种开始了解这种新模式的简单方法。 实施要求 在iOS(Swift + Xcode)上构建应用程序的基本级别。 观察者模式知识。 知道如何使用CocoaPods依赖系统。 组件 状态:代表应用程序的状态。 只能有一个,可以分为子状态。 动作:这些是描述系统功能的简单对象。 根据情况,这些对象是否可以携带信息。 它们是由View层调度的,目的是更改应用程序的状态。 减速器:这是我们开发应用程序主要逻辑的地方。 减速器必须是无副作用的纯函数,并且必须是同步的。 它们是唯一可以为应用程序创建新状态的对象。 给它们一个动作和当前状态,并返回一个新状态。 请注意,单向流是在View调度Action时发生的。 此Action传递到相应的Reducer,因此,此Reducer根据先前的Action生成一个新的State,并将State传递回View,以便对其进行更改。 商店:这是此实现过程中最重要的组成部分。 它是汇总上述所有组件并使流程正常工作的原因。 View将新的Action调度到商店。 然后,商店将此动作与当前状态一起传递给Reducer,然后接收回还原器的新状态。 每当创建新状态时,都会警告视图,这可以通过实现观察者设计模式来实现,该观察器设计模式允许视图成为要通知的商店的“订户”。 开始吧 我开始学习Redux的方法是使用一个名为ReSwift的库来构建示例应用程序(“ Rock,Paper and Scissors”游戏),该库在Swift中实现了该架构的概念。 我们首先概述应用程序的外观。 为简单起见,该应用程序应在单个ViewController上工作,在底部包含3个按钮(Rock,Paper和Scissors),在顶部包含1个消息字段,并包含2个占位符,以显示玩家何时进行移动以及最后移动揭示玩家的武器。 为了开始开发,我提出了一个用例,其中Player1选择剪刀,而Player2选择Rock ,从而赢得了Player2的胜利。 该流将发生如下: 发展历程 在Xcode中创建一个新的“单视图应用程序”项目,并使“包含单元测试”能够使用Redux的概念进行测试。 使用CocoaPods安装“ ReSwift”吊舱。 接下来,我们将创建第一个组件,状态。 查看上图,我们可以清楚地看到应用程序在执行过程中将发生变化的部分,其中每个部分都由应用程序的状态组成。 然后,我创建了一个State.swift文件,并将状态形成结构以及可能形成应用程序概念的模板结构放入其中。 重要的是要指出结构必须是不可变的,以便Redux起作用,只有这样我们才能确保状态仅由化简器更改,因此我使用了Structs和Enums而不是Classs: 现在让我们创建一个动作,该动作将描述打算更改状态的动作。 在这种情况下,我们只有一个ChooseWeaponAction ,它在每个玩家选择武器时触发: 最后,我们将构建减速器。 在这里,我们过滤创建的Action,获取应用程序的当前状态,并根据将使用Action中包含的信息开发的逻辑来生成新的State: 测试 […]

Swift中的Redux模式

在Swift中进行Redux吗? 是否可以在Eine iOS App中恢复JavaScript库并消除JS Zeug残酷的问题? 在设计模式和建筑设计中的修女,以及其他的书目。 标准MVC模式和ViewController之间的问题,由Gerl mal ins Endlose reichen负责。 Testbarkeit istfürnArsch,死于Übersichtüberdas ganze Projekt。 Dann haben wir noch den viel zu oft kopierten Code um wieder and wieder die gleichen Dingeauszuführenetc pp。 Natürlichgibt es daunzähligeMöglichkeiten(Wie immer)死于问题zulösen。 iOS Swift Blase lebe的应用程序之类的应用程序,以及React Native的其他应用程序。React.js在Webprojekte网站上的发布,请立即订阅。 Eine kleineErläuterung,我们的人eigentlich sich dieMühemachte,sich das Redux Prinzip zuüberlegen: 在React ist中,Komponenten aufgebaut ist中的dass eine […]

重新练习

关于将Redux-Style应用程序体系结构用于iOS应用程序存在一些嗡嗡声。 该模式绝对值得仔细研究,并具有一些巨大的优势,但是在实践中效果如何? 关于ReSwift的许多介绍已经在那里,但是,我还没有看到有关如何在实际的可交付应用程序中应用ReSwift的出色示例。 在本文中,我试图演示如何使用ReSwift构建几乎“真实世界”的应用程序,同时使所有内容都可测试并与系统API和网络服务很好地集成。 如前所述,已经有大量的信息介绍ReSwift了,我正在努力使其简短。 单向数据流意味着没有将您的应用程序状态保存在许多不同的地方。 取而代之的是,存储组件将所有状态保持在中央位置。 视图对这种状态的更改做出反应,而不是在内部进行处理。 动作是触发状态更改的唯一方法。 动作不会自行执行状态更改,而是将它们更像是表示某些内容将发生更改的消息。。这些“消息”是针对使用执行实际状态更改的Reducer的Store对象发出的。 还有中间件来处理副作用,稍后将介绍。 这种方法有一些很大的好处: 业务逻辑与UI保持独立 减速器具有纯功能-非常适合测试 视图内的简单逻辑-只需对状态更改做出反应并将操作发送到存储 可以在多个视图(或其他组件)之间轻松共享状态 与标准MVC相比,总体而言,方法更加清洁 有一个反例以非常基本的形式展示了上述架构。 在该示例中,一切看起来都很干净,但是通常的应用程序开发要复杂得多。 操作系统或第三方框架提供的异步操作(例如,网络连接)和功能需要集成。 本文的其余部分介绍了如何设置一个经过全面测试(UI除外)的示例应用程序,该应用程序集成了CoreLocation和Google的网络服务,该服务需要用户的位置。 以下是要求的简短列表: 处理位置权限并对其进行更改。 获取用户位置并更新用户移动。 接收到用户的位置后,在附近显示餐馆。 您可以在GitHub上查看所有源代码。 对于每一步骤,至少有一个提交。 每个步骤都将提供指向相应源代码的链接。 项目设置 作为起点,已经存在一个将ReSwift作为依赖项的Xcode项目。 此外,围绕Google Places API的简单网络抽象以及用于检索地点(更好的餐厅)信息的相应模型类已经在那里。 显示餐厅的第一步是设置所需的ReSwift对象:Store,Reducer和State。 中间件的定义如下: 更新操作 -如您在上图中所看到的,现在需要两个操作来获取和设置位置。 (此处设置表示更新状态。) 与以前不同,不需要执行任何副作用,因此不需要中间件。 而是需要一个Location Observer器。 在外部,它的行为几乎类似于某些UI组件,但是它不是根据用户输入发送动作,而是在状态发生特定变化时发送动作。 LocationObserver的重要部分。 仅局部观察该状态,并在newState上调度提取位置操作。 上面的代码段显示了观察位置变化的实现。 同样,这里省略了集成和测试。 您可以在此处看到完整的实现:65bef0d 这样就完成了示例应用程序的工作。 您应该能够构建和运行该应用程序。 授予位置权限后,您应该可以看到周围的餐厅,而移动餐厅时,这些餐厅应会自动更新。 在本文中,我比其他大多数文章更深入地介绍了用ReSwift编写应用程序的细节。 在处理副作用时,我展示了中间件的大量使用。 这有点违背提供的示例项目(例如GitHubBrowserExample),但将动作和网络逻辑分开,并提高了可测试性。 好消息是ReSwift足够灵活,可以调整应用程序体系结构以满足您的需求。 另一方面,我对标准模式没有将副作用与行动分开并且没有提供良好的策略测试感到非常不满意。 […]