Tag: swift

戴夫·托马斯(Save Swift)

本系列文章展示了我对Dave Thomas的代码集的思考过程和Swift解决方案(正在进行中)。 在空手道中,kata是您不断练习以取得成功的一系列编排动作。 还记得丹尼尔森是如何做到的? 游戏时间 好吧,这是数千个小时。 练习时间 这些解决方案针对可读性,可测试性和可维护性进行了优化。 我的编码风格强调变量,类型和方法名称的简洁性。 我希望能够回到6个月前编写的代码,或多或少了解我在做什么。 我希望能够自信地重构一种方法,因为我没有破坏任何东西。 这是GitHub存储库,其中包含所有已完成的练习。 我很想听听您对这些方法的看法,并很高兴在评论中与您讨论。 卡塔斯 Kata04:数据整理-第1部分,第2部分,第3部分 Kata18:传递依存关系

自上而下的iOS错误架构

关于iOS生态系统中的错误处理的文章并不多,即使对于每个应用程序来说,这似乎都不是简单而实质的组成部分。 今天,我希望解决这个问题,并提出我的解决方案,以减少iOS应用程序中的破坏性错误处理。 初始状态 为了确保我们在同一个页面上,通过错误我将表示一个符合Swift的Error类型的实例,并且错误处理是对此的一种反应。 这些错误可能以同步方式(磁盘操作,无效参数)或异步方式 (没有Internet连接,会话令牌无效等)到达。 显然,由于Swift的标准do – catch结构,第一组更容易处理。 通常,简单的自下而上的过程就足够了,其中错误会中断执行并自动将其传递给调用方。 如果您具有带有完成处理程序的异步API,则开发人员可以自由选择众多技术(通常也遵循自下而上的模型)(提及Promise或RX流)。 通常,自下而上意味着如果我们无法完全处理错误,则会将错误传播到父层。 我发现,在许多情况下,自下而上的方法不是最佳方法,当错误必须遍历许多层以达到能够消耗它的目的时,就会导致沮丧。 举一个例子,假设APIClient收到401 Unauthorised HTTP响应,并且您的应用应返回到初始登录屏幕。 您是否熟悉ViewModel中的完成处理程序是否能够处理特定错误,是否不将其传递给其Flow Coordinator,然后将其传递给父级Flow Coordinator以最终到达通用位置,是否听起来很熟悉?您指示导航控制器移回根目录。 它最终以一个代码打开,该代码可以广泛地打开错误,例如: 如果您为不同元素之间的通信选择了委托机制,则委托协议可能会开始膨胀-每个协调器都应包括其嵌套子代的错误事件。 我观察到,在许多应用程序中,树的上层只能处理某些特定错误(例如会话无效或不受支持的API版本),并且在下面是一个聚集点,所有错误均以警报视图或类似形式呈现给UI。 )。 因此,我建议不要从底层开始,而是还原传播顺序,并从顶层开始并仅在父层无法处理时将其向下传递。 请注意,在根元素中,我们获得了应用程序中所有错误的单个聚集点,这对于分析目的可能是有用的。 因此,让我们将错误处理过程分为两个阶段: 阶段1:将事件传播到其父对象(无条件) 阶段2:逐层向下传播事件,找到可以处理事件的事件 这样的过程并不是一个全新的想法,例如JavaScript使用了一种非常类似的技术,称为冒泡事件委托,将触摸事件分配给DOM元素。 协议定义 实现自上而下的错误处理体系结构并不是一项艰巨的任务。 让我向您展示,您可以使用面向扩展开放协议的面向通用协议的解决方案轻松地在50行代码中实现它。 出于本文的目的,我将使用受Swift关键字列表( catch , throw , finally )启发的函数名称。 尽管你们中的一些人可能会觉得有点争议,但我相信这样的类比有助于理解它。 我们将定义唯一的协议ErrorHandleable ,它将用作错误处理树中的节点。 每个实例都保留对第1阶段传播中使用的父层的引用,并封装第2阶段中使用的通用闭包( HandleAction ),该闭包决定是否应将其填充(引发错误)。 此外,没有什么可以阻止在操作块中重写错误。 只需抛出一个全新的错误(例如在errorHandler#1中),所有下面的层将观察到已修改的错误。 但是,请记住,强大的力量伴随着巨大的责任,请谨慎使用此功能… 该协议API非常通用,因此我们可以添加一些扩展以方便使用: 约束将错误捕获为通用类型并透明地传递所有其他类型:

探索容器视图

我刚刚完成了一个应用程序,在该应用程序中,用户使用个人电子邮件和密码登录,如果用户已经登录但尚未注销,则希望该应用程序直接打开主供稿。 为了实现这一点,我使用了一个视图控制器,该控制器将负责控制哪个视图将是第一个,并将其称为“应用程序控制器”。 为了使App Controller具有此功能,我们需要执行一些步骤,因此让我们开始构建这个惊人的View Controller。 首先,我们需要将容器视图添加到App Controller的视图中。 容器视图在视图控制器的视图子图中定义一个区域,该区域可以包含子视图控制器。 这意味着,我们正在创建一个父子容器关系,其中父是我们的App Controller,而容器视图对象是一个占位符对象,代表一个子视图控制器的内容。 在添加任何子级之前,我们必须管理容器视图的大小和位置,因为它将反映在子级属性上。 在我的应用程序中,我将容器视图限制为应用程序控制器视图的边缘,因此任何子项都将在整个屏幕上显示。 其次,我使用了通知中心来控制从一个孩子到另一个孩子的切换,因此我在App Controller中添加了通知观察者,其中每个观察者的选择器是一个将实现切换的功能。 添加通知观察者的代码为: 该代码的第一部分具有两个功能。 第一个是不言自明的,使用通知的名称切换视图控制器,这里有两种情况,要么关闭登录VC,然后打开主VC,反之亦然。 为了执行此切换,我分别调用了另一个功能,以使用情节提要ID进行关闭和打开过程。 此功能是代码段“ switchToViewController(with id:StoryboardID)”中的私有功能。 根据Apple的文档格式,从旧VC到新VC的过渡周期包括两个步骤,将子View Controller添加到您的内容中,然后删除子View Controller。 在此过渡周期中,我们需要做的第一件事是通过调用值为nil的老孩子的willMoveToParentViewController:方法来准备用于更改的两个视图控制器。 willMoveToParentViewController: 在将视图控制器添加到容器视图控制器或从容器视图控制器中删除之前调用。 讨论区 当您的视图控制器需要知道已将其添加到容器时,可以重写此方法。 如果要实现自己的容器视图控制器,则必须在调用removeFromParentViewController方法之前,先传递子视图控制器的willMoveToParentViewController:方法,并传入父值nil 。 当您的自定义容器调用addChildViewController:方法时,它将在添加之前自动调用视图控制器的willMoveToParentViewController:方法作为子级添加。 现在,让我们开始添加子步骤。 为了使代码更简洁,我创建了两个单独的函数,loadViewController(withID:)使用情节提要ID实例化视图控制器,以及add(viewController:),它们将按照Apple文档中的说明实现添加过程,如下一个片段。 让我们在第二个函数中逐行进行,以了解如何将child添加到我们的容器视图中。 在第一行中,我们称为方法addChildViewController,该方法负责创建父子关系。 然后将新VC的视图添加到容器视图,并指定大小和位置。 最后,调用didMoveToParentViewController :,在这种情况下,父对象将是App Controller。 为了实现删除过程,我们需要通过调用view.removeFromSuperview()从容器的视图中删除子级的根视图,然后调用子级的removeFromParentViewController方法以最终确定父子关系的结尾。 最后的最后一步,调用新孩子的didMoveToParentViewController :,其中父VC是App Controller 完成所有这些步骤之后,我创建了一个函数,该函数将处理用于管理用户打开应用程序时应显示哪个VC的逻辑。 就我而言,由于我正在使用Firebase Auth,因此我将检查当前用户是否为nil,然后显示主VC,否则将显示登录VC。 除了App Controller的viewDidLoad()内的addNotificationObservers函数外,我还调用了此函数。 当用户单击登录按钮时,在我的登录VC内,将发布一个通知以关闭登录VC,而当用户单击注销按钮时,在主VC内将发布一个关闭主VC的通知。 在这两种情况下,App控制器都将监听这些通知。 这只是使用容器视图的一个简单示例,可用于段控制,导航控制器或选项卡控制器。 多亏了我出色的讲师Jim和Joel,以及Apple文档。

最快捷的包装

每个程序员都听说过JSON格式。 很好,易于理解,可以采用。 在大多数情况下,它用于服务器和客户端应用程序之间的通信。 但是……您可能会意识到,当您拥有大数据集以进行快速传输时,这不是最佳选择,无法真正对本机模型进行快速序列化或反序列化。 因此,还有其他一些但尚未流行的格式。 例如,谷歌使用的格式叫做Flatbuffers 。 最初,他们是为游戏开发和其他性能关键型应用程序创建的。 现在,Facebook将其用于数据传输。 在我们公司中,我们讨论了在客户端和服务器端进行JSON替换应采用的最佳格式。 由于体积小和性能好,我们观察到了MessagePack格式。 我已经在github上访问了已知的MessagePack实现,以便将其集成到我们的swift项目中。 经过一番苦苦挣扎和几次卡纸试验后,我终于找到了a2消息打包器 。 打包和解压缩我们复杂的结构和类层次结构变得微不足道。 有关如何使用此打包程序的完整而详细的技术教程,请参见Wiki github页面 。 集成后,我们具有以下性能: 优化标记=快速,iPhone 4,JSON文件大小2.7 MB(zip 190kB) 从JSON文件加载NSData = 0.0921s 将NSData转换为JSON对象= 1.203s JSON转换为本机模型= 3.2691s 原始模型打包到字节数组= 3.051s 字节数组到NSData = 0.0033s NSData到Byte数组= 0.00390s 本地模型的字节数组= 2.6518s 将NSData写入文件= 0.101s 为了进行测试,我们有一个大的JSON文件。 字节数组表示以MessagePack格式打包的数据。 将字节数组转换为原始模型的速度明显快于将标准JSON对象转换为原始模型的速度。 swift中的JSON对象表示实际上是NSDictionary或NSArray。 而且,打包的模型可以轻松转换为NSData并写入文件。 打包数据的文件大小约为743kB,比原始JSON文件小约3.5倍。 它被压缩到200kB,这与压缩后的JSON文件的大小类似。 我相信,通过一些技巧,MessagePack模块可以大大加快速度,就像在FlatBuffers格式中所做的那样,正如您在本文中所看到的。 快速应用程序中的本机模型应用程序也可以使用系统基础上的NSKeyedArchiever类保存到文件中,但事实证明,它比同样的MessagePack-ing慢几倍。 这种打包概念使您可以从Cocoa框架和NSObject类轻松地编写代码。 MessagePack格式的唯一缺陷是它的无方案。 打包数据投入生产后,很难添加或删除属性。 无论如何,MessagePack都很棒。 所以,去吧。 […]

Swift 3中的属性(@ IBOutlet,@ escaping,@ UIApplicationMain,@ available)

快速编码时,您可能在某处遇到了@符号,这意味着您遇到了一个属性 。 快速语言内置了属性,以提供有关声明或类型的更多信息。 由于它们是内置的,因此您无需编写自己的属性,尽管它们有时可以采用参数。 使用属性类似于使用关键字(即,类,var,协议)—它们具有非常特定的实用程序,但它们并不相同。 主要区别在于关键字告诉编译器您正在定义的内容,而属性则告诉编译器您正在使用它的上下文。 在此示例中,关键字“ var ”表示您要创建一个新变量,而属性“ @IBOutlet”表示您要将该变量与接口生成器同步。 @IBOutlet允许您将代码连接到情节提要 ,并在代码中更改UILabel的属性。 还可以应用其他三个属性,以允许不同的声明与接口生成器同步。 您可以在变量声明上使用@IBInspectable 以允许在检查器面板中查看和编辑变量。 @IBAction用于方法声明,以将方法功能连接到接口构建器中的对象。 最后, @ IBDesignable可以应用于类声明,以允许在界面生成器中呈现类属性,而无需运行模拟器。 请记住,所有这些属性都是特定于“界面”构建器的,因此仅适用于UIKit中的类型。 其他例子 有关swift提供的所有属性的完整列表,可以查看此页面。 我将在这里复习一些常见的知识。 如果在创建项目后查看AppDelegate.swift文件,应该会看到@UIApplicationMain属性应用于您的AppDelegate类。 请记住,属性可以应用于任何类型的声明,包括类声明。 此特定属性为您的应用程序创建一个入口点,并指定应用程序委托。 这等效于在main.swift文件中调用UIApplicationMain(_:_:_ 🙂 。

iOS中的队列多线程

要创建吸引用户每天使用它的iOS应用,精美的设计必不可少,因此应用中的快速体验也至关重要。 几个月前,我下载了一个教您如何烹饪的应用程序。 我在使用它的10分钟内删除了它。 为什么? 图片无法加载,我被卡在加载标牌上很长时间(例如1-2分钟?)。 这很令人沮丧,因为我想看看教程中的菜式。 好吧,这没有发生。 那么到底需要发生什么呢? 排队是有益的,也是麻烦的 iPhone内有一个CPU(中央处理单元)。 CPU一次只能执行一项任务。 待处理的任务排成一行 。 将排队视为排队等候餐厅座位的人。 当服务员有空位时,将排在第一位。 先进先出(FIFO)。 那么,我们的手机又如何如此迅速地响应我们的触摸和动作? 答案是:多线程。 多线程允许处理器创建并发线程以便在处理器之间进行切换。 结果,可以同时执行多个任务。 每个线程都有自己的队列。 就像剧院票房的台词一样。 一条线供客户购买机票,另一条线供客户打印预订的机票。 人们在每一行同时进出。 相同的概念适用于iOS生态系统。 如果我们在应用程序内部具有多线程,则不会发生像烹饪应用程序这样的悲剧。 我们不必从服务器获取配方,而不必等待它完成然后显示图像。 让我们看看我们如何做到这一点! 在iOS中引入Grand Central Dispatch(GCD) Grand Central Dispatch将任务分配给不同的线程(不同的队列)。 例如,主线程负责从服务器获取配方数据,而后台线程将更新UI并显示图像。 因此,该应用程序保持响应状态。 很好,对吗? GCD API围绕 DispatchQueue 。 此类使我们能够创建/修改队列,以及同步或异步分配代码 和更多。 同步方式 为了。 当您执行同步操作时,以后出现的所有内容都必须等待上一个操作完成才能开始。 另一方面,当您异步执行某项操作时,以下代码可以立即运行。 在其他队列上启动任务后,它将立即将控件返回到当前队列,而无需等待其结束。 异步方法不会阻止当前队列上的任务执行。 如您所见,当队列给我们带来麻烦时,我们可以通过它的变体来巧妙地处理它。 请继续关注第二部分–代码中的GCD实现!

iOS 10,Swift 3和Xcode 8即将面世-您准备好了吗?

我们真的有令人兴奋的消息! 您可能已经知道,iOS 10即将发布。 正在进行一些相当大的更改-如果您担心被遗忘,我们可以理解。 那就是我们进来的地方。 Udemy最好的两名讲师Rob Percival和Mark Price为您提供了涵盖iOS 10各个方面的全新课程。 罗布(Rob)是一位自称是编码专家的人,曾是一名专职老师,亲自发现了 成为Web开发人员带来的自由。 他辞去工作,现在经营Eco Web Hosting(超级流行的Web托管和设计服务),并在夏季教孩子们编写代码。 如果那还不能解决您的问题,那么他的完整Web开发人员课程将是Udemy收视率最高的课程-有史以来。 在他的课程中,您将通过学习来学习。 您马上就可以(实际上)弄脏手,学习Swift和Xcode,然后继续使用动画和地理定位等更高级的功能。 在整个课程中,您将立即了解市场上成功和流行的应用程序的幕后花絮。 制作应用后,您还将获得有关如何推销这些应用的绝妙提示! 马克(Mark)除了是喜欢从iOS学习的应用程序开发人员的最爱之外,还拥有两家非常成功的技术咨询公司,并建造了令人难以置信的57个移动应用程序。 他遵循自己的热情,这是为了帮助人们对编程充满信心,并揭示他们从未拥有过的隐藏的才华。 他拥有从初创公司到顶级公司的各种类型的公司的经验。 在Mark的课程中,您将学习使用iOS 10进行编码的最重要和最实际的方面-客户正在寻找什么以及制作出色应用程序所需的技能。 凭借他丰富的应用程序开发和教学经验,他知道您需要学习什么以及如何最好地帮助您学习。 您将非常有能力。 大松一口气吧? 您也不必等待数周的时间-这些课程将于7月27日发布! 我们知道,那很快。 但是我们想提醒您,因为那是朋友的目的。 课程启动时请密切注意我们即将发布的电子邮件(包括限时折扣-最高可享受50%的折扣!),并花一些时间在日历上以获取有关新操作系统的消息。 同时,请在此处查看有关课程的更多信息: –完整的iOS 10开发人员课程—构建21个应用程序 –最佳iOS 10和Swift 3教程 https://www.youtube.com/watch?v=G2o6qY_NqiY

核心图形第1部分

Core Graphics是IOS内建的绘图框架,当系统内建的样式无法满足您的需求时,可以拿一张图丢进元件中代替原本的图样,或者用Core Graphics绘制出需要的样式。 当你想要在一个元件上做绘图时,你需要在draw(_ rect:CGRect)里面添加Core Graphic的程序代码,以下使用UIView作为示例: 这是一个UIBezierPath已经内建的方法椭圆形,但路径本身并不会有任何动作,所以需要一个fill来填满这个封闭的路径,当然也可以改变它的颜色。 在这边如果要看到你每一次修改的结果,可能会觉得很麻烦需要一直重新构建你的模拟器,Xcode里面有个属性@IBDesignable可以让Storyboard中的查看及时更新。 除了填满(fill)的方法之外,还有无填满(stroke)的方法: 在上面的示例可以看到因为有线宽的关系,在边界会被切掉一些,所以试图不要让图形完全贴合在视图的边上,尤其是在无填满的状况下尤其明显。 上面的例子都是一个完整的图形,那若是要画线条呢?在UIBezierPath的有移动与addLine的方法: 利用前面介绍过的方法应该能够组合出很简单的图形 在Core Graphic上绘图都是一层一上上去的,如果换个顺序,把最细长的矩形quad往下放时 以下补充一些方法 这些方法都很常在图片中出现,可以自己尝试看效果会是某种子

Swift用Swift编写的AST。 ∞的第5部分

在上一部分中,我介绍了函数声明。 我开始简化AST需求的语法。 使结果类型尽可能易于使用。 在这一部分中,我将介绍枚举声明。 通常,让我们看一下语法部分: 首先,令人惊讶的是,原始值枚举具有特殊的语法。 实际上,这是两个不同的声明。 枚举声明本身只是2之一。 因此,让我们首先对它们进行建模。 联合枚举声明 联合枚举由一个可选的间接修饰符,名称,可选的通用参数,可选的类型继承,可选的通用约束和成员列表组成。 结构,我选择你! struct UnionEnumDeclaration { 让间接:间接? 命名:标识符 让genericParameters:GenericParameterClause? 让typeInheritance:TypeInheritanceClause? 让genericConstraints:GenericWhereClause? 让成员:[UnionEnumMember] } 有两种方法可以为indirect状态建模。 临时的,我选择不是将其建模为bool,而是将其建模为带有单个大小写的可选枚举。 枚举间接{大小写间接} 我希望这将使通过代码构建AST比传递布尔值更加直观。 枚举主体由成员定义数组组成。 枚举内可能有3种成员: 正则声明为嵌套类或函数。 实际枚举案例 用于条件编译等的编译器语句 我将所有情况建模为枚举,让枚举主体建模为枚举: 枚举UnionEnumMember { case`case`(UnionEnumCaseClause) 案例声明(声明) 案例CompilerControl(CompilerControlStatement) } 编译器控制语句不在我们的讨论范围内,声明已经在本周期的第二篇文章中定义,因此我们需要关注case子句: struct UnionEnumCaseClause { let属性:[Attribute] 让间接:间接? 让案例:[UnionEnumCase] } struct UnionEnumCase { 命名:标识符 let类型:TupleType } 如您所见,我们可以使用单个案例构造声明多个案例,这就是为什么将其建模为实际案例定义的数组。 原始值枚举声明 原始枚举由名称,可选的通用参数,必需的类型继承,可选的通用约束和成员数组组成。 […]

在Swift中开始使用自定义代理的两种方法

您是Swift委派的新手吗? 让我们讨论两种在代码中开始使用委托和协议的好方法。 但是首先,代表是什么? 委托是一种设计模式,当特定事件发生时,该模式允许一个对象与另一个对象进行通信。 委托对象保留对另一个对象(委托人)的引用,并在适当的时间向其发送消息。 这使我们可以更新委托以反映事件的完成。 大! 因此,让我们谈谈开始实现自定义委托的最简单的地方。 显示弹出窗口并检测与该窗口的用户交互 要创建一个弹出窗口,请在您的项目文件中添加一个新的CocoaTouch View Controller类,然后选择“还创建XIB文件”框。 这是我们将构建弹出视图的地方。 我们将其命名为“ PopUpViewController”。 这将是我们的委托对象。 在您的.xib文件中,添加两个按钮。 将出口链接到您的PopUpViewController.swift文件。 我们将第一个按钮命名为“ optionAButton”,第二个按钮命名为“ optionBButton”。 目的是从您呈现的视图控制器中检测用户选择哪个选项。 让我们将呈现的视图控制器称为“ MainViewController”。 我们还将这个视图控制器称为我们的委托 。 我们将需要在两个班级之间进行交流-这是委派非常有帮助的地方! 第一步是创建一个协议。 什么是协议? 协议定义了一组功能,属性或其他要求以完成特定任务。 协议可以由类,结构或枚举所采用,而类,结构或枚举又将提供那些要求的实现,并且据说该协议符合该协议。 因此,基本上,协议定义了将委派的职责。 因此,回到我们的代码。 让我们在PopUpViewController.swift文件的顶部添加一个名为“ PopUpDelegate”的协议。 我们将在我们的协议中添加两个功能(认为是“责任”),我们的代表以后将实现这些功能。 它看起来像这样: 您可能会注意到,我们还在PopUpViewController上添加了proxy属性。 这样便可以通过我们的MainViewController对其进行访问。 稍后,您将看到MainViewController将PopUpViewController的委托分配给它自己。 接下来,让我们为两个按钮连接IBAction。 这里要记住的重要一点是IBAction会检测与PopUpViewController的用户交互。 一旦进入该函数,就让委托人知道我们正在调用委托方法。 现在,让我们回到MainViewController并实现我们的委托方法。 (提醒一下,这些方法是在我们的PopUpDelegate协议中定义的,并在我们的PopUpViewController中调用的。现在,该调用它们了,现在该决定做什么了 !) 首先,我们的MainViewController需要符合PopUpDelegate协议。 在类定义之后添加协议,编译器将给您一个错误,指出MainViewController类不符合PopUpDelegate协议。 所以,让我们修复它! 我们将添加PopUpDelegate协议中列出的两个函数。 现在,我们仅打印出已按下特定按钮。 我们仍然没有办法看到我们的PopUpViewController。 因此,让我们在MainViewController中定义PopUpViewController类并推送弹出窗口。 我们将在MainViewController上添加一个按钮,用户可以按下该按钮以显示弹出窗口。 然后,别忘了将PopUpViewController的委托分配给MainViewController的重要步骤! […]