如今,我们在iOS中遇到问题。 代替代表模型视图控制器的MVC设计模式,我们有一种叫做Massive-View-Controller的疾病。 作为一项出色的“重构技能”,我们迅速采用了MVVM(模型-视图-视图模型)设计模式。 确实很好,这是毫无疑问的,但是让我们看一下iOS内部最常见的错误之一-精确地使用cell, UITableViewCell和UICollectionViewCell ,以及我们发现自己被“大规模”病毒感染的原因。 假设我们要在集合视图中展示用户: struct用户{ 变量名称:字符串 var imageUrl:字符串 } 我们这样定义集合视图单元格: 类UserCollectionViewCell:UICollectionViewCell { @IBOutlet弱var nameLabel:UILabel! @IBOutlet弱var profileImageView:UIImageView! } 在具有集合视图的视图控制器内部,我们将具有: 类UsersViewController:UIViewController { var users = [ 用户(名称:“ Djuro”,imageUrl:“ some_url”), 用户(名称:“ Alfirevic”,imageUrl:“ some_other_url”) ] } 扩展UsersViewController:UICollectionViewDataSource { func numberOfSections(在collectionView:UICollectionView中)-> Int { 返回1 } func collectionView(_ collectionView:UICollectionView,numberOfItemsInSection部分:Int)-> Int { 返回users.count } func collectionView(_ collectionView:UICollectionView,cellForItemAt indexPath:IndexPath)-> UICollectionViewCell […]
在Swift中,我们有引用类型(类)和值类型(结构,元组,枚举)。 值类型具有复制语义。 这意味着,如果您为变量分配值类型或将其作为参数传递给函数(除非它是inout参数),则将复制该值的基础数据。 您将拥有两个具有相同内容的值,但是分配在两个不同的内存地址中。 有关Apple博客上引用类型和值类型之间差异的更详细说明。 由于我们将要讨论写时复制,因此了解Swift值语义非常重要。 所以……开始吧 什么是写时复制? 在Swift中,当您具有较大的值类型并且必须将参数分配或作为参数传递给函数时,就性能而言,将其复制可能会非常昂贵,因为您必须将所有基础数据复制到内存中的另一个位置。 为了将问题最小化,Swift Standard库为某些值类型(例如Array)实现了这套机制,其中值仅在发生突变时才复制,即使在该值具有多个引用的情况下也才复制,因为如果该值是唯一引用的,不需要复制,可以在引用上进行更改。 因此,仅分配给变量或将Array传递给函数并不一定意味着它将被复制,并且确实可以提高性能。 真正重要的是要知道的是… 写时复制不是值类型的默认行为,它是在Swift标准库中针对某些类型(例如数组和集合)实施的。 因此,这意味着并非标准库中的每个值类型都具有此行为。 此外,除非您自己实现它,否则您创建的值类型没有它。 这是我将在下一节稍后讨论的内容。 让我们在实践中看一个例子: 这就是本文的全部,希望您喜欢🙂 如果您有任何意见或疑问,请告诉我。 我很高兴收到您的反馈feedback 您可以在Twitter上@ LucianoPassos11找到我。 感谢您阅读🙂
如果您已经构建了一段时间的iOS应用程序,则可能已经编写了一些View Models。 在本文中,我们将以一个视图模型为例,说明如何使用Swift枚举编写更安全,更易读和更可扩展的代码。 在我们的示例中,我们将构建一个必须表示三个状态的视图模型: 第一个状态是加载状态,表示在我们有任何内容要显示给用户之前正在进行的网络操作。 通常,向用户显示带有某种活动指示器的空白屏幕。 第二种状态是项目(即产品)的同类列表。 内容加载后将显示此状态。 第三状态是刷新状态,表示用户何时手动触发了正在显示的内容的刷新。 在这种状态下,我们希望继续显示以前加载的内容,但还向用户显示一些指示正在进行网络操作以获取新内容的指示。 我们可以构建一种表示这种情况的结构的方式如下: var viewModel:ViewModelstruct ViewModel { var isLoading:布尔 var isRefreshing:布尔 var content:[内容]? } 乍一看似乎很好,但是仔细检查后,很明显,我们的视图模型具有我们实际上不需要的几种状态。 例如,将isLoading和isRefreshing都设为true并没有多大意义,它们应该是互斥的。 您可能会认为这很好,因为您知道这些标志应该互斥,并且可以确保每次将值分配给一个时都将反向赋给另一个。 可能会带来摩擦的地方是不具备该知识的人来对此代码进行一些更改。 仅仅通过查看View模型上的属性,他们就可以假设isLoading和isRefreshing可以同时为true ,或者当isLoading为true时content可以包含元素,这不是我们的意图。 用数学术语来表示,如果我们将content视为布尔值(我们有内容或没有内容),则我们的视图模型具有2³= 8种可能的状态,与我们预期的三种状态相反。 输入枚举 我们如何阐明和执行我们对视图模型的规则? 我们想要实现的是一个数据结构,其可能的状态恰好反映了我们视图的预期状态。 使用Swift枚举,我们可以这样做: var viewModel: ViewModel枚举ViewModel { 装箱 装箱(内容:[内容]) 案例刷新(内容:[内容]) } 查看此内容,我们可以清楚地看到我们的视图应具有三种状态:加载内容,显示内容和刷新内容(但仍显示先前加载的内容)。 这使初次查看此视图模型的人更容易了解确切的预期状态。 当我们需要与我们的视图模型进行交互时,以前的代码看起来像这样: 如果viewModel.isLoading { //设置加载界面 } 否则,如果viewModel.isRefreshing { //如果让content = viewModel.content { […]
NavigationController对View,View to Presenter,Presenter to Interactor和Router都有很强的引用。 因此,我们让NavigationController包含所有模块。 结果,此解决方案使我们可以在将ViewController从NavigationController中删除后销毁所有模块。 TableViewDelegate和TableViewDataSource 我们建议使用的方法不是唯一可能的方法。 创建它是为了没有更好的东西。 让我们用一个例子来展示它。 这里有一系列的人类对象,它们是通过一些API异步接收的。 人类具有以下特性: –名称:字符串; –年龄:整数; –照片:UIImage; 为了显示信息,UITableView正在使用。 人的名字,年龄和照片被加载到一个单元格中。 VIPER原则之一说View是被动的,但是Presenter不能向View返回任何内容。 视图应具有设置器,Presenter用来显示信息。 问题在于它仅适用于已创建的对象,而不适用于动态创建的对象。 这意味着您不能为每个单元格写一个setter。 这是为什么? 首先,编写应用程序时,单元数是未知的。 其次,为相同的对象编写setter是愚蠢的。 解: 让我们为View内部Human的每个属性创建一个变量,然后为每个新变量添加一个setter。 接下来,在Presenter中创建一个方法,该方法从Interactor获取数据并将其设置为View中的“ currentHuman”变量: 这样,每个单元格都填充有正确的数据。 它破坏了原理,该原理说View除了对UIViews和Presenter的引用外不应有任何其他内容。 但是它遵循另一个规则,并且代码仍然可以测试。 PS:本文由BytePace移动开发人员撰写。 您可以在这里查看他的作品和其他文章:http://bytepace.com/
当今行业参与者是越来越有争议的苹果。 尽管Apple不做广告,但Apple与开源社区有着长期的牢固关系。 苹果为许多开源项目做出了贡献,因为它们将它们整合到iOS和新品牌的macOS中,更不用说Apple操作系统的支柱是FreeBSD,Mach Kernel和Darwin Kernel的混合,还有更多开放性像GNU Utils这样的源软件。 开放代码的好处有两方面,并且往往会反馈给自己。 首先,仅通过访问即可打开代码,从而使公众受益。 然后,作者受益,因为公众可以提出建议,甚至可能做出更改。 当公共论坛围绕该代码进行对话而使代码库变得更好时,它会引起更多关注。 谦卑的Linux内核就是这样开始并成为操作系统领域的主宰者。 因此,让我们看一下苹果顶级开源项目列表: Swift程式设计语言 2014年,Apple宣布其Swift编程语言震惊世界。 Swift是一种具有许多功能的现代编程语言。 考虑到它可以同时利用C和Objective-C库和框架,它的采用率无与伦比,并且拥有非常现成的库。 当苹果决定开源其新语言时,苹果再次让世界感到惊讶。 从那时起,Swift在Apple和Linux平台上开始流行。 WebKit WebKit最初于1998年作为KHTML发布,并且是KDE项目的一部分,已经存在了一段时间。 WebKit是渲染引擎,可为Safari(台式机和移动设备)以及Google Chrome(台式机和移动设备)提供支持。 WebKit在保持性能的同时提供了广泛的标准支持,这对于现代网站中的大量媒体至关重要。 WebKit是一项不断发展的强大技术。 ResearchKit和CareKit 这是两个框架,只是良好的意图。 ResearchKit是一个框架,可让医学专业人士开发能够以前所未有的方式准确跟踪和测量疾病的应用程序,并将其与CareKit结合使用,可将功能置于患者自己手中。 患者可以轻松地向医生提供有关医疗状况进展或衰退的每日更新。 这使患者以及医学研究界都受益。 这两个开源框架可能会彻底改变医学。 不难看出,苹果公司非常重视开源。 他们是主要的贡献者,而不仅仅是他们领导的项目。 一定要检查提供的链接,以查看Apple在其他方面的贡献以及其他公司的贡献。 您认为苹果对开源做出了足够的贡献吗? 您认为他们可以做得更好吗? 在下面的评论中,让我们知道您对Apple与开源的关系有何看法。 也许你喜欢 ☞iOS 10和Swift 3:从初学者到付费专业人士 ☞Swift 3基础知识和基础培训 ☞完整的iOS 10开发人员课程—构建21个应用程序
这适用于iOS App开发的初学者或各个阶段的所有开发人员,他们真的想回顾一下基础知识。 如今,所有iOS开发人员都在努力将其应用程序设计转变为MVVM设计模式,但是所有开发人员都知道为什么这样做吗? 还是您中的一员,谁正在更改模式,因为您的高级开发人员想要更改。 我会解释“ 为什么 ”,不用担心don 首先,我想让您知道UIKIT应用程序基于MVC(模型-视图-控制器)设计模式。 模型 –就像是您的应用程序的宝贵资源,就像您想要在应用程序上显示的数据,其应用程序的数据以及业务逻辑一样,可以帮助将数据修改为应用程序中更具表现力的方式。 视图 –主要处理用户界面,它是数据的可视界面。 控制器 –就像模型和视图之间的桥梁一样,它在适当的时候从模型和视图之间移动数据,反之亦然。 请查看下面由Apple提供的有关MVC设计模式如何工作的图像。 在了解了MVC的知识之后,让我们开始MVVM。 我将以最简单的方式来描述MVVM,MVVM只是MODEL-VIEW-VIEWMODEL。 在这种情况下, Viewmodel充当View和Model之间的桥梁。 在大多数情况下,业务逻辑也从Model插入,并在ViewModel中完成。 其次,在这种情况下, View仅用于发布内容,它仅显示分配给它及其子视图的内容,应显示的内容以及应如何显示的内容再次由ViewModel处理。 看起来ViewModel是此设计模式中的主角。 那么,为什么许多iOS开发人员更喜欢MVVM而不是UIKIT的MVC? 当涉及到Xcode Project时,当您尝试在UIKIT APP中创建ViewController时,将以如下所示的形式创建它: 您可以看到Controller是用自己的View创建的,它们就像密不可分,因此,如果您要对Controller进行单元测试,它实际上涉及View,那么模拟ViewController非常困难,并且您需要做很多事情不知道ViewController会发生这种情况,而我们实际上没有任何控制权。 最后,您实际上没有机会独立测试实际的业务逻辑。 在MVVM设计模式中,您实际上可以独立地测试业务逻辑,可以将业务逻辑与模型分离,可以分离数据的转换方式,以便在View中显示,并且模拟ViewModel真的很容易。 如何将给定的UIKIT的MVC设计转换为MVVM,请查看以下图像: 至 因此,为了将MVC更改为MVVM,您需要一个额外的文件,例如:ViewModel.swift并将所有逻辑拉入其中,只是将整个ViewController视为MVVM。 因此,在这里,ViewController的部分是发布或显示数据和视图。 如果ViewController(在MVVM中为视图)要发布某些内容,则它应该来自ViewModel,后者再处理来自Model的数据以将其提供给ViewController。 如果用户想要更新其数据,则用户将更新屏幕上的数据,并将该信息发送到ViewModel,后者将更新模型。 那是最终读者,希望大家喜欢这篇文章。 如果您有任何疑问或想念任何内容,请在💁下方评论。 朱丽叶编码器签字……
在移动开发中,测试应用程序是一项非常重要的任务,特别是当您的产品需要经常发货并且您不是一个人的团队时。 但是,即使自第一天起就不应该将持续集成作为目标,但我们都知道,在现实世界中,我们大多数人都会跳入已经由其他人启动并且具有许多未经测试的代码的项目。 考虑到这一点,这将是我的第一篇有关代码测试及其有关在Swift中测试UITableView组件的文章。 更具体地说,有关如何测试UITableViewDataSource。 我选择该主题的主要原因是我花了大部分工作时间在Swift中进行开发,并且UITableViews是所有iOS应用程序中使用的最基本的组件之一。 代码结构 有关此项目的代码可在此处找到,并且由一个包含UITableView的单屏应用程序组成。 由于UITableViews是非常复杂的组件,并且我们可以使用许多不同的方式,因此本示例重点介绍如何测试所需的UITableViewDataSource方法,这些方法是: 如果下载并运行代码,您将看到一个空行的屏幕,该屏幕可滚动,但没有太多显示。 我曾经认为我需要查看自己所做的所有事情才能正确测试它。 一段时间后,我意识到可以分离逻辑和UI并分别对其进行测试。 这听起来像是显而易见的推理,但有时实践并不像理论那么容易。 TL; DR如果您对测试部分比对项目本身更感兴趣,请参考“ 测试”部分 。 AppDelegate.swift 这是我们应用程序的起点。 它有很多样板,通常我们不需要触摸它就可以开始开发任何东西。 但是,在这种情况下,我想将UIViewController与情节提要隔离,因此我插入了一个函数来手动设置RootViewController : 和功能: 该函数的作用是创建一个TodoListViewController实例并将其设置为我们的窗口rootViewController 。 通过这样做,我们将应用程序直接加载到我们要测试的UIViewController中 。 作为本文的补充,我们还将看到如何使用UI Test来测试是否正确加载了初始视图控制器。 TodoListAdapter.swift 由于我们要正确测试UITableViewDataSource ,因此最好将其与UIViewController分离。 为此,我们创建一个单独的类来实现数据源协议。 该适配器类也可以用于实现UITableView的委托,但这超出了本文的范围。 现在,让我们集中讨论如何实现数据源。 由于我们的适配器的主要目标是实现UITableViewDataSource协议,因此我们需要将该接口添加到适配器的声明中。 我们还需要添加一个NSObject 接口,因为UITableViewDataSource 需要符合NSObjectProtocol 太: 常数 为了能够利用iOS内存优化的优势,我们应该为UITableViewCells分配一个标识符,该标识符将使它们在需要时出队并再次使用,而不是每次都进行新分配。 由于我们的适配器将负责管理UITableView内容,因此让我们在其中添加一个常量,使我们可以在具有相同标识符的代码库之间保持一致。 变数 由于我们想将数据与UIViewController分离,所以我们创建一个变量以将我们的数据存储在适配器中,并创建一个适当的初始化器来接收该数据: 这样,每次创建适配器时,我们都可以为其分配用于呈现UITableView的数据 。 另外,这对于测试非常有用,因为我们可以根据需要在适配器内部直接模拟数据。 为简单起见,我们的数据将为Strings数组。 在以后的文章中,我将更好地解释如何在适配器内处理不同的数据类型,但是现在使用Strings就足够了。 UITableViewDataSource 从根本上说,在测试UITableViewDataSource时,我们希望能够检查数据是否正确加载。 就像我之前说过的那样,有两种方法需要实现: tableView(UITableView,numberOfRowsInSection:Int) 此方法负责定义UITableView具有的数据的行数。 由于我们要在表视图中显示所有数据,因此我们将使其返回数据数组计数。 […]
对于本文,我们决定为Marvel API编写一个客户端。 在深入研究代码之前,让我们解释一下为什么选择它: 它是开放的 。 去获得一个API密钥! 它很大 。 否则,为什么要建立一个可扩展的客户端? 是真的 。 迫使我们处理丑陋的现实细节。 这是一致的 。 好的,我们不需要太多丑陋的细节,我们也需要系统的实现。 它有一个很棒的API测试器 。 文档非常丰富,此在线工具使测试我们的代码变得非常容易。 太酷了 。 😎 在本文的其余部分,我们将使用Swift Playgrounds对该API进行试验。 如果您真的想对它有用,请推荐使用Karumi的MarvelApiClient。 功能性无状态核心周围的有状态外壳 定义组件接口时,一个很好的规则是使您的设计基于无状态类型,且其值语义不会产生任何副作用。 拥有该核心后,您可以创建另一个将这些类型映射到实际副作用的类型。 我们的API客户端组件将围绕三种类型构建: APIRequest :将创建JSON请求的值类型。 APIResponse :将从JSON响应中创建的值类型。 APIClient :将接收请求,将其发送到服务器,然后通过回调通知调用方。 最后一种似乎比其他类型更混乱! 好吧,这就是我们的有状态外壳,它将是处理这种混乱状态的外壳。 实际上,该组件将与大多数细节无关,而您几乎不需要更改它。 APIClient初始接口 基于先前的想法,我们可能会从以下内容开始: 让我们从更改APIRequest协议开始: Decodable协议为我们做了很多工作! 您是否注意到ID是非可选的? 因此,如果我们遇到格式不正确的ComicCharacter ,则会引发错误,并且我们将能够以我们认为合适的任何方式对其进行处理。 但是,那里还有另一种类型。 查看Image类型? 在任何Apple框架中都没有定义,这是我们定义的自定义类型。 让我们看一下它的代码: 由于某种原因,Marvel API决定发送分为路径和扩展名的图像URL。 我们不希望将此解析细节公开给我们的业务逻辑层,因此我们尝试从该输入中构建正确的URL。 由于我们的输入和输出不匹配,因此我们必须走很长的路,并完全实现Decodable 。 这意味着定义一个CodingKey枚举和一个init 。 […]
这篇文章提出了一种同步处理网络的方法。 它开始回顾当今如何异步处理网络,继续检测异步性的源头,并以展示如何删除它如何改善可伸缩性,可测试性,表达性,模块化和线程控制为结尾。 如今联网 当作为移动开发人员,我们需要从服务器获取数据并将其显示在设备中时,我们遵循的方法不会冻结UI: 我们在主线程中显示了一个动画微调器; 我们在另一个线程中请求数据; 当请求完成时,我们返回主线程; 我们删除了微调器; 我们使用接收到的数据刷新UI。 我们获取和呈现数据而不冻结UI的方法。 数据来自以下服务: 使用NSURLSession,Alamofire或其他机制发送HTTP请求; 使用本机工具,SwiftyJSON或其他库解析HTTP响应; 将结果(事物数组或错误)统一为两个可选参数或结果monad; 使用回调机制,作为参数接收的函数或先前分配的委托返回结果。 一种可能的服务实现,它使用回调来返回获取的数据(或错误)。 异步的根源 从网络获取数据的任何组件都必须异步处理,以避免冻结应用程序; 但是,应该在API级别包括异步性吗? 我们以后不能处理吗? API级别的异步性迫使我们立即对其进行处理; 我们无法决定何时处理。 如果我们注意前面的代码段,则在发送HTTP请求时会出现异步性。 我们丢失了流控制,并在调用作为参数传递的函数时将其恢复。 然后,如果我们在另一个线程上,则返回主线程以更新UI。 吻:保持同步,s **** d! 让我们删除异步源,以异步方式转换异步服务API: 自iOS 9以来,没有很多库向我们提供同步请求,并且不赞成使用本机执行同步请求的方法,但是我们可以使用本机SDK解决该问题。 首先,它看起来非常简单:您想要的东西,可以调用getThings() ,您将收到您想要的东西(或错误)作为返回值。 但是,如果您在主线程中调用它,则该应用程序将冻结,直到我们收到对HTTP请求的响应。 因此,现在该处理它了: 我们使用此辅助函数来提高可读性和模块化性,但是我们可以直接使用SDK,使用任何async-await库,任何Futures库或继续期待Swift 5。 这样,我们选择在后台线程中执行服务以避免冻结应用程序。 一些优点是: 该代码更简单,语义上也更好。 我们索要一些东西,然后得到它作为……一个返回值☺,而不是作为回调函数或委托方法中的参数(参数用于发送事物,而不是返回它们)。 我们在控制线程。 我们决定在后台线程中以有效意图执行某些代码(不阻塞UI),我们决定以另一个明确定义的目的返回主线程(更新UI)。 如果不需要,我们不在乎切换线程。 我们可以通过一种更简单的方式伪造服务,无需在延迟后调用回调,只需将所需的值作为返回值返回即可。 我们可以以更舒适的方式测试服务,无需期望,因为在运行测试时没有UI线程会阻塞,因此您可以在当前线程中同步使用服务。 重要的是要注意,这些测试是集成测试,而不是单元测试。 使用服务时,我们是在测试环境(在最佳情况下为此类测试专用的环境)中创建/修改/删除实体。 我们可以以可扩展的方式编写服务呼叫。 一些缺点是: 以非标准方式处理网络。 没有同步发送网络请求的直接方法。 结论 软件组件的API定义了其使用方式。 如果我们在组件内部处理异步性,并添加回调函数作为参数,那么我们将迫使使用API的任何人立即处理异步性。 […]
我真的很高兴宣布发布我的新课程“ 使用蒸气3掌握服务器端Swift ”。 Swift是在WWDC 2014上宣布的,它席卷了整个世界。 除了编写iOS,Mac,Apple Watch和Apple TV应用程序外,您还可以使用Swift语言编写完整的后端。 在本课程中,您将学习如何使用Vapor 3框架将现有的Swift技能带到云中。 您只需9.99美元即可获得完整的课程。 让我们检查一下课程内容: 了解路线 在本节中,您将学习有关路线的所有信息。 这包括将参数传递到路由以及查询字符串。 我还将解释如何使用分组和嵌套路由来清晰地构建路由。 JSON格式 Vapor 3使获取和发布JSON值变得非常容易。 在本节中,您将学习如何使用Vapor框架中定义的帮助程序协议将自定义类转换为JSON表示形式。 期货 蒸气3与蒸气1和蒸气2框架完全不同。 在本节中,您将学习期货。 期货允许执行异步响应,从而使您的服务器可以立即返回响应。 SQL Fluent提供程序 在本部分中,您将学习如何将Vapor 3应用程序与SQLite3数据库集成。 我正在演示如何使用SQL Fluent Provider从数据库中获取数据。 从数据库返回的数据/记录将作为JSON响应发送给用户。 控制器 在本节中,您将学习如何通过实现Controller更好地组织代码。 控制器充当视图和模型之间的中间人。 开发大型应用程序时,控制器变得至关重要。 将Vapor Web API与iPhone应用程序集成 在本部分中,您将把Web API与iPhone应用程序集成。 这将包括通过iPhone应用程序创建新记录,获取记录,过滤和删除记录。 我真的希望您喜欢这门课程! 我已经开始研究本课程的未来更新。 非常感谢您的评分和评价。 谢谢,喜欢这个课程, Azam