我们将探索依赖注入,重点是快速的iOS开发,但是这个概念适用于大多数面向对象的语言。 我们还将看到在iOS环境中应用DI的一些实际注意事项。 本文是我深入实施DI并学习各种实践,理论方面的结果。 什么是依赖注入? “依赖注入”是5美分概念的25美元术语”。 是关于DI的经常重复的格言。 从本质上讲,DI意味着尽可能地通过从代码外部提供对象来代替在代码内部创建对象 。 因此,这个词。 构造函数,属性,方法通常是我们创建对象的地方,可以从外部替换。 因此,我们最终得到3种类型的注射。 构造函数注入 资产注入 方法注入 我们将在“如何..?”部分中探讨这三种类型。 为什么要进行依赖注入? 让我们在一个场景的帮助下进行讨论。 场景-Koala Koder! 假设您有一个具有用户登录名的应用程序。 用户模型struct / class封装了登录的用户数据。 假设您是Koala Koder(一个像树袋熊一样懒惰的程序员)。 您也许提出了一个既快速又肮脏的解决方案。 将用户模型存储在NSUserDefaults中,然后通过属性获取它。 众所周知,苹果是如何喜欢单例类的,所以我们遵循它们使UserModel成为单例。 问题,到处都是问题… 1.单元测试员Vader罢工! 一位高级开发人员突然转为阴暗面,开始抱怨单元测试 。 他/她将不允许未经单元测试的应用通过代码审查。 2.出现疯狂的新用例! 如果这还不够,则应该支持一个新的用例 。 我们的应用程序现在应该支持多个用户 。 3.“让我们移动到” 现在,我们还达到了用户默认设置无法扩展的地步,希望迁移到新的数据序列化方法。 现在,甚至UserProfile中的我们的获取器和设置器也不安全。 为什么会有问题? 因此,我们发现自己陷入了严重的麻烦。 让我们分析为什么。 1.单身人士很难测试 现在,如果要对使用此单例的viewcontroller进行单元测试。 在单元测试中,您将创建一个UserProfile对象,手动调用viewDidLoad或其他方法。 现在,您必须验证是否调用了UserModel.sharedInstance.greet() 。 由于UserModel.sharedInstance是不可变的,因此我们不能用扩展UserModel的模拟类替换它,该类将覆盖greetUser并设置一个可以检查的标志。 因此,我们的测试范围下降了。 2.代码约束内部的实例化会产生强烈的耦合,从而产生有害的约束 在我们的方案中,通过使用单例,我们将代码库绑定到单个UserModel,但是现在我们的应用程序需要多个用户模型。 因此,一般而言,使用单例将很难适应新的用例。 您以为单身汉的想法突然不再那么单身了 […]
在学习Swift的过程中,我注意到一些函数要求使用Element类型的参数。 我有点被为什么要求我们提供一个类型变量感到困惑 我从未听说过像Array这样熟悉的东西…… 例如,让我们看一下在字符串 数组上调用的append()方法。 当我们使用自动填充来完成append函数时,系统会要求您提供一个参数: newElement:Element。 因为我们已经知道数组包含字符串,所以为什么类型不是String的参数呢? append方法将接受String并打印该方法,并将新字符串添加到Array的末尾。 那么,这是怎么回事? 为什么要求我为该方法提供一个Element,但是它将接受一个String ? 让我们再思考一下数组 。 我们创建的每个数组都有可用的append()方法,但并非所有数组都属于同一类型。 与其在String Array上使用append() ,不如在Int Array上尝试一下。 真好! 即使我们的数组类型从[String]更改为[Int],我们也可以使用Element类型来传递String或Int参数。 由于数组中各项的类型正在更改,因此使用Element可以根据数组中的内容传递正确的类型。 但仍然..什么是Element ? 这是一堂课吗? 结构? 事实证明,它被称为“通用代码”。 这里直接来自Apple文档: “通用代码使您可以编写灵活,可重用的函数和类型,这些函数和类型可以根据您定义的要求与任何类型一起使用。 您可以编写避免重复的代码,并以清晰抽象的方式表达其意图。”
iOS 10.3带来了一项甜蜜的新功能; 允许用户对应用进行评分,而无需访问App Store。 此新版本还允许开发人员在App Store上回应评论。 但是我们在这里谈论应用内评级。 因此,让我们看看它是什么样子! 它不仅漂亮,而且实现起来也非常简单。 这实际上是两行代码! 这是发生了什么事: 在第2行上,导入了StoreKit模块。 在第9行,将SKStoreReviewController.requestReview() 。 这将显示上面显示的评级提示。 此方法不带参数。 因此,目前无法更改提示上的文字! 就是这样。 您的应用现已具有应用内评分!
欢迎来到一系列致力于学习设计模式的文章。 尽管许多想法与代码无关,但我们的目标是向您展示如何在Swift中实现它们(在撰写本文时为Swift 3.0)。 每个帖子彼此独立,所有项目代码都可以 在Git上找到 。 策略模式允许您在运行时更改算法的行为。 使用接口,我们能够定义一系列算法,封装每个算法,并使它们可互换,从而允许我们选择在运行时执行哪种算法。 一个例子就是游戏中的角色。 我们的角色-好人-有3种状态:奔跑,步行和站立。 我们希望将状态与其他对象(即Bad Guy角色)一起重用,因此我们希望封装它们,并能够将状态与其他对象一起使用,从而减少了代码量。 我们可以使用UML图显示Good Guy角色如何工作: 首先,在Xcode中创建一个新项目。 我们不会在UIKit中碰任何东西,因此创建一个macOS Terminal项目。 首先,我们需要定义接口/协议(这是同一件事,但是Swift使用单词protocol)。 我们的基本接口定义了一种称为enterState()的方法。 它看起来像这样: 我们可以建立Good Guy类。 此类被设置为接收我们的状态并在我们传递给它的类中执行enterState()方法。 它看起来像这样: 然后我们可以建立我们的状态类。 我们将创建步行,跑步和站立状态。 这个想法是,好家伙将使用这些特征,但坏家伙也将能够在实现时通过实现CharacterState接口来使用它们。 它们非常基础,仅运行打印命令来表明我们已进入该状态。 这是我们各州的实现: 现在,在您的主类中,我们希望能够使用内置在其中的setState函数将状态传递给您的好家伙。 它看起来像这样: 我们将不同的状态传递给我们的Good Guy类,它将执行每个不同的(尽管是基本的)算法。 点击运行,您的输出将如下所示: 进入步行状态 进入站立状态 进入运行状态 程序以退出代码结束:0 欢呼! 您已经实现了策略模式。 奖励-坏家伙 因此,如上所述,“坏人”还可以利用“好人”的功能,而不必重复任何代码(“策略”模式的一大好处)。 创建一个名为BadGuy.swift的新类。 该类看起来与Good Guy类似,但您可以向其中添加特定的Bad Guy功能(例如杀死或射击)。 坏家伙看起来像这样: 为了说明它是如何工作的,在我们的主类中,我们将创建一个Bad Guy对象,并设置状态,就像我们对Good Guy所做的一样,因此现在看起来像这样: 运行它,您将得到以下结果: 进入步行状态 进入站立状态 进入运行状态 进入步行状态 […]
长期以来,我是一名Android开发人员。 我喜欢android XML布局编辑器,它很容易实现我想要的结果。 而且我可以在编辑器中轻松预览。 从android到iOS,都有一个等效的编辑器-XIB或情节提要及其布局系统-约束系统。 我个人不喜欢故事板,要在故事板上定义所有内容并不容易。 对于团队发展,有时会在编辑中产生冲突。 因此,我选择SnapKit作为我的布局创建工具。 SnapKit是最好的iOS库之一,我喜欢它的语法,但是,iOS约束系统本身仍然不容易完全理解。 我花了几周的时间研究约束系统,但其结果仍不总是我的期望。 这就是为什么我写了一个库来处理我通常会重复使用的某些情况的原因。 在snapKitten中,引用了Android的线性布局。 它完全基于SnapKit和Constraint系统。 目的是处理iOS布局设计的一些线性情况。 SnapKitten的目标 1.基于约束系统 2.解决线性布局,例如android中的LinearLayout 3.易于使用,如SnapKit 编码示例 考虑一下,我们需要一个用户配置文件布局,其中图像在左侧,用户名在右侧。 让userProfile:UIView = Kitten.horizontal()。from() .add(imageView).size(40) .add(label).itemOffset(10) 。建立() 这是怎么回事? 1.定义方向(水平/垂直) 2.在from()方法中选择userProfile的父UIView 3.小猫添加一个UIImageView和UILabel 4.在每个添加视图操作之后,您将定义线性布局的一些属性。 5. build()返回连接所有子视图的父UIView 小猫实际上是做什么的? 1.在调用build()方法之前,它什么都不做。 它没有添加任何视图,并且您的视图没有属性更改。 2.当调用build方法时,它开始在子代及其父容器之间建立约束。 3.包括位置对齐,大小,内容压缩,内容拥抱属性。 4.位置和大小与任何帧原点/大小无关。 纯约束。 更复杂的例子 考虑一下,我们需要一个视图控制器,它的用户配置文件顶部对齐,菜单的三个图标底部对齐,其余中间部分是scrollview。 让userProfile:UIView = Kitten.horizontal()。from()。defaultAlignment(.start) .add(iv).size(40) .add(lblA).itemOffset(10) 。建立() userProfile.backgroundColor = UIColor.orange 让bottomMenu = Kitten.horizontal()。from() .weightMode(true) […]
集合是一种集合类型(一种将相关项组合在一起的方式),类似于数组,但有一些区别。 集合将不同的值存储为无序列表,而数组是可以具有重复值的有序列表。 另外,一个集合只能采用符合Hashable协议的类型,这使得该集合可以维护一组独特的值,并使其在数组上的搜索和检索速度比数组快得多。 所有基础类型都是可哈希的,并且可以与集合一起使用。 如果要使用一组自己的类或结构,则需要实现Hashable协议。 何时使用套装! 在元素的顺序不重要的情况下,或者需要确保某项仅出现一次的情况下。 让我们仔细看看! 让我们选择一个问题,并尝试使用一组解决方案,一次使用数组解决问题,然后根据执行时间和迭代次数决定哪个更快。 我们有一本词典,其中包含有关州及其州首府的信息,并且我们想知道哪个州的州首府不包含与州名相同的字符。 我们将首先使用一组回答这个问题。 我们将从在字典中进行迭代并创建两组字符开始,一组用于州名,另一组用于大写名。 然后我们将检查相交的结果-集合的基本操作之一,确定两个集合具有哪些共同的值-然后我们可以得到问题的答案。 容易吧? 代码将是: 现在,让我们尝试通过为状态名称和大写字母的名称创建一个字符数组来解决该问题,然后在大写字母数组的内部循环以检查每个字符是否存在于相应状态的数组中,或者不。 此代码将是: 如我们所见: 这两个函数都会重复前两个步骤50次,因为我们的词典有50个键。 使用一组,我们能够在一行代码中执行比较,并且该行将针对每个状态执行一次。 使用数组,我们需要一个for循环,该循环将重复50次乘以每个大写字母名称中的字符数。 因此,很明显,使用集合比使用数组来解决这些类型的问题要快。 可以通过调用每个函数100次并测量每个函数的平均执行时间来证明这一点,如下面的屏幕截图所示。 尽管在我们的示例中运行时间的差异很小,但是当与大型数据集一起使用时,这可能会产生很大的差异,因此,请始终考虑使用集作为传统数组的替代方法。
嘿大家! 本周,我想谈谈我在应用程序中添加的一项功能,我认为它很酷,因此我决定为什么不与我的观众分享此功能。 我要指的功能是带有文本字段的UIAlertController。 添加警报控制器不仅是显示数据和错误的好方法,而且还有助于提示用户输入一些相关信息。 我想给你一个简短的教程,关于如何在UIAlertController内部实现文本字段。 让我们开始吧! 步骤1: 让警报= UIAlertController(标题:“大标题”,消息:“请输入内容”,preferredStyle:UIAlertControllerStyle.alert) 首先,我们需要创建UIAlertController的实例。 UIAlertController具有三个参数,分别是title , message和perferredStyle 。 perferredStyle带有两个选项,分别为alert和actionSheet 。 第二步: let action = UIAlertAction(title:“名称输入”,样式:.default){(alertAction)在 让textField = alert.textFields![0]作为UITextField } 接下来,我要创建UIAlertAction的实例。 UIAlertAction带有两个参数title和style 。 另外, UIAlertAction也有一个闭合块,我们将在其中添加文本字段。 如果愿意,我们可以添加其他文本字段,我们所要做的只是更改索引(它显示[0] )。 第三步: alert.addTextField {(textField)在 textField.placeholder =“输入您的名字” } 实现文本字段后,现在需要告诉UIAlertController添加文本字段。 在这里,我们可以选择使用文本字段的闭包块来完成其他工作。 在我的闭合块中,我决定添加一个占位符。 第四步: alert.addAction(动作) self.view.present(viewController,动画:true,完成:无) 最后,我希望我的UIAlertController添加在第二步中创建的动作。 接下来,我所要做的只是显示警报控制器。 这就是所有人。向警报控制器添加文本字段是一项容易的任务。 希望该帖子对一些不太熟悉该主题的观众有所帮助。
前iOS主管一直给我留下的一句话是始终追求卓越。 我本周每天醒来,在上班途中考虑这一点。 这周,我开始研究项目的一些屏幕和错误修复。 这是第二周我学到的一些东西: 一致的约束插入量/偏移量-使用一致的约束插入量和偏移量时,最好将它们包含在结构中,这样约束中就不会乱扔随机数。 例: 我得到的数据具有枚举类型,且其关联值均为“ asap”或Date类型,两者均为关联值。 我必须从数据中获取日期,但我一生都无法弄清楚。 访问没有关联值的枚举的通常方法是使用(.dotNotation)。 这对我不起作用,因为这不是您访问枚举的关联值的方式(请坚持这种想法)。 这导致我尝试了一些复杂的整理方法,将数据转换为字符串,结果是: 字符串“ date(“ 2018–07–07 11:00:00 +0000”)” 然后使用.dropFirst()和.dropLast()的字符串处理方法删除“ date()”部分,最终导致 字符串“ 2018–07–07 11:00:00 +0000” 但是由于某种原因,我尝试使用Date方法将此字符串转换为不起作用的日期….. 这是我终于向经理寻求帮助的时候。 他确认我的最初直觉是正确的, 我应该通过数据枚举的关联值来访问日期! ;-; tldr:有两种方法时: 一种简单的方法应该可以工作,但您无法弄清楚或 一个漫长而复杂的方法,这可能是您应该获得帮助的时候,因为这是浪费开发人员时间的方式。 如果我刚刚问这个问题,我可能会节省很多时间,这将花费一分钟使我走上正确的道路。 学过的知识! 无论如何,这是如何访问枚举的关联值: 通过打开枚举 通过大小写访问关联的值 3. 如何从Date()中完全拼出月份和月份的顺序日期 例如:日期(2018–07–07 11:00:00 +0000)—> 2018年7月7日 据我所知,Apple没有提供给您拼写出月份和顺序日期的方法。 所以这就是我的做法: 你这周学到了什么?
背景 在开发iOS应用程序时,您知道用于开发iOS应用程序的体系结构是MVC(模型-视图-控制器),每个应用程序组件按其职责分开。 在MVC架构上,模型负责提供数据或控制与数据访问有关的所有事物。 Person或PersonDataAccess类是模型的示例。 View负责将数据显示到用户界面。 并且,最后一个是Controller,负责更改模型并准备要呈现给View的数据。 根据Apple MVC的概念,视图将用户操作发送到控制器,然后控制器使用从视图发送的数据更新模型,然后模型将模型中的数据更改通知控制器,最后一个是控制器更新视图。 看起来很简单,对吧? 但是现实与上面的概念并不相同。 大多数iOS开发人员(包括me😂)倾向于制作Massive View Controller。 为什么将其视为问题? 因为视图和控制器紧密耦合,很难说它们是分开的。 在这种情况下, UIViewController子类被视为View。 因此,您倾向于将所有这些职责(网络调用,事件处理,数据更改,显示数据等)写入视图控制器( UIViewController子类)。 似乎易于实现,但有很多后果。 由于表示和数据操作合为一层,因此很难测试业务逻辑(视图) 难以维护 膨胀的UIViewController 继续… 由于以上所有这些问题,我开始学习一种替代的体系结构,该体系结构解决了这些问题。 认识MVVM 因此,这是视图控制器庞大性的拯救。 有一个名为ViewModel的组件。 那么,ViewModel的职责是什么? 就像MVC架构上的Controller一样,ViewModel负责更改模型并准备要呈现给视图的数据。 但是,有许多差异。 ViewModel提供了到View的数据绑定机制,因此,如果ViewModel上有数据更改,则View将自动更新,并且ViewModel没有对View的引用。 就个人而言,由于数据绑定机制,我喜欢这种体系结构,因此如果ViewModel发生更改,则View可以更新自身,并且由于职责分离,因此易于测试模型和业务逻辑。 因此,将MVVM应用于iOS开发的好处是 易于测试业务逻辑和模型 视图几乎是被动的,因为存在数据绑定,尽管如此,我们仍然使用视图控制器执行segue(作业,请分开此😂) 轻松更改UI而不弄乱业务逻辑 您可以将业务逻辑或网络调用放入ViewModel,这样视图控制器就变得不那么肿了 ViewModel的经验法则 无参考资料 没有import UIKit 没有引用任何UIKit的组件,例如UIView , UIButton , UITextField等 只是一个数据。 JSON,字典或其他数据结构 RxSwift ReactiveX提供的一种反应式编程框架。 这是Rx的Swift版本。 它依赖于可观察的模式,如果Observable发生了数据更改或事件,则观察者可以执行某些操作。 在这里可以找到RxSwift的更多详细信息。 在这里,我在MVVM体系结构上使用RxSwift提供从ViewModel到View的数据绑定,并在Model发生更改时通知ViewModel。 […]
在本文中,我们将探索Swift的一个非常简单但非常有趣且有用的功能,称为Autoclosures。 如果您不熟悉此概念,那么前几个问题可能是:那是什么? 和“正常”关闭有什么不同? 好吧,让我们从Swift.org语言指南中的定义开始 自动闭包是一种自动创建的闭包,用于包装将作为参数传递给函数的表达式。 它不带任何参数,并且在调用它时,它返回包装在其中的表达式的值。 这种语法上的便利性使您可以通过编写正则表达式而不是显式闭包来省略函数参数的花括号。 所以……基本上,自动闭包是封装表达式并返回该表达式的结果值的闭包。 如果您定义一个以自动闭包为参数的函数,例如,定义一个函数func f(_ x:@autoclosure()-> Int),则基本上可以使用与普通函数相同的语法调用该函数。像f(3)这样的Int类型,编译器确保将其包装在闭包内。 因此,对于调用方而言,它变化不大。 但是自动关闭的最大优点是它们使我们可以延迟表达式求值,因为该表达式仅在函数调用关闭之后才运行。 表达式的计算成本很高并且不能在函数内部使用时,可以很好地使用它。 说了这么多,让我们来看一个代码示例 这就是本文的全部,希望您喜欢🙂 如果您有任何意见或疑问,请告诉我。 我很高兴收到您的反馈feedback 您可以在Twitter上@ LucianoPassos11找到我。 感谢您阅读🙂 参考文献 Swift编程语言(Swift 4.2):语言指南:闭包。 https://docs.swift.org/swift-book/LanguageGuide/Closures.html