在上一篇文章中,我们讨论了错误处理。 开始进行Swift编程第14部分-错误处理 在上一篇文章中,我们讨论了泛型。 medium.com 我们介绍了有关如何创建自己的错误并在代码中使用它们而不只是崩溃的基础知识。 如果您有兴趣知道,我只剩下5个主题,然后向您指出一些我最喜欢的资源,这些资源可以帮助我编写程序。 本文将简短,但是它将结合一些我们已经学到的概念,并可能使您考虑如何以新的方式编写代码。 以前,当我们谈论枚举时,我告诉过您,它将为您提供选择的选项,而我们只限于那些选项。 我们可以在枚举案例中使用关联的值来定义使用枚举时将实例化的类型。 现在,我告诉您有关完全使用闭包,枚举和泛型的信息。 这是我从纳塔莎机器人那里学到的一个很酷的技巧,我在自己的代码中使用了很多。
免责声明:阅读前请检查对本文的评论。 Ilya Puchka 找到了更好的解决方案作为解决方法。 您可以注意到,当您使用其录制工具录制UI Test并点击SFSafariViewController内容时,Xcode 9.3崩溃。 捕获到“ NSInternalInconsistencyException”,“无效的参数不令人满意:[后裔_isDescendantOfElement:self]” 发生这种情况是由于Xcode中的错误,非常令人讨厌。 例如,如果我们要测试Facebook或其他社交登录信息,则需要与Web视图进行交互。 有趣的是,在调试期间,您可以进行debugPrint Web视图的所有按钮和字段,而不会出现任何问题。 但是由于NSInternalInconsistencyException的原因,您无法在文本字段中tap按钮或typeText。 为了成功进行测试,我需要Web视图元素中的一些内容: 检查元素是否存在 等待元素出现 点击按钮 清除文本字段中的文本 在文字栏位中输入文字 请记住,我们可以debugPrint元素,我们可以尝试这个疯狂的解决方案:解析元素的debugDescription ,从中获取其框架,然后按坐标点击XCUIApplication 。 让我们编码为: 等待元素出现 点击元素并检查元素是否存在 下一个问题是我们不能在文本字段中typeText Text。 但这不能阻止我们,对不对? 我们可以点击位置,因此可以通过复制/粘贴菜单粘贴文本。 同样,我们可以使用“全选”和“剪切”清除文本字段中的当前文本。 咱们试试吧。 在文字栏位中输入文字 就这样。 现在,我们可以测试Web视图中的许多内容。 这应该适用于SFSafariViewController并且希望与WKWebView或UIWebView (我没有对此进行测试)。 所有代码都在一个文件中:https://gist.github.com/pilot34/09d692f74d4052670f3bae77dd745889
最后但并非最不重要的一点是,我们有时间做我们以前无法做到的事情,适当地讨论事情,而无需感到压力。 我可以继续下去,当您节省的是时间时,可能性无穷无尽。 时间是值得信赖的绝佳货币。 建立iOS Foundation并在选择的框架中添加约定可以帮助我们减少在客户端项目之间进行切换时的上下文切换量。 如果您希望拥有协作文化,那么这至关重要。 由于实现之间的转换很容易,因此提供帮助的动机就越来越大。 它们越相似,上下文切换就越容易。 我们首先担心基金会会引入限制,从而降低团队的整体幸福感。 它确实要付出代价:自由选择或跳跃任何新事物。 就是说,基础并不是一成不变的,它应该像所有实施一样,成为始终应受到质疑,修订和改进的指南。 在选择新技术并引入影响整个团队的新依赖项时,它应该可以帮助您清醒。 建立牢固的基础需要花费时间,并且需要进行大量讨论,您可能会发现您在所有事情上均未达成共识,但是我敢打赌,您和您的团队都会为已经被定义为潜规则的事情感到惊讶。 当然,并不是所有人都会对所有决定感到满意,这就是民主的来龙去脉。我们使用赞成或反对的态度投票,如果多数票通过,那么就应该解决。 话虽如此,仅仅是因为某些东西没有被基金会接受并不意味着它不能被使用。 核心基础只是定义了转到工具的工具,如果该工具达不到标准,请问自己“可以改进吗?”。 如果是,那么这是进行开源开发的绝佳机会。 如果没有,也许我们需要完全不同的东西。 如果工具真的是通用的,并且从长远来看是属于基础的,那么随着时间的流逝,过多的否定也应该成为指导。 基金会最终比我们原先想象的要多得多,不仅仅是问题。 这是我们讨论团队前进方向的论坛。 即使我们基础上的活动放慢了速度,也并不意味着它不起作用。 相反,我认为事实恰恰相反。 随着摩擦的减少和结论的真实性,我们认为我们未来的自我会发现能够查明为什么,如何以及何时到达自己的位置会很有用。 如前所述,回顾过去可能是前进的最佳方式,但请记住,对过去的选择要仁慈,即使最终选择是错误的。 相反,拥抱并祝贺自己做出了选择。 我们仍然将Slack用作沟通的主要工具,但是一旦我们进入涉及做出会影响运营的决策的讨论时,我们就会将其移至基础,以便每个人都有时间参与。 最初,我想在一篇文章中总结所有内容,很快我发现一次开会的内容太多了,因此我决定将其分为四个部分。 这是“ 从灵感到生产” 的第一部分, 不要担心开发人员,即将到来的文章中将有一些代码片段。 您可以在Twitter @ zenangst和GitHub @zenangst上找到我 。
今天,我将在博客中介绍Swift中的另一个基本概念-可选变量。 苹果将可选内容描述为: 您可以在不存在值的情况下使用可选选项 。 可选选项代表两种可能性:要么有一个值,要么可以打开该可选选项以访问该值,或者根本没有任何值。 可选变量有? 符号定义后告诉编译器该变量可能为nil 。 您可以在这里阅读更多关于nil信息。 为了将变量定义为可选变量,必须指定对象类型,例如Int , Bool或String 。 让我们看一个例子: var optionalInt:整数? var optionalBool:布尔? var optionalString:字符串? 您会注意到上面示例中的所有变量都是var ,这是因为在let时不能将变量分配为nil 。 可nillable对象必须始终具有以后可以分配具体值的能力,否则编译器将在内存中保留常量nil变量,而不能在以后进行更改(变异)。 在此处阅读有关let vs var更多信息。 因此,以下示例将无法编译: 让optionalString:字符串? 外卖小贴士 可选变量必须使用类型声明。 可选变量必须为var not let 。 在此处获取此博客的游乐场。 雨燕快乐!
在本系列的第一部分中,我们讨论了触摸处理。 现在,让我们更深入地研究“响应者链”的工作方式,并查看可在iOS应用程序中处理的其他类型的事件:运动事件,远程控制事件和按键命令。 有关响应者链的更多信息 我们在介绍触摸事件时简要概述了响应程序链,但让我们仔细看一下。 响应者链是在整个应用程序中传递这些其他类型事件的主要机制。 响应者链的概念很简单。 它由UIResponder对象链组成。 链中的每个元素都有机会响应给定的消息。 如果给定的响应者不响应该消息,则链中的下一个项目将有机会进行响应。 如果链中没有对象响应该消息,则该消息将被丢弃。 第一个有机会响应消息的对象称为“第一响应者”。第一响应者是最近调用-[UIResponder成为FirstResponder]的对象 。 通过覆盖-[UIResponder canBecomeFirstResponder]并返回YES,对象可以成为第一响应者。 当对象作为第一响应者完成时,调用-[UIResponder resignFirstResponder] 。 作为动作事件和远程控制事件的响应者,非常简单。 作为第一响应者的对象被发送了运动或远程控制消息。 然后,它就有机会响应事件,将事件发送到响应者链,或者通过处理事件然后在超类上调用方法来完成。 涉及的方法在UIResponder中定义。 Apple的UIResponder类参考是获取更多信息的重要来源。 在实践中 我们可能会问,“这一切都很好,但是我可以在实践中使用它吗?”让我们来看一个示例应用程序。 首先,请克隆GitHub存储库。 该应用程序不是很有用,但是它演示了响应程序链如何工作的简单示例。 它需要一个硬件键盘,并且只能做一件事:在灰色框中打印字母“ a”。 它可以在模拟器或带有键盘的任何iOS设备中正常运行。 让我们深入一些代码。 首先,让我们看一下主视图控制器: 类ViewController:UIViewController { @IBOutlet弱var customTextView:CustomTextView! @IBAction func textViewTapped(_ sender:AnyObject){ customTextView.becomeFirstResponder() } } 这个视图控制器很简单。 它包含CustomTextView类型的自定义视图,并处理在情节提要中配置的轻击手势识别器的操作。 轻击手势处理程序在customTextView上调用beginFirstResponder 。 现在让我们看一下CustomTextView 。 类CustomTextView:UIView { 私人var textToRender:NSString吗? { didSet { […]
今朝呟いた本件ですが,色々补足しつつ记事としてまとめました。 値が自选な字典とは? 値がOptionalなDictionaryとは例えば次のようなものを指しています(値がOptionalなことが大事なのでString型以外でも何でも良いです)。 var d = [String:String?]() 以下の糖衣构文ですが,上の简単な记述とするのが一般的なはずです。 var d = Dictionary <String,可选>() 倾向于として値が可选な词典を扱うのはなるべくやめた方が良いと思っている ,例えば以下のように非Optionalでも下标して取った结果はString?となります。 var d = [String:String]() 让x:字符串? = d [“ key”] もし値がOptionalだと,次のように取得结果がString?? ( Optional<Optional> )とOptionalのネストになってしまいます。 var d = [String:String?]() 让x:字符串? = d [“ key”] 词典の各种处理の戻り値自体がこのように任选ように返すものが多いため,値の型自体が任选だとややこしくなります。大抵抗は値の型自体を任选にすることなく词典が包含する任选だけますハンドリングできると思っています。 とはいえどうしても値を可选としたいこともありえるかもしれませんし,色々いじってたら面白かった,という次第です(・‿・`) ちょっと相对きが长くなりましたが,以下本题です。
继去年发布Codable协议之后,许多iOS开发人员一直在忙于删除沉重的自定义JSON解析器,并在模型层中将它们替换为对Decodable的流畅且轻量级的构造……我也不例外:在最近的项目中作为一个客户,我很高兴增强大型应用程序的模型层,其目标之一就是完全符合Codable。 挑战1:嵌套的异构集合 最初,过渡到Decodable的过程很顺利(“祝您删除代码愉快!” )。 但是,自定义JSON解析器的删除导致重要的类类型映射的删除-原本功能强大的Codable协议未直接支持Codable 。 为了举例说明我在说什么,请考虑以下几点: 在这种非常简单的情况下,我们有一个超类Pet,它被两个类Cat和Dog继承,从而允许一个人拥有一个超类Pet的单个集合,而集合中的实际对象是Pet, Cat or Dog类型。 从JSON解码Person对象时会发生问题,因为“宠物”列表中的对象不是同一类型: 宠物列表将在Person初始化程序中像这样解码: container.decode([Pet].self, forKey: .pets) ,但这样做将检索超类Pet对象的列表,从而失去所有子类属性。 用Cat或Dog代替Pet也是不够的,因为我们对每种不同的类型(包括它们各自的属性)都感兴趣。 同时使用JSONSerialization反序列化JSON以使用类型区分JSONSerialization进行映射,似乎完全消除了使用Codable的好处,因为这是使用旧JSON解析器完成的方式。 解决方案:集中类映射 幸运的是,我并不孤单地遇到这个问题:Tom Stoffer写了一篇很棒的文章,介绍如何处理嵌套在Decodable对象中的异构列表。 尽管此解决方案在某种情况下是好的,但对于大型项目而言,这并不是一个非常干净的解决方案,因为我们可能会重复类型映射代码,因此我们可能会遇到多个对象相同的异构列表。 为了解决这个问题,我们可以将类型信息(映射)提取到类家族的集中位置。 这可以通过利用快速枚举上的函数来完成。 目标是通过公开用于检索正确映射的类型的函数以及JSON有效内容中类型鉴别符的键,来创建一个表示相关类家族的枚举。 因为我们希望解决方案尽可能通用,所以我们可以编写一个协议来定义所需的公开信息: 注意:仅在您为鉴别器使用不同键的情况下,才真正需要鉴别器类型。 (在我正在从事的项目中,我有两个不同的键)。 此外,鉴别变量是静态的-稍后会详细介绍。 ClassFamily协议允许我们创建一个描述任何对象族的枚举。 让我们看一个例子,说明我们的Pet案例的枚举是什么样的: 太好了,这个枚举现在描述了我们的宠物对象家族! 然后,我们可以直接在Person类的可解码的初始化程序中使用它,如下所示: 但老实说,此解决方案仅提取了映射,并没有真正使初始化程序更简洁。 因此,为了解决这个问题,我们将在KeyedDecodingContainer:的扩展中创建解码函数的泛型重载KeyedDecodingContainer: 这样做可以使我们极大地清理Person的初始化程序: 我们已经完成了可解码对象的嵌套异类列表的解决方案-干净整洁,不是吗? 但这不是全部… 挑战2.异构集合作为返回类型 …如果异构集合未嵌套在Decodable对象中怎么办? 例如,考虑以下情况: Person的Pets集合不是属性,而是在运行时通过API调用从服务器获取的: 在这种情况下,问题仍然存在: [Pet]类型的解码将导致带有名称的未知宠物的列表。 使用Cat或Dog也是不够的,并且为了像Tom Stoffer的方法一样,我们不像在Person类的初始化程序中那样具有带键控容器的Decoder对象。 令我惊讶的是,我在Stackoverflow或其他地方找不到很多类似情况的信息,然后返回反序列化JSON以读取鉴别符类型,然后进行解码,这对我来说是不可接受的。 解决方案:包装器类! 因此,经过数小时的类型推断问题和编译错误,我设法使用包装器类和一些枚举提出了一个通用解决方案。 这个想法是能够通过利用相同的类家族枚举来使用与嵌套集合相同的方法。 为此,我们必须围绕可解码类(及其子类)创建一个包装器类,然后可以将其映射到正确的类型。 让我们看一下如何实现它。 包装器必须是可解码的,以便我们使用JSONDecoder直接对其进行解码。 此外,它需要保留对我们希望创建的对象的引用。 […]
我于2017年7月开始在臭名昭著的猫鼬(comparethemarket.com)背后的公司工作。我刚刚结束了为期3个月的编码课程,专注于Ruby和Javascript全栈Web开发,在完成我的最终项目后,我结束了加入流行的Meerkat电影应用程序背后的团队。 我很高兴有机会开始使用我以前没有经验的技术,并且从头开始学习如何成为移动开发人员。 我不仅要向自己证明,而且要向我的团队证明,尽管我缺乏相关技能,但确实有可能。 因此,我白天进行了视频课程,晚上则阅读了编程书籍,在同事的帮助和支持下,我很快就会发现自己为我们应用程序的代码库做出了贡献,并每天都在捡票。 但是,达到这一能力水平绝对是一路颠簸,途中遇到了许多主要障碍。 在许多领域,我仍然是一个完整的新手,需要学习更多知识。 但是,到目前为止,我所面临的挑战以及克服这些挑战的经验证明是宝贵的经验。 因此,我希望通过分享这份建议清单,我可以启发甚至帮助其他有抱负的(移动)开发人员,他们发现自己处于与六个月前相似的状况。 请享用! 1.开始使用该应用 如果您已经接受工作或开始工作,这无庸置疑。 但是,如果您正在公司面试,那么了解可能要使用的应用程序同样重要。 尝试像用户一样使用它,并进行一些案例分析-也许您会想到一些有关如何改进应用程序的想法。 就我而言,我将研究要看的电影,看一些预告片,通过搜索或使用当前位置找出在哪里看电影,兑换我的优惠券代码,最后购买我的打折电影票并去电影院。 这不仅帮助我更好地理解了产品(尤其是从用户的角度),而且还帮助了我要开发的代码库。 2. 找一位导师 …如果可能的话。 我没有被任命为正式的导师,但是从一开始我就很自然地开始受到我们团队中一名高级人员的监督。 关于我的发展和重点领域,我们经常进行代码审查和每月召开的会议,并且有人在跟踪我的进展并向我提供反馈。 这在开始时至关重要,因为要评估自己的进度并决定要专注于什么可能会非常困难,尤其是在刚开始的时候! 3. 制定计划 –并制定明确的目标。 如果没有计划,学习会变得势不可挡,尤其是当清单和资源似乎无限时。 制定目标和时间表,并要求您提供有关您的进度和正在进行的工作的反馈。 极大地帮助了我的一件事是创建了Trello板,其中包含针对iOS工程的概述技术路线。 4. 了解何时寻求帮助 重要的是要学习如何自己找到答案,而不要依赖他人来完成您的工作。 因此,我建议在寻求帮助之前,总是尝试自己解决所有问题。 但是,如果您尝试查找错误的时间超过30分钟,或者如果某个特定的概念没有被点击,那么向某人寻求帮助当然是相关的-绝对应该这样做。 但是,学习如何以最佳方式找到平衡使用资源的方法,而又不会滥用具有更多经验的人坐在您旁边的事实。 并非总是如此。 5. 交流! 如果您无法在约定的时间内完成分配给您的任务,请让相关人员知道并共同制定新计划。 另外,在冲刺计划会议中保持直言不讳,并参与评估票务,尤其是要处理的票证。 对自己的能力诚实是很重要的,并且有责任让您的团队知道您是否对某项工作的某个时间表存在疑问和忧虑。 但请放心,在您作为开发人员的第一份工作中,至少前3到6个月,您的工作速度会慢得多。 6. 做代码审查 参与他人的代码; 进行代码审查,并请您的同事向您介绍他们的代码。 看到更有经验的开发人员如何解决问题并让他们向我解释他们的工作方式,我学到了很多东西。 确保您有足够的时间与编写该代码的人一起阅读复杂的代码,以便以后理解。 另外,请尝试让年长者与您一起检查您自己的代码,并获得关于您工作的口头反馈,而不仅仅是对Github的评论。 最后,配对程序-这可能并非总是可行,但是我认为这是最好的学习方法之一。 7. 找到自己的学习方法 互联网上充斥着各种各样的学习资源,有时这可能会让人有些不知所措。 各种各样的书籍,博客,播客和视频课程无处不在,并且很难知道该选什么和从哪里开始。 问题是,没有“最佳”道路。 有些人通过阅读学习得更好,其他人则通过学习视频教程来学习。 但是,我将分享三个对我的学习最有帮助的资源,我将向在同一路径上的任何人推荐: […]
JSONCafe :模板驱动的源代码生成器,可自动创建任何语言(Swift,C#,Java,VB,PHP等)的样板模型类源代码。 包括预定义的自定义功能模板或编写您自己的模板以完全按照所需方式生成代码。 预定义的代码模板 迅捷类 迅捷可编码 Swifty JSON 迅捷结构光泽 雨燕类光泽 适用于Android的JAVA 适用于Android的Java Gson PHP类 C#类 JSONCafe生成模型类所需的JSON对象和代码模板。 JSON对象包含数据,而代码模板包含代码和占位符(标记,例如{{className}},{{type}}等)placeholde动态替换为json数据 生成器还允许您在逻辑运算符和循环的帮助下编写条件模板代码 有关如何生成代码模板的更多详细信息,请访问 jsoncafe.com 如果您需要任何其他信息或有任何建议,请随时与我联系,谢谢
假设您正站在一个有n楼梯的楼梯前面。 您一次只能上一两个楼梯。 您到达顶部的独特方式的总数将是多少? 如果我们知道到达n-1和n-2的方法的数量,那么到达n点的总方法就是将这两个值相加。 这实际上与计算斐波那契数列的第n个数相同。 在这里,我试图提出解决斐波那契问题的不同方法,其中包括算法比较以及围绕时间和空间复杂度的讨论。 递归方法 我们可以从递归方法开始。 假设我们已经知道所有先前的数字,所以不用说当前数字是最后两个数字的和(自顶向下方法)。 func fib(_ n:Int)-> Int { 守卫n> 1 else {return n} 返回fib(n-1)+ fib(n-2) } 时间复杂度:O(2 ^ n) 空间复杂度:O(2 ^ n) 该解决方案的问题在于,我们不断地重复计算相同的子问题。 换句话说,我们的算法做了很多重复的工作。 该算法呈指数增长,需要付出很多努力,尤其是当n大时。 迭代法 这种方法来自相同的想法,但稍有调整。 如果我们将所有值存储在数组中以备将来使用,则可以避免冗余工作。 每当再次调用该函数时,该函数将直接从备注数组返回结果。 func fib(_ n:Int)-> Int { var fibs:[Int] = [1,1] (2 … n).forEach {我在 fibs.append(fibs [i-1] + fibs [i-2]) } 返回fibs.last! […]