Tag: Swiftlang

Yummypets iOS应用程序堆栈

今年即将结束,我们想为 Yummypets 应用程序 共享iOS堆栈 。 我们开始做吧! 码 该应用程序的97%是完全用Swift编写的。 总共46k行代码。 MVC 整个应用程序遵循good’ol MVC模式 。 当控制器太大时,我们会将它们重构为较小的控制器。 核心 我们所有的模型逻辑和网络代码都封装在Core.framework中。 这迫使我们将逻辑和视图代码分开。 这也具有使我们能够在不启动完整应用程序的情况下处理纯逻辑的巧妙优势。 网络层 我们的网络层隐藏在Api界面后面。 这意味着我们的View控制器对我们的网络库一无所知。 这也意味着我们可以将api实现切换为返回虚拟数据以进行测试,而无需触摸控制器\ o /的任何一行 。 图像加载和缓存🖼 我们使用Kingfisher异步加载图像和缓存处理。 该api很简单,但是非常可定制。 我们以前使用AlamofireImage,但由于性能原因而切换到Kingfisher。 联网☁️ 对于网络,我们使用ws☁️,这是一个简单的基于诺言的JSON网络库。 它具有打包3件东西的优点: 出色的Alamofire库,用于网络请求 一个简单的Promise库 一个简单的JSON解析库 这种整洁的抽象层使我们能够有效地添加和维护路由。 异步🕐 我们严重依赖Promises来保持我们的异步代码清洁和可维护 。 为此,我们使用🎬 JSON解析⚙️ 我们对JSON解析库有严格的要求:不应强迫我们对模型进行子类化,同时支持类和结构,并且要尽可能简单。 为此,我们使用Arrow🏹,它将为我们处理所有样板JSON解析代码,并保持模型映射的清洁和可维护。 布局🏝 我们不会使用Storyboard或Xibs ,原因是我们在此不做详细说明。 TLDR : 更容易维护 。 您可以在这里找到有关此问题的精彩文章。 是的,我们所有的视图和TableView单元格都在代码中! 这带来了巨大的好处! 如您所知,纯自动布局的代码非常冗长 ,这就是为什么我们使用Stevia🍃使其保持可读性和可维护性 […]

蒸气与Kitura基准测试

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 […]

斯威夫特的数字协议

我在新加坡iOS Conf的Jesse Squires演讲中,他讨论了Swift的数字类型和协议。 这让我想起了SE-0104(面向协议的整数),现在该实现是在Swift 4中进行的,认为现在是仔细研究的好时机。 让我们从每个人喜欢的整数类型Int的协议层次结构开始: 与标准库中的大多数类型一样, Int符合很多协议! 从头到尾, Numeric是基本协议之一。 所有不同大小的整数类型( UInt , Int8等)以及浮点类型( Float , Double等)都符合Numeric 。 数值,从0x00开始 您可以在Integers.swift.gyb中遵循标准库源代码,尽管照常,所有相关代码都将在下面内联。 这是开始的协议声明: public protocol Numeric : Equatable, ExpressibleByIntegerLiteral { 这里有另外两个符合性: Equatable增加了对==运算符的支持。 ExpressibleByIntegerLiteral允许使用let a: Int = 42这样的代码let a: Int = 42其中42是上述的“整数文字”。 这意味着所有数字类型都可以使用整数文字进行初始化,但不能使用浮点文字进行初始化。 这段代码可以: let someFloat: Float = 42 // ✅ 但这不行: let someInt: Int = 4.2 // […]

使用RxSwift时应避免的8个错误​​-第1部分

细节可能会有所不同,但是不管您的观察对象,观察者或订阅如何,基本含义都保持订阅的含义。 要发现的关键是,忽略参考周期管理器 (又称disposable ,可以使您摆脱自己破坏参考周期的可能性。 它是进入内存配置的门户药物,一旦无法使用,就再也没有回头路了。 如果使用_ =语法,则基本上可以说,释放observable observer和observer的唯一方法是完成可观察序列。 有时候这可能正是您想要的! 例如,如果您要调用Observable.just ,则不必确保打破周期,这并不重要。 单个元素被瞬时发出,随后是.completed事件。 但是,在许多情况下,您可能无法完全确定所讨论的可观察性的完成可能性: 您从另一个对象获得了Observable ,文档没有说明它是否完成, 您从另一个对象获得了Observable ,并且文档确实说明该对象已完成,但是在此过程中该对象的内部实现发生了一些变化,没有人记得要更新文档, Observable明显未完成(示例包括Variable , Observable.interval ,subject), 可观察的实现中存在错误,例如忘记在Observable.create闭包中发送.completed事件。 由于您几乎无法控制应用程序中的所有可观察对象,即使那样也有可能出错,因此经验法则是确保自己打破参考周期。 要么保留对disposable的引用,并在时间到时调用.dispose()方法,要么使用方便的助手(例如DisposeBag来帮您完成。 您还可以使用.takeUntil运算符提供一个单独的可观察到的循环中断。 选择哪种方式取决于您的具体情况,但请始终记住: 既然我们已经解决了所有问题,我觉得我欠您一点解释。 我上面绘制的心理模型很好,这是一种心理模型,因此并不严格正确。 当前的RxSwift实现(在撰写本文时版本3.x / 4.x)发生了一些复杂的事情。 要了解实际行为,让我们更深入地研究RxSwift内部。 在哪里实现subscribe方法? 毫无疑问,搜索的第一位将是ObservableType.swift文件。 它包含订阅方法的声明,作为ObservableType协议的一部分: func subscribe( _ observer: O ) -> Disposable where OE == E 什么实现了此协议? 基本上,所有各种类型的可观察物。 让我们集中讨论称为Observable的主要实现,因为它是RxSwift中定义的所有可观察对象(除了其中之一)的基类。 其subscribe方法版本简短而简单: public func subscribe( _ […]

SWIFT 3中的UICOLLECTIONVIEW,没有界面构建器。

集合视图类似于表视图,它们显示由自定义布局定义的单元格的集合,并且像表视图一样,它们必须符合协议才能显示数据和执行操作。 在本教程中,我们将创建一个看起来像网格的集合视图。 好吧,让我们潜入…… 让我们开始创建一个新项目,这次选择Swift。 现在,让我开始向您介绍将处理collectionView布局的类UICollectionViewFlowLayout。 创建一个新的Cocoa Touch文件并使其成为UICollectionViewFlowLayout的子类,添加此完整的实现,我将对其进行解释…… let innerSpace: CGFloat = 1.0 let numberOfCellsOnRow: CGFloat = 3 override init() { super.init() self.minimumLineSpacing = innerSpace self.minimumInteritemSpacing = innerSpace self.scrollDirection = .vertical } required init?(coder aDecoder: NSCoder) { //fatalError(“init(coder:) has not been implemented”) super.init(coder: aDecoder) } func itemWidth() -> CGFloat { return (collectionView!.frame.size.width/self.numberOfCellsOnRow)-self.innerSpace } override var itemSize: […]

如何通过模拟Vapor 3和Swift中的依赖关系来测试控制器

测试具有外部依赖关系的控制器(尤其是涉及HTTP请求的控制器)非常棘手。 那是因为发送实际的请求会创建一个您不能依靠的测试,并且测试非常缓慢。 但是,您可能熟悉一种非常简单的技术:使用协议并将依赖项注入控制器。 最近,当我尝试从单元测试中的路由获取参数时遇到了一个问题。 因此,今天我想向您展示如何在Vapor 3中轻松传递正确的路线到测试功能。 让我们开始吧🚀 您为什么还要测试控制器? 🤔 想象一下一个在线商店。 在“产品详细信息”页面上,用户可以检查有多少可用商品。 问题是可用性系统是一项外部服务,因此,每当用户想要检查产品状态时,都必须调用外部服务。 由于外部服务可能已关闭或用户正在寻找的物品不再可用,因此控制器必须返回正确的信息。 模拟外部依赖dependencies️ 您无法检查外部服务是否正常运行,但是可以通过模拟外部服务来检查控制器是否正在返回正确的状态代码。 您可能之前已经做过,但很简单: 使用协议进行外部依赖 将其注入控制器内,例如: init方法内 在测试套件内部,使用符合协议的模拟对象 让我们定义一个简单的协议— AvailabilityCheckerProtocol : protocol AvailabilityCheckerProtocol { func checkProduct(id: UUID, quantity: Int) throws -> ProductDetailsResponse var req: Request? { get set } } 我添加了var req: Request? 只是因为它很容易使用client().get()方法并从实现内部的外部服务获取状态,所以使用该协议。 现在创建一个具有checkAvailability功能的控制器: import Vapor import Foundation final class AvailabilityController { […]

在Swift中展开和编辑iOS表格单元格

来自 petethedeveloper.com的交叉发布 最近,我们实现了一项功能,该功能允许用户向UITextView中输入任意数量的文本,而UITextView本身包含在UITableViewCell中。 要求如下: textview不可滚动。 而是,textview应该随着内容大小的增加/减小而扩展/折叠。 textview永远不会小于指定的基本大小 单元格应该因为包含的textview展开/折叠而展开/折叠 下面,我描述我们如何做到的。 实施 实现分为三个步骤: 当textview的内容更改时,计算显示所有内容所需的框架的大小。 这是使用以下方法在UITextViewDelegate textViewDidChange(textView :)方法中计算的; / *计算显示所包含内容所需的帧大小 内容* / 让fixedWidth = textView.frame.size.width 让newSize = textView.sizeThatFits(CGSize(width:fixedWidth,height:.greatestFiniteMagnitude)) var newFrame = textView.frame 如果显示文本所需的框架大小与当前的textview框架大小不同,请更新textview框架的大小。 请记住,我们绝不会将textview框架的大小更新为小于我们指定的基本框架大小; //我们的基本身高 let baseHeight:CGFloat = 50 / *我们的新高度绝对不能小于我们的基本高度,因此请使用两个中的较大者* / 让高度:CGFloat = newSize.height> baseHeight? newSize.height:baseHeight newFrame.size = CGSize(width:max(newSize.width,fixedWidth),height:height) 当textview的框架大小更改时,更新out表单元格的大小以匹配所包含textview大小的大小。 我们使用委托协议来告诉UITableViewController更新单元格高度。 我们传递计算出的框架高度,然后将其作为单元格的高度返回给UITableViewDelegate tableView(:heightForRowAt :)方法; //保存新高度 EditorialCellHeight = […]

像老板一样进行测试— RxSwift简介

在此处阅读全文 在最近的几篇文章中,我们一直在研究Streams –随时间变化的值。 掌握起来并不是很困难,我们一直在努力将它们集成到View Model中。 硬币的另一面虽然正在测试我们的代码。 我们如何使用RxSwift测试反应式代码? 我们使用一个名为RxTest的库。 RxTest使我们可以访问测试计划程序。 调度程序就像一件工作,通常我们将在特定线程上运行它,例如在先前的文章中,我们使用主调度程序实例将后台工作移至主线程上。 这里唯一的问题是序列添加到Rx思维方式的同一件事:时间本身。 流可以随时接收事件,因此,为了模拟我们只是伪造它们出现的时间,但是我们对顺序进行了具体说明。 更好的是,我们有一个网络呼叫需要20秒,我们可以伪造20秒而不必等待那么长时间的嘲笑。 考虑带有Rx的视图模型的一种好方法是输入需要提供的输入以及它们将产生的输出。 自然,我们想在此处测试输出,但是输入可能是搜索栏的文本值,这会触发网络调用,并且视图模型会公开我们可以测试的输出值。 它可能是结果列表,或者在这种情况下是String。 现在,让我们看一下如何进行视觉测试: 如你看到的。 视图模型具有输入和输出,这两个输入和输出都可以通过某种方式使数据突变。 就像工厂一样,数据被“绑定”到其中,它被更改并在最后创建一个可观察的对象作为输出,除了我们想在最后订阅该输出。 让我们看一个例子: 在测试目标中,请确保导入RxSwift,RxTest以及项目的@testable导入,以访问其视图模型和其他类似的类: 导入XCTest 导入RxSwift 导入RxTest @testable导入YourAppName 类YourAppName:XCTestCase { 我仅在此项目中使用CocoaPods,但您需要分别导入RxSwift和RxTest。 类ViewModel { //这是我们将向其发送输入的公共属性。 也许这可能是搜索栏的文本或标签的文本值。 公共变量输入:BehaviorSubject = BehaviorSubject (值:“”) //这是我们将测试的输出值 惰性var输出:BehaviorSubject = BehaviorSubject (值:“”) 让disposeBag = DisposeBag() //简单的初始化即可自动自动调用我们的订阅 在里面() { setupBindings() } //订阅更改的简单函数 私人功能setupBindings(){ input.subscribe(onNext:{[弱自我]值 self?.output.onNext(“更改的\(值)”) } […]

在MacOS和Linux上使用ImageMagick,Vapor 3和Swift对照片加水印

回顾过去(您还记得Web 2.0吗?😉),我过去经常围绕照片进行编程。 在格式之间调整大小,缩放,旋转和转换。 最近,我不得不创建一个Web服务,该服务使用Vapor生成带水印的图像。 事实证明,还没有太多用于图像处理的库,因此这个简单的任务成为研究中一个有趣的问题。 有两个主要的图像处理库:GD和ImageMagick。 ImageMagick有点像高级库,所以我决定首先使用它。 ImageMagick有一个有趣的Swift包装器,称为MagickWand。 不幸的是,它根本不支持文本操作,因此我不得不更深入地研究。 我找不到任何有用的东西,所以我想:“为什么不在Swift中只使用一些ImageMagick C函数呢?”事实证明,这很容易做到! 安装ImageMagick 在ImageMagick网站的开发部分中,您可以找到MagickWand和MagickCore的文档。 这些是用C编写的用于与ImageMagick处理库进行交互的接口。 将其包装到Swift包中非常容易,但是首先让我们安装ImageMagick。 让我们从macOS开始。 您可以自己编译ImageMagick,但对于macOS,仅使用Homebrew会更容易。 ➜brew安装imagemagick @ 6 如果未指定@6 ,则默认情况下将安装最新版本。 我在Linux上的版本7遇到问题,因此我决定将macOS和Linux都使用旧版本。 安装后,您会注意到自制软件未创建符号链接(有关以下信息:仅桶依赖项)。 为了获得有关ImageMagick所需的信息,此部分很重要: 为了使pkg-config查找[受电子邮件保护],您可能需要设置: 导出PKG_CONFIG_PATH =“ / usr / local / opt / imagemagick @ 6 / lib / pkgconfig” 您可以只在终端中在每个pkg-config命令之前添加PKG_CONFIG_PATH ,但是如果使用zsh只需运行,则永久添加它更容易: ➜echo’export PKG_CONFIG_PATH =“ / usr / local / opt / imagemagick […]