IBM-Swift团队的Chris Bailey最近在Swift London上发表的讲话显示了服务器端Swift的有趣基准图。 我对这些结果感到非常惊讶,因此我针对最新的Vapor(0.15),最近一次针对性能的版本(0.11)和针对Kitura(0.24)的Vapor’s Engine(在Vapor 0.15中使用0.4)运行了一些本地基准测试。 结果表明,Vapor的最新性能版本击败Kitura,而最新版本则落后5%。 在IBM-Swift的图表中看不到40%的领先优势。 此外,可以说与Kitura更具可比性的Vapor Engine具有领先优势。 IBM的测试可能正在发现Vapor的弱点,但由于IBM的结果中未包含确切的环境和基准,因此我们很难知道。 如果有人了解环境和过程的详细信息,请发表评论,以便我们改进调优! 结果 与往常一样,这是本文中运行的基准测试的深入细节和结果。 机 蒸气(0.11) Gertrude:〜tanner $ wrk -d 10 -t 4 -c 8 http:// localhost:8080 / plaintext 运行10s测试@ http:// localhost:8080 / plaintext 4个线程和8个连接 线程统计平均值Stdev Max +/- Stdev 延迟438.06us 97.97us 5.03ms 84.30% 要求/秒4.53k 140.02 5.04k 70.79% 182307个请求的读取时间为10.10秒,已读取25.91MB 请求/秒:18050.53 传输/秒:2.56MB 蒸气(0.15) Gertrude:〜tanner $ wrk -d […]
最初发表在 Swift Post上 。 如果您开发移动应用程序已有一段时间,那么您可能听说过MVVM和VIPER。 虽然有些人说MVVM不能很好地扩展,但另一些人则说VIPER太过分了。 我在这里解释为什么我认为它们非常相似,我们甚至不需要将两者分开。 首先让我们快速了解一下MVVM和VIPER。 View提供用户操作以查看模型 。 视图模型处理这些操作并更新其状态。 然后, 视图模型通过数据绑定或使用委派或块手动通知视图 。 视图将用户操作传递给演示者 。 然后, 演示者将这些动作传递给交互器或路由器 。 如果该动作需要计算,则交互器将其处理并将状态返回给presenter 。 演示者然后将此状态转换为演示数据并更新视图 。 导航逻辑封装在路由器中 , 演示者负责触发它。 有关这些架构的更多信息,请参考 Bohdan Orlov的 这篇精彩文章 : iOS Architecture Patterns 我们的首要目标是将UI和业务逻辑分开。 因此,我们可以轻松更新UI而不破坏任何业务规则,也可以隔离测试业务逻辑。 MVVM和VIPER都保证了这一点,但是方式不同。 如果从这个角度来看,它们的结构如下。 MVVM在UI层中只有一个视图组件,而VIPER将UI职责分为三个组件,即View,Presenter和Router。 同样很明显,业务层看起来几乎相同。 让我们看看它们在实践中在UI层中实际上有何不同。 想象一下,我们正在使用MVVM构建一个简单的应用程序,该应用程序将从IMDB中获取前25部电影并将其显示在列表中。 这些组件可能如下所示。 几周后,苹果发布了iOS 26,乔尼·艾夫(Jony Ive)通过引入全新的设计系统,拉开了一个新招。 我们的设计师兴奋不已,并迅速提出了适用于iOS的新UI。 现在,我们的工作将是实施此新设计并通过A / B测试证明其可用性,这意味着我们将仅向一定比例的用户显示新UI。 实际上,我们的信誉很好! 我们只需要为iOS创建一个符合MovieListView协议的新视图,并将其与演示者连接即可。 十分简单。 protocol MovieListView: […]
打开/关闭原理指出模块应该打开以进行扩展,而关闭则可以进行修改。 从本质上讲,在iOS中,这意味着应该通过扩展打开模块,无论是通过快速扩展(或Obj-C类别),子类还是通过依赖项注入(或可以提供的其他任何扩展模块)。 使用扩展模块的任何其他模块仍应能够使用它。 这与SRP原理非常吻合,因为您无需使用该模块更改其他模块中的任何内容。 我将演示第一个示例—在第一部分的ImageStorage类示例中进行子类化。 假设我们要添加一个新的ImageStorage类,它所做的只是保存带有黑色效果的图像。 我们要做的就是子类化ImageStorage并覆盖它的saveImage函数: 如你看到的 我们仍然可以像以前一样使用ImageStorage 。 它没有任何改变,因为我们以对扩展开放的方式编写了ImageStorage 。 它应保持关闭状态以进行修改,因为任何修改都可能使其他模块以我们不希望或期望的方式运行。 对于此示例,我们将修改ImageStorage类的工作方式。 我们将为其添加文件访问器。 从现在开始,所有文件I / O将成为访问者的工作。 (这也将给我们带来更高级别的职责分离。SRP FTW!) 首先,由于我们使用依赖注入,因此ImageStorage现在不能使用静态函数。 接下来,我们需要声明文件访问器协议: 现在,我们需要更改ImageStorage类。 我们将通过init()注入FilesAccessor,从现在开始,加载和保存功能将与FilesAccessor一起使用: 看看SRP效果如何? 现在,ImageStorage不再关心文件访问的责任。 现在,我们可以实现我们的DocumentDirectoryFileAccessor: 最后,我们需要修改ProfilePicManager以使其与具有特定FilesAccessor的特定ImageStorage一起使用 。 例如,我们可以这样做(第9行): 现在,我们可以欣赏代码的灵活性 我们可以走这行: let imageStorage = ImageStorage(访问器:DocumentDirectoryFilesAccessor()) 并替换为: let imageStorage = NoirImageStorage(访问器:RemoteServerFilesAccessor()) 然后,我们会将带有Noir效果的图像存储在服务器上 ( RemoteServerFilesAccessor将与某些服务层模块一起使用)。 视图控制器对此更改一无所知,并且扩展我们的ImagesStorage现在是无限的。 我们可以声明所需的任何访问器,并将它们注入到任何ImageStorage中 ,只要它们符合FileAccessor即可 。 这导致我们进入第3部分: Liskov替代原理。
自动生成Swift网络模型 永远不要再编写网络模型。 无论您是移动开发的新手还是经验丰富的移动开发人员,您都很可能会遇到将API集成到移动应用程序中的情况。 将JSON转换为可用模型的艺术可能是乏味的,也是一项令人讨厌的工作。 出于本文的目的,我们将使用下面的Library JSON响应来生成我们的Swift网络模型。 下一步 ModelSynchro目前正在积极开发中,仍然可以添加一些很酷的功能。 我一如既往地重视反馈,并鼓励对改进此广告连播的任何想法。
RS字母在汽车工业中经常用来指代给定汽车的最强大版本。 对于MVC-RS,适用相同的想法:它是MVC,但是由于另外两个额外的层(路由器和存储),功能更加强大。 香草MVC MVC的第一个版本是在70年代与SmallTalk一起推出的,旨在满足GUI应用程序不断增长的需求。 苹果公司推荐的用于构建应用程序的MVC新版本略有不同。 在此版本中,视图层和模型层不再相互了解。 完全分离这2个层的主要优点是使它们可重复使用。 但是,具有可重复使用的层意味着什么? 可重用性 Swift可以在所有Apple平台,iOS,macOS,watchOS和tvOS上重用,但也可以在Linux上以及某些天在Windows和android上重用。 重用View层与重用一些通用代码不同,因为View与它们所运行的平台紧密耦合。 这是有道理的,您没有呈现相同类型的UI,也不允许在手表,手机,平板电脑,计算机或电视上进行相同类型的用户交互。 如果您在macOS上编写了View层,则使用了AppKit,因此,只有在AppKit可用的情况下,该层才可以重用,这意味着只能在macOS上使用。 Model层没有View这样的约束,因此您应该能够在所有支持该语言的平台上重用Models。 您在iOS开发中经常遇到的第一个常见错误是在模型中使用UIKit。 Apple并没有为您提供帮助,因为他们在文档和某些WWDC会话中都犯了同样的错误。 这样做的问题是您不再可以在其他平台上重用Model。 让我们看一下这个例子: 在上面的示例中,您有3个不同的数据库提供程序。 如果您使用的是db A,则可以在任何地方重用Model。 但是,您可能还在macOS项目上使用了db C,并且该数据库在任何其他平台上均不可用。 在这种情况下,您的模型将无法在其他平台上重复使用。 这里的问题是,当您考虑MVC时,总是只考虑这三个层:模型,视图和控制器。 但实际上,有一个隐式的第四层:持久性(通常是本地文件,本地数据库或远程调用)。 如果您的模型知道您的持久性,则只有在您的持久性可用的情况下它才可以重用。 为避免这种情况,让我向您介绍MVC-RS的S层,它代表存储。 存储 存储层将只在您的持久性和模型之间进行,它将有2个任务: 与持久性交谈 将原始数据从“持久性”转换为模型的实体 与持久性交谈 此任务比看起来要复杂,因为对持久层的调用是异步的,因此它们可能会成功,但也可能会失败。 您的存储层将必须考虑所有这些参数。 转换数据 当您读取应用程序将呈现给用户的数据时,您的存储层会将从持久性获得的原始数据转换为模型的实体。 但是它也应该能够以其他方式做到这一点:当您的应用程序需要保留某些元素时,您的存储层应该知道如何将模型的实体转换为原始数据,并将其发送给Persistence。 重要的是要记住,这两个任务都可能失败。 例 在本文的示例中,您将看到之前的简单Car Model和非常方便且常见的Result枚举模式,而不是繁重的do-try-catch语法来管理错误。 这是您可能在存储层中找到的示例。 读取方法不会立即返回,因为对持久性的调用是异步的。 它们将闭包作为参数,当存储工作完成时将调用它。 如果在与Persistence交谈或将原始数据转换为Model实体时发生故障,则将调用onComplete闭包并显示错误,否则将使用适当的Model实体进行调用。 您可能会发现不同的读取方法,例如一种使用id来获取模型的单个实体的读取方法,或者另一种使用一些参数的读取方法,以防您不想加载模型的每个实例。 编写方法,创建,更新和删除完成了此CRUD示例。 它们具有相同的签名:它们采用Model的实体和onComplete闭包。 如果在将Model实体转换为原始数据或将该原始数据发送到Persistence时失败,则将调用onComplete闭包并显示错误。 否则,将以nil调用。 优点 将存储定义为协议非常方便,因为它允许您针对不同类型的持久性编写不同的实现。 您可能会发现一个用于sqlite数据库的数据库,另一个用于Couchbase数据库的数据库,一个用于某些Rest API调用的数据库……如果您要模拟持久性,还可以编写一个实现用于测试目的。 […]
如今想出一个主意很容易,但要迅速将其转变为产品并不容易。 如今,随着BaaS(后端即服务)的可用性,我们可以轻松地消除构建后端系统以支持我们的移动客户端的需求。 对于这篇简短的文章,我将列出一些简单的步骤,以构建可轻松与Firebase连接以利用其实时功能的应用程序! 我正在使用的应用程序将是一个名为PokemonWo的应用程序,请不要运行它,我希望我可以在2小时内完成。 该应用程序基本上利用了Firebase的登录功能,执行身份验证和登录,然后从FirebaseStorage加载图像,然后用户可以执行随机捕获并获得积分。 每个人都可以使用高分表来跟踪分数变化。 第1步:设置Firebase帐户和数据库: 转到Firebase控制台,设置一个帐户,然后在此添加您的应用程序。 您可以随便叫它。 之后,请在此处下载sdk,然后按照以下步骤将您的项目与Firebase关联。 您应该有一个GoogleService-Info.plist。 我使用Pod安装我的Firebase SDK。 设定验证 接下来,在firebase控制台中,启用“电子邮件/密码的身份验证/登录方法”,这将允许您创建一些简单的帐户,供用户登录到您的应用程序。 然后,转到“用户”并开始创建一些帐户: 然后,转到“存储”并开始上传一些图像,每次上传后,每个文件都将存储在Firebase服务器中,您将能够从每个图像获取url地址,以便以后可以添加到数据模型中。 有事吗 安全规则存在危险迹象,这是因为为了简单起见,我允许任何人访问图像文件甚至上传到存储,但是在构建将要发布的更复杂的应用程序时设置适当的规则很重要给更多用户。 我们快到了,剩下的就是主要数据本身,Firebase使用无模式JSON格式化数据库。 您可以轻松地将此数据转换为 宠物小精灵{ [{ “ name”:“ charmander”, “得分”:3 },…] } 那么到目前为止我们已经设置了什么? 我们已经设置: Firebase控制台 iOS SDK 帐目 存储 数据库 完成这些步骤后,我们就快到了! 只需将数据提取到我们的移动应用程序中,我们就很好了! 第2步:安装项目并集成Firebase iOS SDK 对于我的项目,我仅使用两个导入,即Firebase和FirebaseDatabase,可以随意探索其他导入,它们对于进一步启动应用程序很重要! 在appDelegate进行简单设置,导入和配置。 还记得我们在firebase中有设置帐户吗? 我们调用FIRAuth.auth方法执行身份验证,然后在数据库中与主用户帐户分开创建培训者数据。 那这里怎么了 在通过Firebase成功进行身份验证后,我将获取Firebase生成的ID并创建新的培训师条目,然后将其存储在本地,以便以后可以使用它来更新分数。 因此,这里有2个主要功能,其一是玩随机捕获游戏来得分,其二是查看所有教练的得分。 对于功能1,首先,我调用我创建的所有神奇宝贝条目并将其存储在数组中: 一旦我们准备好了宠物小精灵,我们就准备好抓住它们了! 我运行了一个随机数生成器以随机遍历数组并获取一个神奇宝贝名称,使用该名称附加到我们的Firebase存储的firebase baseurl中,并异步加载图像。 完成此捕获后,它将根据我分配给该口袋妖怪的分数来更新教练的分数,并且实时更新教练的分数! 多么酷啊! […]
我在iPhone上经常使用Gmail应用程序,并且我一直喜欢Google用来循环加载电子邮件和/或在向下滑动时刷新的循环进度动画视图。 我说的是“材料设计圆形进度视图”,尤其是不确定的实现,其中圆上的两个点似乎一直在互相追逐。 我决定使用Swift在iOS上实现此功能,然后尝试一下。 您可以在这里找到git repo。 这是一个相当快的实现,我只花了几个小时就完成了。 真的可以改善吗? 可能是。 我将来会重新讨论吗? 当然。 进度视图动画由两个基本部分组成:笔划和旋转。 简而言之,我们有一个视图,我们要添加一个称为“ circularLayer”的CAShapeLayer 。 我们必须确保它没有填充色,并为其定义可见的线宽,因为我们将使用它来绘制圆。 让CircularLayer = CAShapeLayer() CircularLayer.lineWidth = 4.0 CircularLayer.fillColor = nil layer.addSublayer(circularLayer) 我们需要设置一个圆形的UIBezierPath ,稍后再对其进行动画处理。 这是在视图的layoutSubviews()方法上完成的。 为此,我们计算要绘制的圆的半径并描画一条圆弧路径,并将其添加到circularLayer中。 请注意,起始角度为π/ 2,这是基于对进度视图在Gmail iOS应用上开始和结束的位置的观察。 让半径=最小值(bounds.width,bounds.height)/ 2 — circleLayer.lineWidth / 2 让arcPath = UIBezierPath(arcCenter:CGPointZero,radius:radius,startAngle:CGFloat(M_PI_2),endAngle:CGFloat(M_PI_2 +(2 * M_PI)),顺时针:true) CircularLayer.path = arcPath.CGPath 好的,让我们来看看实现的内容。 现在我们有了中风的道路,但我们需要对其进行动画处理。 我们使用strokeEnd从点0.0到1.0进行动画处理。 我们还将动画添加到strokeStart中 ,以稍稍延迟地赶上移动点。 对于最后的触摸,我们使用具有EastIn和EaseOut时序的动画曲线,以突出显示“追赶”效果。 让inAnimation:CAAnimation = […]
几周前,我启动了我的第一个iOS应用程序,它具有关于Swift的非常基础的知识,这些知识是我通过一些教程和主要是Apple文档中学到的。 但是后来,我对任何编程语言都不了解,几乎所有在Swift中学到的东西对我来说都是新的。 掌握了基本数据类型,条件语句和循环的新手知识之后 ,我开始使用Swift进行编码。 在开发应用程序的过程中,我学习并使用了某些iOS SDK和库。 随着我的知识的增长,它主要取决于应用程序开发本身,而很少涉及编程概念。 这里要具体说明数据结构和算法。 我在应用商店中发布了我的应用。 在与朋友一起审查该应用程序并谈论其可扩展性时,我了解到我还有很多东西要学习。 不仅是编程语言,数据结构和算法,还涉及解决问题的方法。 我相信通过经验学习,这样我才能更快地学习,解决现实生活中的问题并保持动力。 因此,我一直在浏览有关我应该学习什么以及应该如何开始的网站,并且对初学者学习编程的所有不同选择感到非常困惑。 幸运的是,我的一个朋友向我介绍了Leetcode。 LeetCode是一个平台,允许任何人以越来越困难的方式解决编程问题。 当我开始解决问题时,我觉得LeetCode是一个很好的平台,可以提高您在解决问题和编码方面的技能 。 它支持Swift,因此我也可以掌握编程语言,最终帮助我更快更好地构建iOS应用。 LeetCode 提升您的编码技能,并迅速找到工作。 这是扩展您的知识并做好准备的最佳场所。 leetcode.com 如何开始解决问题的实践: 首先,对要学习的东西有个目标 。 对我而言,这是使用正确的OOPS概念编写更好,更简洁的代码,并对数据结构和算法有深入的了解。 从简单的事情开始,这样您就会获得信心。 然后逐步解决中/硬问题。 尝试在40分钟内解决 。 如果找不到解决方案,请查看提示,然后继续。 当您开始感到更加自信时,请减少时间。 如果您不了解某些内容,请不要犹豫 。 当我说Google时,请不要用Google搜索问题本身。 Google会根据您的想法解决问题,并了解如何实施这些想法。 对于数据结构(DS)学习 ,首先列出要保留的数据结构 。 然后阅读有关google,GeeksforGeeks等中的内容的信息,一旦您掌握了一些知识,请先使用您选择的编程语言来实施DS ,然后再使用DS解决问题。 然后在LeetCode中搜索数据结构的实现问题。 解决该问题的方法有助于我理解如何将特定类型的数据结构应用于问题。 测试您的代码并提交 。 如果您的代码没有通过,请不要放弃。 尝试修复错误,直到正确为止。 LeetCode的主要学习成果: 了解面向对象的编程。 更好地解决问题 。 开始掌握时间和空间的复杂性 。 了解哈希表和B树 。 […]
CAEmitterLayer仅指定粒子的起点。 要更改粒子的外观,我们必须看一下CAEmitterCell。 CAEmitterCell CAEmitterCell是CAEmitterLayer发射的粒子的模板,并确定其外观和移动方式。 CAEmitterLayer具有一个或多个CAEmitterCells数组,每个可能具有不同的配置和样式。 要更改粒子的外观,可以使用CAEmitterCell的以下属性: 内容 单元格的内容需要PNG图像文件作为粒子的基础。 该图像将用于确定粒子的形状。 最好使用白色图像,因为视觉属性会为其着色。 视觉属性 设置内容后,视觉属性可以更改粒子的颜色和比例。 也可以设置颜色的RGBa值的速度和范围。 色速定义了颜色在单元生命周期中的变化方式,范围指定了每个新生成的粒子之间颜色成分可以变化的量。 运动属性 运动属性可以使细胞旋转,并确定粒子发射角的方向。 就像颜色范围一样,发射范围可以改变每个新生成的粒子之间的发射角度(以弧度为单位)。 例如:上面显示CAEmitterLayer形状的GIF显示出2π的发射范围,这使得粒子可以在任何方向上生成。 时间属性 这些属性涉及与时间相关的任何事物,包括寿命,出生率(每秒粒子数),粒子的速度和加速度。 让我展示一个示例,说明四个不同的单元格可能是什么样的: 太酷了吧? 创建线条和彗星 现在,我们对粒子在iOS中的工作方式有了基本的了解,让我们看看Voicy中的线条和彗星。 画一条线 画一条线很简单。 您只需要两个坐标,一个颜色和一个宽度。 和繁荣! 就这样,你有一条线。 我创建了一个LineModel结构,该结构具有坐标和线条颜色的属性。 一个简单的drawLine()函数返回一个CAShapeLayer,可以将其添加到UIView来绘制两个坐标之间的线: struct LineModel { var startPoint:CGPoint var endPoint:CGPoint var lineColor:UIColor func drawLine()-> CAShapeLayer { //创建路径 让linePath = UIBezierPath() linePath.moveToPoint(startPoint) linePath.addLineToPoint(endPoint) //将行创建为CAShapeLayer 让lineLayer = CAShapeLayer() lineLayer.path […]
在Mimo,我们每周举行一次Show&Tell会议,每个团队介绍他们上周所做的工作,之后,您可以分享您最近学到的东西或您热衷的东西。 我在Mimo讨论了“枚举何时具有超能力”之后,创建了以下文章,因为我想这可能会让更多开发人员感兴趣。 这是Swift版本。 您还可以在这里找到这篇文章的Kotlin版本。 假设我们有一个用Swift编写的iOS应用,现在我们想添加一个用户登录名。 我们使用4个参数创建一个简单的登录功能。 username , password , success callback , failure callback 。 到目前为止,一切都很好。 我们还假设我们已经提前计划了。 对于在登录时遇到错误的情况,我们创建了一个IncorrectPasswordError类来处理故障回调中的此特定错误。 我们的解决方案有效。 我们可以添加诸如TimeoutError或UsernameNotFoundError类的更具体的错误,并在我们的失败回调中对其做出反应。 那么,如果它起作用了,我们为什么还要改变它呢? 🤔 我们依靠类型转换来处理特定的错误。 我们将所有AuthenticationErrors与可能发生的任何其他RuntimeError混合在一起 很难发现不同的错误。 如果您知道自己在做什么,则类型转换可能不是问题,但其他两点仍然成立。 在登录过程中,我们基本上会在登录流程中遇到一些错误,例如用户输入了错误的密码/电子邮件或发生了超时。 与可能发生的任何其他意外错误不同 ,对此类错误的处理方式不同 ,因此也许应将它们分开处理。 第二大问题是可发现性 。 如果不查看确切的实现,我们不知道我们需要处理哪种错误。 编译器在这里也无济于事。 那么我们如何做得更好? Swift具有强大的功能( 带有关联值的枚举),其中枚举可以容纳更大的数据块。 我们将使用它来重构我们的登录流程。 我们首先对所有可能发生的情况以及我们要专门处理的情况进行建模。 success – success登录将包含user对象。 usernameNotFound找不到usernameNotFound名 incorrectPassword错误的密码 timeout unexpectedError —任何其他意外错误。 现在将这些错误与其余错误分开 。 现在,我们新的登录功能只有一个带有LoginResult回调。 为了处理不同的结果,我们创建了一个简单的开关盒 。 首先,我们现在将登录流程错误与任何其他意外错误分开了。 这些意外错误在单个case块中一起处理,而其他usernameNotFound如usernameNotFound或incorrectPassword具有自己的case块。 […]