Tag: 软件工程

如何有效地打开拉取请求

拉动请求是任何推送代码的世界级科技公司的必备工具。 它可以确保您的代码正在由其他工程师审核,并增强了被审核者对他们的代码符合工程团队所同意的一组标准的信心。 但是就像每杯️☕一样,质量在PR中扮演着重要角色。 质量低劣的公关人员可能会使被审核者和审稿人的口感不好,这通常是由于抨击和非建设性的反馈造成的。 它可能导致工程师之间的不信任和烦恼,直接影响代码库,甚至影响公司的文化。 但是,有效的PR可以引发有关最佳实践,代码约定和团队标准的良好有机对话。 PR中的代码修改将影响当前和将来形式的代码库。 最终,每个被批准和合并的PR都是构建产品和公司的LEGO块。 考虑到这一点,工程师确定如何打开有效PR的要点非常重要。 有效的公关人员应该就思想共享,最佳实践,工程惯例和团队团结进行对话。 创建有效的PR似乎是一项琐碎的任务,但是许多工程师陷入了不良习惯,甚至没有意识到。 这里有一些技巧,以提高您的PR游戏。 保持拉短要求 在审查代码之前,您如何惹恼其他工程师? 使用10,000行以上的代码更改来打开大型请求。 眼疲劳,背痛和偏头痛只是工程师在滚动巨大PR时遇到的一些副作用。 让您的公关简短,简单,切合实际。 从历史上看,大型PR通常会被迅速批准并合并,因为审阅者会在代码中略过一遍,没有任何评论可望结束滚动。 如果团队完全不关心代码库的健康状况,但是如果您的团队追求质量和维护,请打开小而简洁的PR,作为附带的好处,您的审阅者会感谢您。 公关应该改变多少行没有神奇的数字,但是如果您在跟踪或描述代码在一个句子中的工作时遇到困难,请分拆公关。 另一个好的经验法则是,票证与PR之间的比例为1:1,并且如果票证看起来太大而不能将其分解为子任务票证,则可以确保较小的PR。 请记住,易于阅读的PR假定可以在工程师之间产生良好的对话和协作。 PR不能用于跟踪谁可以更改回购中的大多数线路。 具有较大PR的另一个副作用是,由于分支修改的文件很多,因此需要不断进行重新编制基准,以使分支保持最新状态。 保持简短简短是游戏的名称。 保持评论的建设性 收到非建设性和不完整的反馈是很普遍的,它会使工作贬值,并且不会使有关各方受益。 一些评论甚至看起来更好,因为它可能冒犯或侮辱了打开PR的工程师。 留下建设性的反馈会引起良好的对话。 在评论PR时,有以下一些注意事项: 不要告诉工程师您将如何实施该解决方案。 大家都知道有很多方法可以解决。 仅仅因为审阅者可以用另一种方式来做并不意味着工程师必须以某种方式来实现它。 只要代码遵循一组团队惯例和体系结构,就可以为它开绿灯。 不要过多地关注代码样式,例如多余的空格,而将其他代码放在换行符上。 将这些任务留给小子而不是人眼看。 不要浪费精力查找丢失的代码样式,而要专注于实际的实现。 给予积极的反馈。 给予积极的反馈会鼓励您的工程师。 收到建设性的反馈是很棒的,但是太多的反馈仍然会使被审核者感到束手无策。 我们所有人都打开了PR,以解决一个难题,同情您的被审核者,并告诉他们他们在重构方面做得很好,或者感谢他们修复了一段时间以来令人讨厌的错误。 请勿使用“只是”或“我以为我告诉过你……”之类的词或短语。 这些短语可能意味着受审者应该已经了解了一些事实,但未能使用此知识,而在大多数情况下,受审者对此一无所知。 通过暗示一些标准,可以使他们感到过时和沮丧。 相反,请教您的同事工程师,并为他们指出正确的方向。 每个人都喜欢可以教书,学习并保持友善的工程师。 以下是移情评论的一些示例: 而不是 “仅使用用户模型工厂”, 而是 使用 “签出用户模型工厂,那里的某个功能可能会帮助您尝试完成” 代替 “我不是告诉您不要分配这个Car对象”, 而是 […]

用委托模式分离问题

在上一篇文章中,我们讨论了Closures的语法以及如何在其接收函数中使用它们。 今天,我们通过确保视图尽可能与控制器分离,来研究一种可用于在应用程序中实施分离和可扩展性的技术。 什么是代表? 委托模式被认为是解决关注点分离的一种可能解决方案,即任何对象都应处理自己的独特问题集。 例如,矩形结构不应处理计算圆的面积,依此类推。 这是一个非常明显的例子,但是委托的思想是一个对象应该使用专门的帮助对象来完成一项任务。 在MVC中,视图应负责向用户显示内容。 由于用户将通过单击,点击或按下视图来与视图进行交互,因此它们还必须接收输入事件。 但是,控制器的责任是实际解释输入并决定采取什么措施来响应输入,这是我们需要真正考虑设计的地方。 那么,如何在不使其中一个过于依赖另一个实现的情况下将视图的输入传递给控制器​​? 我们可以认为Controller在这方面是代表吗? 让我们从代码开始 让我们看一下我们将用于实验的类。 这是我们的BaseController,当我们要扩展应用程序时,它指定了一些不错的功能: 协议控制器{ 关联类型ViewType var typedView:ViewType {get} } class BaseViewController :UIViewController,控制器{ 覆盖func loadView(){ self.view = ViewType() } var typedView:ViewType { 如果让view = self.view为? ViewType { 返回视图 }其他{ 让view = ViewType() self.view =视图 返回视图 } } } 这是我们的BaseView,它为我们的View执行相同类型的操作: BaseView类:UIView { 便利init(){ self.init(frame:.zero) } 覆盖init(frame:CGRect){ […]

设计自定义数据结构的过程

在开发应用程序时,我们可能会遇到这样的情况,即我们无法使用的数据结构真正适合我们要完成的任务。 最近,我遇到了这种情况,正在开发一项功能,该功能要求使用事件监听器的集合。 今天,我想我会借此机会分享设计自定义数据结构背后的思考过程,并提出一些在实现它之前可能要回答的问题。 步骤1:要求 每个设计过程的第一步都是实际确定您希望新结构能够完成的工作。 这包括写下绝对必须要做的事情,以及在哪些方面我们可以摆脱“次优”表现。 让我们针对我们的侦听器列表进行此操作。 在我们的列表有效期内,听众可能会分配,也可能不会分配。 我们不希望我们的列表成为对象保留在内存中的原因(并有可能以意想不到的方式操纵我们的程序),因此我们将需要一些保持弱引用的对象。 由于侦听器对象可能已释放,因此我们需要一种清除这些对象的方法。 清理结构最多需要O (n)时间,并且应该可以将清理作为其他操作的一部分进行。 使用侦听器列表时,将对象插入列表比删除对象更常见。 因此,插入将需要尽可能快,但是移除将不是关键。 我们将采用O (1)的插入复杂度和最多O (n)的移除复杂度。 通知列表中的听众将是我们列表中的重要任务。 由于我们需要通知列表中的每个侦听器,因此O (n)是我们可以避免的最快的复杂性。 在满足所有要求之后,我们可以开始考虑使用什么基础数据结构。 步骤2:设计 查看标准库中的可用内容,我们遇到了NSPointerArray —一个可以保存指向对象的弱指针的数组。 这满足了我们的第一个要求。 实际上,此数组几乎可以满足我们上面印刷的所有要求。 唯一不满足的要求是第二个要求。 当我们在其上调用.compact()方法时,有很多nil指针的情况下,运行时复杂度(基于实现细节的假设)最终将是二次方的,这不是我们想要的。 如果我们考虑自己设计自定义数据结构怎么办? 什么是合适的? 让我们看一下链接列表。 链接列表满足第一个要求,因为我们可以设计节点以保留对其对象的弱引用。 通过将新节点添加到列表的根端,我们可以在恒定时间内插入新对象。 删除对象将是O (n)操作,因为我们需要遍历列表并查找引用。 这是可以接受的,因为这是我们在要求中指定的内容。 通知我们的侦听器对象就像遍历列表一样简单,这是一个O (n)操作。 另外,我们可以使用相同的遍历查找零引用并将其删除。 如果我们在通知周期的一部分中实现删除,那么这将与重新分配一些节点指针一样容易,并且将是O (1)操作。 考虑一下,这种结构实际上听起来很适合我们的目的。 步骤3:实施 我们将花费一些时间来查看自定义结构的一些实现细节。 首先,让我们看一下链接列表的“协议”(严格来说,这只是出于说明的目的,因为为这样一个特定的实现制定协议实际上没有任何意义,并且其中包含了我们不会通常要公开)。 这些方法将是非常基本的,并且我敢肯定您自己实现这些方法不会遇到任何问题,但是我将花几行.notify()讨论一种很好的方法,以将一种清除列表的方法合并到.notify()方法。 协议WeakReferenceLinkedList { relatedtype数据类型:AnyObject var root:WeakRefNode ? func insert(_ object:DataType)->无效 […]

Swift内存布局简介

在上一篇文章中,我们讨论了Swifts 值类型语义的一些功能。 今天,我们将看一下如何分配不同的Swift类型以及这对我们的应用程序性能意味着什么的基础知识。 一个简单的类是Reference Type ,就像上一篇文章中提到的那样。 它具有一些我们大多数人都很早就了解的功能。 例如,单个类对象可以被多个变量引用和操纵。 但是,当我们创建一个类的实例时会发生什么呢? 类MyClass { var a:整数 init(a:Int){ self.a = a } } var A = MyClass(a:1) 上面的示例提供了一个简单的类,使我们可以开始研究创建引用类类型的变量A时发生的情况。 当我们调用MyClass初始化程序时,将进行一次调用以分配足够的内存以将我们的对象存储在内存中,在这里我们需要查看主内存的两个非常重要部分的第一个,即The Heap 。 堆是我们内存中用于动态分配的一部分。 这意味着如果在运行应用程序时需要即时创建一个新的引用类型对象,则需要在The Heap上请求空间。 由于内存的这一部分需要能够一次为多个线程提供服务而又不破坏任何数据,因此还需要一些机制来避免两次分配相同的内存块并处理并行分配调用。 在不详细介绍如何进行管理的情况下,让我们继续进行以下假设,即这实际上需要花费一些时间。 一旦找到足够的内存块并为我们分配了内存,我们的应用程序即可完成对对象的初始化。 除了初始化类中的实际值外,还需要初始化一些元数据值(其中包括类型数据和引用计数器)。 Swift使用一种称为自动引用计数(ARC)的方法来跟踪实际使用的对象。 如果某个对象未被任何变量引用,则可以安全地将其删除,并且可以将存储块返回以用于其他用途。 这是一个很棒的功能,但是它伴随着每次创建或共享对象时更新计数器的开销。 通常,如果您只是分配一个新对象,那么花费的时间就不会引起您的注意。 但是,如果您的程序不断创建这样的新对象,那么成本将会增加,并且您最终可能会浪费本来可以更好地用于其他用途的资源。 正如我在上一篇文章中提到的那样, struct和enum (以及其他)都是Value Types 。 我们需要问自己的问题是:将在哪里分配值类型? 这部分会有些棘手。 让我们首先看一下堆栈 -两个重要的主内存部分中的第二个。 堆栈是内存的一部分,它从其操作方式中获得名称-就像堆栈一样。 这个想法是,您有一堆数据,如果您添加任何新数据,它将被添加到顶部。 删除数据也被限制在内存空间的顶端,因此我们只能删除当前放置在顶部的数据。 系统使用一个简单的指针来跟踪当前堆栈内存顶部的位置。 这意味着分配和取消分配内存是通过简单的增量或减量操作完成的,因此非常快。 不利的一面是,这种结构与The Heap可以提供的动态分配类型不太兼容。 […]

Dominique Riley:从化学到计算机

“我相信工程师能够解决问题,而且我一直都知道工程是我的使命。” 多米尼克(Dominique)可能知道工程是她真正的使命,但她到达化学之路始于对化学的好奇。 “小时候,我会写下房屋周围各种产品的成分,例如肥皂和洗涤剂,然后在午餐时间去图书馆并查找并研究成分。” 由于好奇心,她获得了路易斯安那大学拉斐特分校的化学学士学位。 当她发现自己对编程的热情时,她正在考虑计算化学的研究生学位。 “当我刚开始编程时,我什至没有自己的笔记本电脑。”但是,在老板的慷慨礼物–她的第一本Macbook之后– Dominique的发展顺利。 “我下班后参加了本地的Ruby编程课程,并积极地追求自己的技术职业。” 在2017年,她的编程技能与WOS(劳动力机会服务)的支持相结合,使她在GE软件工程学徒计划(SWEAP)中名列前茅。 “ WOS为像我这样的非传统背景的程序员提供了在技术行业找到工作的信息和机会。”这种支持来自她职业生涯的关键时刻。 她以成为2017法国街区音乐节应用程序的首席iOS开发人员而感到自豪。 “该应用程序被数百人使用,包括我的朋友和家人在内,在音乐节上浏览音乐,美食以及更多内容。 它对我也很感动,因为它在我的家乡新奥尔良使用。” 媒体对该项目的关注也令人鼓舞。 “对于我们新奥尔良的城市和(对于)像我一样想要编程的女孩来说,这是一个重要时刻。 我希望我的故事会鼓励更多的女孩想更多地了解科技以及女性正在做的令人惊奇的事情。” 多米尼克(Dominique)对学习和建设的热情很快使她成为该行业的专家。 在经理和队友的大力鼓励下,她申请了GE数字技术领导力计划,目前,她正在德克萨斯州休斯敦的贝克休斯(Baker Hughes)进行第二轮任职,专注于与石油和天然气行业相关的产品。 “我的人生目标是为他人服务。” 尽管她工作方式的很大一部分是帮助周围的人成为更好的程序员,但Dominique尤其致力于为女孩和妇女提供服务。 “我对该领域其他女性的建议是在您所在的领域找到其他女性并依靠她们。 即使只是喝咖啡或在这里和那里打了5分钟电话,在其他女人的支持下,这不仅对您的人际关系网很重要,而且对于您是否知道其他女人在您的鞋中也很重要。”

Swift中的依赖注入

轻松启用验收测试 在使用OOP范式开发应用程序时,您将意识到,如果您希望能够测试软件,则需要使用IoC原理。 通常,为了实现这一点,我们使用依赖项注入模式,该模式包括为每个类提供所需的依赖项。 在本文中,我将展示一种在Swift中放置此模式的方法,以便轻松模拟图形的某些依赖关系。 即使您不关心测试应用程序,我也建议您在代码中应用此原则,但随着项目规模的扩大,您将不胜感激。 在构造函数中使用默认值 在Swift中,我们可以通过在构造函数中使用默认值来实现混蛋注入。 容易吧? 起初,这似乎是一个好主意,因为它提高了可测试性,并且易于安装,但是确实存在一些问题。 此解决方案的主要问题是您与依赖项的默认构造函数紧密相关。 想象一下,您想编写一个UI测试,在其中使用test double模拟HTTP响应。 在这种情况下,您将需要提供此UIViewController的所有依赖关系 ,因为您不能再使用默认构造函数。 这是浪费时间,不是吗? 浸洗 由于上面讨论的问题,我决定尝试一些替代方法,例如Dip或Swinject。 这两个框架实际上非常相似。 它们是依赖项容器,您可以在其中注册如何使用lambda解决依赖项。 在高层,他们的工作是将这些lambda存储在字典中,并在需要实例化服务时使用它。 因此,要解决该问题,您只需要覆盖要用test double替换的所有内容即可。 但是,这些框架有一些缺点,例如: 依赖关系图是在运行时构建的,因此,如果您忘记注册如何解决依赖关系,应用程序将在运行时崩溃。 由于您需要在使用依赖项之前先注册它们,因此如果在启动时加载应用程序,则将花费更长的时间。 解决依赖项时,您需要使用强制解包(Swinject)或尝试(Dip)。 在我看来,最糟糕的问题是,如果您需要在运行时传递参数,则不会键入这些参数,并且不会自动完成。 犯错很容易,只需更改依赖关系的顺序,应用程序就会在运行时崩溃。 因此,我决定不使用它们,而是找到一个适合我的用例的解决方案,而又不影响性能,又不会失去由于Swift编译器而获得的安全性。 拟议的解决方案 我建议的解决方案具有您将喜欢的以下功能: 输入了解决依赖关系所需的参数,因此您不会出错。 如果忘记定义如何实例化对象,则该应用将无法编译。 该应用程序的性能不受影响。 您无需注册如何在运行时解析依赖关系。 处理可选值并不难,因为它可以在Swinject或Dip中进行。 您不需要使用任何外部库。 依赖项注入器会影响整个代码库,并且与第三方库的依赖项存在风险。 为了实现它,该解决方案使用了一些Swift功能,例如类型推断和协议扩展。 让我们看一个简单的例子。 想象一下代表超级英雄的下一个结构: 我们可以使用为您提供超级英雄实例并使用默认实现对其进行扩展的功能来定义协议。 我们将其命名为SuperHeroAssembler : 现在我们可以使用它来解析SuperHero实例: 以及如何在测试中模拟它? 好吧,这确实很容易,我们只需要创建一个符合SuperHeroAssembler的新类,但具有不同的resolve函数即可。 让我们看看如何实现它: 也许通过这样一个简单的示例,您看不到任何优势,但是如果使用得当,此想法将非常有用。 让我们看一个更详细的用例的例子,看看它是如何工作的。 它如何与用例一起工作? 想象一个屏幕,您需要在其中显示给定用户的所有联系人。 联系人来自网络,我们希望能够在验收测试中使用双重测试。 为此,我们使用以下类: 为了定义如何解决依赖关系,我们将针对该用例使用一个汇编器,我将其称为ContactsSceneAssembler […]

使用Swift创建篮球模拟应用程序:第2部分

让我们继续建设篮球。 欢迎回到篮球应用项目。 您可以在这里继续学习第1部分。 如果您还记得我们上次离开的地方,我们已经成功创建了用于模拟篮球比赛的Player,Team和Game类。 比赛由两支球队共同进行,他们都有机会得分。 我们可以进行一些比赛,并获得最终的比分: 但是,仍有许多工作要做。 以下是我想到的一些问题: 我们可以用名字来个性化我们的团队吗? 我们也可以命名玩家吗? 我们可以建立一个盒子得分,看看谁在得分,每个球员得分多少? 从接下来的两个功能开始,我们还需要建立什么样的联赛呢? 我们可以跟踪每个游戏的结果,并在“赛季”结束时提供统计信息吗? 这些是本部分要完成的主要目标。 在我们深入探讨之前,我将提出一个非常酷的建议,即有人提出来支持模拟财产中的三分球目标。 此代码块由Aaron A提供: 我很喜欢这个主意。 但是,对于软件开发过程来说,我决定完全重新考虑playPossession 功能并重构代码。 您会稍等一下。 但是首先,我们需要一些非通用名称! 从上次打开我们的Swift项目开始,让我们开始吧。 为我们的篮球队和球员取名 我们需要一些随机的球员和球队名称。 我们可以将这些名称列表作为数组包含在Player类和Team类中,但是可能会出现问题。 我们可能需要特殊功能来生成普通数组无法处理的名称。 同样,通常,我们应该尝试从类中抽象出尽可能多的与玩家或团队的行为没有直接关系的功能。 让我们创建一个名为NameGenerator.swift的新文件。 我已经填写了一些: 好的,希望其中大部分是简单的。 我们在此列出了球员和球队的可能名字。 现在,让我们具体看一下这一行: Swift中的单例 这条线表示的是单例模式,这是一种常见的软件设计模式。 简而言之(也是因为我对此经验不足),它仅允许在应用程序的整个生命周期中创建和使用一个类的实例。 现在,这种模式将如何帮助我们? 当我们将名称分配给新创建的玩家时,我们不希望两个玩家使用相同的名称。 我们也不想让尼克斯与尼克斯对抗。 因此,我们将能够在单个NameGenerator类中跟踪未选择的名称。 现在,我们将添加功能来完全做到这一点: 基本上在这里,我们将从名称列表中获取,并将其从列表中删除,这样就无法再次选择它。 看起来很棒! 现在,我们将Player类和Team类的初始化更新为一个名称: 我们的第一个目标几乎完成。 最后,我们必须先初始化NameGenerator单例类,然后再开始仿真。 让我们在ViewController.swift文件的顶部进行操作: 现在我们可以在任何地方使用它了:)。 继续执行步骤2。 进行盒子评分 篮球比赛中的得分是两队比赛结果的摘要。 大多数禁区得分都包含诸如得分,篮板和助攻之类的信息,但我们暂时将其保持在得分上。 如果这样做的话,想想一个数据结构来表示我们的盒子分数很简单。 我们有球员,每个人都会得分。 因此,让我们将盒子得分存储为字典,其中玩家将是关键,而他们的得分将是值。 […]

无畏和高效地使用第三方iOS框架

当我开始职业生涯时,我在一家不允许使用第三方框架的公司里工作。 我们必须从头开始编写所有功能,以使其可以在其他项目中作为我们自己的内部框架重用的方式来构建它们。 这是一个奇怪的政策,但它可能是我作为软件开发人员曾经遇到的最好的学习工具之一。 而且,如果您认为从头开始编写功能不可行,我可以向您保证。 尽管进行了额外的工作,我们始终设法按时交付,并且该业务是可盈利的。 也就是说,我完全理解了这种情况无法承受的情况。 实际上,在我的另一项工作中,该协议正好相反:如果有一个第三方框架可以完成我们不希望使用的功能,那么我们会因浪费时间和资源进行重新发明而受到谴责。车轮。 那也是一个很奇怪的政策,但是我从使用第三方框架中学到了很多-这样做使我可以接触到其他人的代码,这反过来又为我提供了很多额外的知识和经验。 我记得那时起有那么多aha时刻。 在我的职业生涯中经历过两次极端时,我发现在两者之间取得平衡通常会给我带来更好的工作成果。 一方面,我坚信自己做事是最好的学习方法,并且从零开始编写一切都教会了我如何构建框架,可重用的模块化代码,良好的API等。 它促使我学习如何有效地编码并解决任何问题,尤其是在我的职业生涯早期,学习曲线陡峭的时候。 另一方面,选择使用第三方框架可以帮助我快速创建高质量的解决方案,使我能够向世界各地经验丰富的开发人员学习,并在遇到软件开发方面的不同见解时提高了自己的技能。 这两种工作方式都非常有价值,我希望每个开发人员也有机会体验它们。 当然,并不是每个人都可以自由地从头开始编写自己的工作,在这种情况下,通过附带项目进行尝试通常是学习的好方法。 这正是我所做的。 早在2009年,作为学习如何编写iOS应用程序的练习,我为网络和JSON,XML和HTML解析器编写了所有Objective-C实现。 为了持久性,我编写了SQLite和Core Data包装器。 我什至编写了自己的基于文本的文件存储方案,这听起来很愚蠢,但这是解决许多问题的简单方法,最终使我的客户满意。 我们非常习惯使用已知技术,以至于我们常常忘记像Core Data这样的框架在很多情况下是过大的,而自定义解决方案更便宜,构建和维护也更快。 明确地说,我还不时使用Core Data,它为更复杂的项目提供了巨大的价值。 例如,即使在过去我曾经在iPod Touch(第3代)上测试应用程序的过去,它也有助于保持较低的内存占用并保持响应式UI线程。 使用定制解决方案来做这些事情将是困难且昂贵的,尤其是在处理大量数据时。 那么,您如何在这两个极端之间找到平衡? 每次从头开始编写都是很浪费的,但是编写模块并在项目领域中重用它们是一个不错的选择,尤其是在学习时。 同时,忽略第三方框架也很浪费,这样做可能会导致您面临许多在其他人的项目中已经解决和验证的挑战。 但… 还记得解析吗? 我做。 我们很难宣布。 从今天开始,我们将关闭Parse服务,经过长达一年的时间(截至2017年1月28日),Parse将完全退休。我们很自豪能够帮助如此多的人构建出色的移动应用程序,对此我们感到很自豪,但我们需要将资源集中在其他地方。 —来自他们的 博客 解析“继续前进”,但是他们的客户继续前进又有多容易呢? 我敢肯定,你们中的许多人都受到Parse关闭的影响,这很可能是关于过多依赖第三方服务的重要教训。 但这并不总是必须像关闭服务那样极端。 例如,当您使用的服务仍然存在但不再满足您的需求时会发生什么? 长期以来,我最喜欢的REST应用程序入门框架是RestKit-与其说是REST方面,不如说是它的主要功能,而是它强大的映射框架,它允许我从JSON / XML源创建应用程序域模型而无需将该责任泄露给模型类。 我希望我可以只使用框架的那部分,但是它的体系结构使得很难分开。 令我感到奇怪的是,我无法创建自己的客户端实现,因为RestKit的许多类都在子类化AFNetworking组件(它是建立在AFNetworking 1.x之上的),因此该项目完全依赖于AFNetworking。 即使这样,我还是决定接受这些缺点并将其用于网络层。 尽管我决定仍然使用它来节省很多开发时间,但我将其隐藏在一个不会泄漏对我的应用程序依赖关系的协议后面。 我的应用程序仅使用API​​协议,并且期望返回不从Core Data,RestKit或任何框架依赖项继承的应用程序域模型。 这使得应用程序极其易于编写和编写,维护和测试,而且轻巧。 然后是苹果推出NSURLSession的那一天,而我正在从事的项目之一需要一些新功能,例如后台下载。 RestKit社区开始了有关如何移植其客户端体系结构以支持此操作的漫长讨论,但截至目前,它尚未完成。 AFNetworking […]

蒸气3与Mongo DB

任何新项目都始于一个伟大的想法。 然后,您必须查看架构。 对于好主意,您应该期望获得更大的规模和大量的数据。 因此,您应该使用NoSQL数据库。 对于我的最新项目,我认为最简单的方法是使用MongoDB。 因此,我开始设置我的项目,并在蒸气3的文档中寻找如何连接到mongo数据库的起点。 令人惊讶的是,蒸气3的文档为空empty。 但这并没有阻止我。 在不同的领域进行了一些研究,我发现有2个解决方案,其中一个是Mongokitten,这似乎是蒸气2最常用的解决方案,另一个是MongoDB本身的mongo db swift驱动程序。 因此,我感谢MongoDB为构建友好的本机Swift驱动程序所做的努力,该驱动程序支持Swift Package Manager的使用。 我认为这可能非常适合我的服务器端swift项目。 我决定使用MongoDB的驱动程序。 那么如何开始呢? 首先,我必须将依赖项添加到我现有的蒸气3项目中。 为此,我必须将mongo驱动程序的url和版本添加到Package.swift文件中并获取新的源代码。 让包=包( 依赖项:[ … // Mongo Db .package(网址:“ https://github.com/mongodb/mongo-swift-driver.git”,来自“ 0.0.7”) ], 目标:[ .target(name:“ App”,依赖项:[“ MongoSwift”,“ Vapor”]), … ]) 这很容易,但是现在我需要知道如何在蒸气框架内使用它。 我在mongo swift驱动程序项目的示例区域中找到了起点。 通过此代码示例,我可以设置数据库连接并将客户端实例注册为蒸气框架运行时中的服务。 为此,必须扩展Mongo Client,以便将服务协议应用于该协议。 //configure.swift //准备好mongo收集服务 public func configure( _ config: inout Config, _ env: inout Environment, […]

iOS:设置基本的语音处理I / O音频单元-Swift

如果您是需要使用Apple的AudioToolbox和AudioToolbox API的不幸者之一,请让我尝试让您的生活更轻松一些。 苹果公司缺乏文档和有效的示例,因此使用Crucis确实可以使用这些工具进行开发。 经过反复试验,我设法弄清楚了如何在Swift中设置Voice Processing I/O音频单元。 该代码还可以用于设置任何其他音频单元,您只需要根据位于不良文档中的常量替换组件类型,子类型和所有其他参数的值即可。 无需赘言,这里是代码。 在该示例中,我还将展示如何设置本机的某些属性,例如“ Automatic Gain Control和“ Bypass Voice Processing 。