热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

swift引入pod_引入自旋:Swift中的通用反馈循环系统

swift引入pod快速应用中对架构模式的需求随着最近对Combine和SwiftUI的介绍,我们将在代码库中面临一些过渡期。我们的应用程序将使用Combine和第三

swift 引入pod

快速应用中对架构模式的需求

随着最近对Combine和SwiftUI的介绍,我们将在代码库中面临一些过渡期。 我们的应用程序将使用Combine和第三方响应框架,或者同时使用UIKit / AppKit和SwiftUI。 随着时间的推移,这可能难以保证一致的体系结构。 很难知道何时将这些新技术结合到我们的项目中。 从一开始就正确选择体系结构可能会大大简化将来的过渡。

传统的架构模式(例如MVC , MVP或MVVM)大多负责UI层。 当以统一的方式在您的应用程序内部混合上述技术时,它们不会有太大帮助。 例如,UIKit应用程序中的MVVM将在很大程度上依赖双向绑定技术,并具有React性扩展,例如RxCocoa或ReactiveCocoa。 随着您逐步引入SwiftUI和Combine,这将变得不那么正确。 很有可能会在您的应用程序中包含多个架构范例。

VIPER更加完整,因为它描述了场景之间的路由机制以及模型(实体)与管理它们的业务规则(交互器)之间的分离。 这种模式执行了关于关注点和依赖关系管理分离的“ 干净架构”的原则。 但是,与MVxx模式一样,当逐步采用Combine或SwiftUI时,它也无法保证语法和范例在时间上的一致性。

SwiftUI将状态视为事实的唯一来源,并对状态突变做出React。 这允许以声明的方式而不是冗长且易于出错的命令性方式来编写视图。

近年来,围绕状态概念出现了几种架构模式: Redux或MVI之类的东西以及更普遍的单向数据流架构。 有时他们会提出以中央方式(有时是局部方式)来管理国家。 这些都是很好的模式,非常适合将国家作为唯一真理来源的思想。 我敢肯定,他们在SwiftUI的制作中具有很大的影响力。

我本人已经在生产应用程序中实现了其中一些模式。 他们向我介绍了函数式编程,因为它们依赖于不变性,纯函数,函数组成等概念。 功能编程和状态管理非常吻合。 功能编程与数据不变性相关,因此状态也应如此。

但是,我遇到了这类架构的一些缺点,这使我发现了反馈回路系统。

什么是反馈回路系统?

反馈回路是一种系统,能够通过将其计算的结果值用作自身的下一个输入来进行自我调节,并根据给定规则不断调整该值(反馈回路用于电子等领域,以自动调整电平例如信号)。

声明这种方式听起来可能模糊不清,并且与软件工程无关, 但是 “根据某些规则调整值”正是为程序以及扩展为应用程序而设计的! 应用程序是我们要调节的各种状态的总和,以便遵循精确的规则提供一致的行为。

状态机描述描述从一个值到另一个值的允许过渡的规则。

什么是状态机?

它是一台抽象机,在任何给定时间都可以恰好处于有限数量的状态之一。 状态机可以响应某些外部输入而从一种状态变为另一种状态。 从一种状态到另一种状态的转换称为过渡。 状态机由其状态,其初始状态以及每个转换的条件的列表定义。

由于应用程序是状态机,因此它可以由反馈回路系统驱动。

反馈环基于三个组件:初始状态,反馈和减速器。 为了说明它们中的每一个,我们将依靠一个基本示例:一个系统,计数范围从0到10。

  1. 初始状态 :这是计数器的初始值0。
  2. 反馈 &#xff1a;这是我们应用于柜台以实现目标的规则。 反馈的输出是对计数器进行更改的请求。 如果0 <&#61; counter <10&#xff0c;那么我们要求增加它&#xff0c;否则我们要求停止它。
  3. 减速器 &#xff1a;这是我们系统的状态机。 它描述了给定其先前值的所有可能的计数器转换以及由反馈计算出的请求。 例如&#xff1a;如果先前的值为0&#xff0c;并且请求增加该值&#xff0c;则新的值为1&#xff1b; 如果前一个是1&#xff0c;并且请求增加它&#xff0c;那么新值是2&#xff1b; 等等等等。 当来自反馈的请求停止时&#xff0c;则将先前的值作为新值返回。

反馈是唯一可以执行副作用&#xff08;网络&#xff0c;本地I / O&#xff0c;UI呈现&#xff0c;无论您执行什么操作来访问或更改循环本地范围之外的状态&#xff09;的地方。 相反&#xff0c;reduceer是一个纯函数&#xff0c;仅在给定前一个值和转换请求的情况下才可以产生新值。 禁止在还原剂中产生副作用&#xff0c;因为这会损害其再现性。

传统单向数据流体系结构的缺点

如您所见&#xff0c;反馈回路范式的特殊之处在于&#xff0c;状态既是输入又是输出&#xff0c;两者都作为一个整体连接在一起形成一个单向回路。 只要循环是活动的并且正在运行&#xff0c;我们就可以将状态用作本地缓存来持久化数据。

典型的用例是浏览分页的API。 这种系统允许使用当前状态来始终使上一页URL和下一页URL可以访问&#xff0c;这与将其存储在其他位置无关。

在更传统的单向数据流体系结构中&#xff0c;状态仅是系统的输出。 输入是触发副作用然后声明突变的“用户意图”。

我经历过几种类型的体系结构&#xff08;Redux和MVI&#xff09;&#xff0c;发现自己陷入了两个主要问题&#xff1a;

  1. 不使用状态作为输入会导致在UI层或缓存存储库中维护本地状态。
  2. 依靠经常是枚举的诸如Intent或Action之类的输入&#xff0c;迫使我们使用&#96;switch&#96;语句对它们进行解析&#xff0c;以确定要执行的副作用。 当我们添加新的意图或新动作时&#xff0c;我们必须改变我们解析它们的方式&#xff0c;这违反了SOLID规则的“打开/关闭”原则。

我并不是说这些问题是使用这些体系结构的“不可行”&#xff0c;也不是说我已经以最好的方式使用了它们。 例如&#xff0c;我研究了MVI的一种变体&#xff0c;其中用“命令模式”代替了意图。 每个命令负责其自己的副作用执行。 没有解析&#xff0c;命令是自给自足的。 这种方法符合“打开/关闭”原则&#xff0c;因为添加新功能就是添加要执行的新命令。 而不修改意图或动作的解析方式。

但是&#xff0c;我赞成采用一种自然解决这些问题的方法&#xff0c;而不是为了满足我的需要而扭曲这些体系结构&#xff1a;反馈回路系统。

什么是自旋&#xff1f;

让我们回到我们的主要关注点&#xff1a;提供一种架构模型&#xff0c;该模型可以吸收我们在应用程序中可以期望的技术差异。

如我们所见&#xff0c;反馈循环是一种非常通用的模式。 这将帮助我们减轻混合技术的影响。 但是我们需要一种方法来以统一的方式声明反馈循环&#xff0c;而不管底层的React式框架或所选的UI技术如何。 这就是Spin发挥作用的地方。

Spin 是在基于Swift的应用程序中构建反馈循环的工具&#xff0c;无论您使用底层的React式编程框架还是使用任何Apple UI技术&#xff08;RxSwift&#xff0c;ReactiveSwift&#xff0c;Combin和UIKit&#xff0c;AppKit&#xff0c;SwiftUI&#xff09;&#xff0c;都可以使用统一语法。

让我们尝试通过构建一个调节两个整数以使其收敛到平均值的系统来旋转 &#xff08;就像某种系统可以调整立体声扬声器的左右音频通道以使其收敛到相同的电平一样&#xff09;。

我们需要一个用于状态的数据类型&#xff1a;

struct Levels {let left : Intlet right : Int
}

我们还将需要一种数据类型来描述要在Levels上执行的转换&#xff1a;

enum Event {case increaseLeftcase decreaseLeft case increaseRightcase decreaseRight
}

为了描述状态机控制过渡的过程&#xff0c;我们需要一个reducer函数&#xff1a;

func levelsReducer (currentLevels: Levels, event: Event) -> Levels {guard currentLevels. left !&#61; currentLevels. right else { return currentLevels }switch event {case .decreaseLeft:return Levels ( left : currentLevels. left - 1 , right : currentLevels. right )case .increaseLeft:return Levels ( left : currentLevels. left &#43; 1 , right : currentLevels. right )case .decreaseRight:return Levels ( left : currentLevels. left , right : currentLevels. right - 1 )case .increaseRight:return Levels ( left : currentLevels. left , right : currentLevels. right &#43; 1 )}
}

到目前为止&#xff0c;代码与特定的React式框架无关&#xff0c;这很棒。

让我们写两个对每个级别都有影响的反馈。

使用RxSwift &#xff1a;

func leftEffect (inputLevels: Levels) -> Observable {// this is the stop condition to our Spinguard inputLevels. left !&#61; inputLevels. right else { return .empty() }// this is the regulation for the left levelif inputLevels. left }func rightEffect (inputLevels: Levels) -> Observable {// this is the stop condition to our Spinguard inputLevels. left !&#61; inputLevels. right else { return .empty() }// this is the regulation for the right levelif inputLevels. right }

使用ReactiveSwift &#xff1a;

func leftEffect (inputLevels: Levels) -> SignalProducer {// this is the stop condition to our Spinguard inputLevels. left !&#61; inputLevels. right else { return .empty }// this is the regulation for the left levelif inputLevels. left }func rightEffect (inputLevels: Levels) -> SignalProducer {// this is the stop condition to our Spinguard inputLevels. left !&#61; inputLevels. right else { return .empty }// this is the regulation for the right levelif inputLevels. right }

合并 &#xff1a;

func leftEffect (inputLevels: Levels) -> AnyPublisher {// this is the stop condition to our Spinguard inputLevels. left !&#61; inputLevels. right else { return Empty ().eraseToAnyPublisher() }// this is the regulation for the left levelif inputLevels. left }func rightEffect (inputLevels: Levels) -> AnyPublisher {// this is the stop condition to our Spinguard inputLevels. left !&#61; inputLevels. right else { return Empty ().eraseToAnyPublisher() }// this is the regulation for the right levelif inputLevels. right }

无论您选择哪种React技术&#xff0c;编写反馈循环&#xff08;也称为Spin &#xff09;都非常简单&#xff1a;

let levelsSpin &#61; Spinner.initialState( Levels ( left : 10 , right : 20 )).feedback( Feedback (effect: leftEffect)).feedback( Feedback (effect: rightEffect)).reducer( Reducer (levelsReducer))

而已。 您可以在应用程序的一部分中使用RxSwift&#xff0c;在另一应用程序中使用Combine&#xff0c;所有反馈循环将使用相同的语法。

对于“类似于DSL”的语法爱好者&#xff0c;还有一种更具声明性的方式&#xff1a;

let levelsSpin &#61; Spin (initialState: Levels ( left : 10 , right : 20 ), reducer: Reducer (levelsReducer)) {Feedback (effect: leftEffect)Feedback (effect: rightEffect)
}

如何开始循环&#xff1f;

// With RxSwift
Observable.start(spin: levelsSpin).disposed(by: disposeBag)// With ReactiveSwift
SignalProducer.start(spin: levelsSpin).disposed(by: disposeBag)// With Combine
AnyPublisher.start(spin: levelsSpin).store( in : &cancellables)

混合React框架不再是问题issue。

在UI透视图中使用Spin

尽管反馈循环本身可以不存在任何可视化而存在&#xff0c;但在我们的开发人员世界中&#xff0c;将其用作产生将在屏幕上呈现的State的方式以及处理用户发出的事件的方式更有意义。

幸运的是&#xff0c;将状态作为输入来呈现和从用户交互返回事件流看起来就像是反馈的定义&#xff0c;并且我们知道如何处理反馈&#x1f601;&#xff0c;当然是自旋的。

一旦构建了Spin / feedback循环&#xff0c;我们就可以使用专门用于UI渲染/交互的新反馈来“修饰”它。 存在一种特殊的Spin来执行这种装饰&#xff1a;UISpin。

作为全局图片&#xff0c;我们可以使用此图说明UI上下文中的反馈循环&#xff1a;

在ViewController中&#xff0c;假设您有一个类似的渲染功能&#xff1a;

func render (state: State) {switch state {case .increasing( let value):self .counterLabel.text &#61; "\(value)"self .counterLabel.textColor &#61; .greencase .decreasing( let value):self .counterLabel.text &#61; "\(value)"self .counterLabel.textColor &#61; .red}
}

我们需要用UISpin装饰“商务”旋转&#xff08;例如&#xff0c;在viewDidLoad函数中&#xff09;。

// previously defined or injected: counterSpin is the Spin that handles our counter rules
self .uiSpin &#61; UISpin (spin: counterSpin)
// self.uiSpin is now able to handle UI side effects
// we now want to attach the UI Spin to the rendering function of the ViewController:
self .uiSpin.render(on: self , using: { $ 0 .render(state:) })
// And once the view is ready (in “viewDidLoad” function for instance) let’s start the loop:
self .uiSpin.start()
// the underlying reactive stream will be disposed once the uiSpin will be deinit

在循环中发送事件非常简单&#xff1b; 只需使用发出功能&#xff1a;

self .uiSpin.emit( Event .startCounter)

那么SwiftUI呢&#xff1f;

由于SwiftUI依赖于状态和视图之间的绑定的思想并负责渲染&#xff0c;因此连接SwiftUI Spin的方式略有不同&#xff0c;甚至更简单。

在您看来&#xff0c;您必须使用“ &#64;ObservedObject”注释SwiftUI Spin变量&#xff1a;

&#64;ObservedObject
private var uiSpin: SwiftUISpin &#61; {// previously defined or injected: counterSpin is the Spin that handles our counter businesslet spin &#61; SwiftUISpin (spin: counterSpin)spin.start()return spin
}()

然后&#xff0c;您可以在视图内使用“ uiSpin.state”属性显示数据&#xff0c;并使用uiSpin.emit&#xff08;&#xff09;发送事件。 由于SwiftUISpin也是一个“ ObservableObject”&#xff0c;因此每个状态突变都会触发视图渲染。

Button (action: {self .uiSpin.emit( Event .startCounter)
}) {Text ( "\(self.uiSpin.state.isCounterPaused ? " Start ": " Stop ")" )
}// A SwiftUISpin can also be used to produce SwiftUI bindings:
Toggle (isOn: self .uiSpin.binding( for : \.isPaused, event: .toggle) {Text ( "toggle" )
}// \.isPaused is a keypath which designates a sub state of the state,
// and .toggle is the event to emit when the Toggle is changed.

UIKit&#xff08;AppKit&#xff09;和SwiftUI使用UISpin的方式非常相似&#xff0c;允许您将先前为UIKit屏幕编写的反馈循环集成到新的SwiftUI组件中。

混合UI范例不再是问题&#x1f44d;。

结论

我们已经达到了目标&#xff1a;提出一种架构模式实现&#xff0c;可以简化新技术之间的过渡。

在Spinners组织中&#xff0c;您可以找到2个演示应用程序&#xff0c;这些应用程序演示了如何将Spin与RxSwift&#xff0c;ReactiveSwift和Combine结合使用。

  1. 基本的计数器应用程序&#xff1a; UIKit版本和SwiftUI版本
  2. 使用依赖项注入和协调器模式&#xff08;UIKit&#xff09;的更高级的“基于网络”应用程序&#xff1a; UIKit版本和SwiftUI版本

有Spin.Swift回购 。 公关当然是受欢迎的&#xff08;例如⭐️&#x1f60f;&#xff09;。

我打算开发与RxJava和Flow兼容的Kotlin实现&#xff08;不胜感激&#xff09;。

希望您喜欢这篇文章。 随时发表评论&#xff0c;以便我们进行交流。




翻译自: https://hackernoon.com/introducing-spin-a-universal-feedback-loop-system-in-swift-rg2i3yab

swift 引入pod



推荐阅读
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 设计模式——模板方法模式的应用和优缺点
    本文介绍了设计模式中的模板方法模式,包括其定义、应用、优点、缺点和使用场景。模板方法模式是一种基于继承的代码复用技术,通过将复杂流程的实现步骤封装在基本方法中,并在抽象父类中定义模板方法的执行次序,子类可以覆盖某些步骤,实现相同的算法框架的不同功能。该模式在软件开发中具有广泛的应用价值。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 提升Python编程效率的十点建议
    本文介绍了提升Python编程效率的十点建议,包括不使用分号、选择合适的代码编辑器、遵循Python代码规范等。这些建议可以帮助开发者节省时间,提高编程效率。同时,还提供了相关参考链接供读者深入学习。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
author-avatar
文女2010_532
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有