Tag: swift

如何在iOS应用程序中为多个模拟器运行UITest?

本文假定您已经知道如何在Xcode中编写UITest。 现在,如何运行与彼此交互的UITest,在这种情况下,您可以为一种消息传递功能编写测试用例,该功能可以在多个模拟器中运行多个用户,并等待他们彼此聊天。 这是消息传递应用程序的理想自动UITest,对吗? 我知道,关于此的StackOverFlow问题不多。 我知道,RayWenderlich网站上也没有教程。 我知道,您可能尝试过不同的解决方案,但是其中任何一个都有一些局限性。 但是,在这里您可以找到适合我的解决方案! 为什么呢 好吧,如果您的应用程序中具有消息传递功能,则应用程序最重要的测试范围是消息传递的整个功能,不仅要确保已发送消息,还要确保将收到消息,或者如果您有群组消息,请确保群组中的所有成员都会收到消息。 您可能会想到可能的解决方案,以下是每种解决方案的缺点: 手动测试 您知道这不是一个好选择! 因为这很耗时,所以即使您的测试场景文档齐全,您也可能会有人为错误,但是测试人员可能会错过对所有案例进行测试的机会! 在一个模拟器/设备中运行两个应用程序: 首先 ,如果您要测试多个用户,则必须为要运行的每个主应用程序定义多个捆绑标识符,并且有一个测试应用程序将与其他应用程序交互。 但可以肯定的是,这不是一个漂亮的测试环境! 其次 ,在应用程序之间进行切换将非常耗时! 第三,但最重要的是,如果您不仅仅想要像行为测试这样的UITest,或者您正在使用像KIF这样的库,它们仅与UnitTest Target兼容,那么在任何这些情况下,您都必须在UnitTest Target中使用测试用例可以访问您的应用程序模块,并获得诸如KIF之类的库的支持! 但是,您将失去运行可与主应用程序交互的单独测试应用程序的功能! 甚至,您将无法在setup()函数中运行以下命令,您将获得运行时错误! XCUIApplication()。launch() 同时运行两个模拟器: 是的,这也是我认为的最佳解决方案。 但请记住,我们不仅需要UITest,而且还使用KIF编写UITest案例!

将酱汁添加到Xcode UI测试:XCUITest +酱汁实验室

XCBlog上的原始帖子在这里 Sauce Labs刚刚宣布支持在其自己的设备场中运行XCUITests。 他们已经在官方博客上宣布了支持。 随着苹果自己的Xcode UI测试框架变得越来越流行,云测试服务不能像Sauce Labs,BrowserStack,Perfecto这样的供应商不能忽视这一事实。 在上一篇文章中,我们看到了XCUITests的各种基于云的设备测试选项。 长期以来,Sauce Labs通过支持和维护Appium一直在帮助社区。 由于本机移动测试自动化框架(例如XCUITest和Expresso)开始流行,因此它们也必须转换齿轮并提供支持。 苹果于2015年推出了Xcode UI测试框架,最后,Sauce Labs支持在其真实设备云上执行这些UI测试。 Sauce Labs已在XCUITest和Real Device Cloud上广播了网络研讨会,以演示我们如何在云设备上设置XCUITest。 不幸的是,我无法参加实时网络研讨会,向主持人提问,但后来我观看了整个视频。 在这篇文章中,我将分享我对此过程的想法,以及可能会给Sauce Labs回答的一些问题。 流程 观看了XCUITest和Real Device Cloud上的整个网络研讨会之后,我了解了在Real Device Cloud上运行XCUITest的过程。 您可以在Sauce Labs Youtube频道上观看在线研讨会,也可以在下面观看。 我将留给您观看网络研讨会,现场演示并了解在Sauce Labs真实设备云上执行XCUITests的过程。 总而言之,该过程涉及以下步骤 使用Swift或Objective-C为iOS应用编写XCUITests 准备两个.ipa文件,一个用于您的主应用程序,另一个用于XCUITest运行器应用程序 使用Sauce Labs Web界面上传这些文件,以便Sauce Labs可以查看那些应用程序 使用设备标识符从Web界面中选择要在其上运行测试的设备 下载XCUITest Runner JAVA应用程序Runner.jar,该应用程序通过传递您的凭据来运行测试。 Java应用程序会将您的凭据,到IPA文件,设备,数据中心的路径作为参数,并触发对指定设备的测试。 然后,您可以查看测试结果,运行设备云的测试屏幕截图和视频。 您可以在上面显示的网络研讨会中看到所有这些工作,因此值得一试。 好处 在实际设备上运行XCUITest肯定有一些明显的好处。 由于iOS模拟器不涉及任何硬件接口。 一些好处是 来自真实设备的反馈非常有价值,因为这些是真实用户使用的设备。 可以选择最常用的设备并对它们并行运行测试 无需购买即可在一周内访问新推出的设备。 我们可以从具有hasipa资产的任何Linux或macOS Continuous […]

SwiftNIO的协议缓冲区

总览 您可以在.proto文件中定义消息模式,然后使用protobuf编译器为您的语言生成数据结构,从而减少编写样板分析和数据访问代码的过程。 然后,您可以与系统的其他部分共享此.proto文件,并为这些语言生成数据访问类。 例如,您可能有一个Java后端以及一个iOS,Android和Web前端,它们都共享此.proto文件来定义共享模式。 优点 Protobuf设计为快速而紧凑。 根据Google的说法,协议缓冲区比XML 小3至10倍,并且快20至100倍 。 Protobuf与语言无关,并提供向后兼容性。 您可以使用完全不同的语言,用新的高性能系统组件替换旧的缓慢系统组件,并确信只要您使用相同的.proto模式,系统就可以继续工作。 实际上,您甚至可以在.proto文件中的消息格式中添加新字段,并且系统将继续运行。 具有旧.proto系统在解析时将仅忽略新字段。 缺点 Protobuf并不适合所有人,尽管它们支持多种语言,但可能不支持您的语言。 XML和JSON比Protobuf更具可读性。 由于它们的设计紧凑,因此当它们通过网络发送时,protobuf会删除字段名称。 仅当您具有.proto文件时,protobuf才有用。 Google设计了用于应用程序间通信的协议缓冲区。 如果您正在构建供外部消费者使用的公共API,则您确实不希望服务器的客户端使用您定义的.proto文件,为其服务生成数据访问类并以这种方式与您的服务进行通信。 入门 您将需要protobuf编译器来生成特定于语言的文件。 这里有安装说明:https://github.com/apple/swift-protobuf。 由于已经安装了Homebrew,因此选择了Homebrew选项。 $ brew install swift-protobuf 注意:此安装可能需要一些时间… 要检查其是否正确安装,可以键入: $ protoc-版本 //我的输出是libprotoc 3.5.1 .proto Protobuf使用扩展名.proto作为其文件格式。 您创建.proto文件来定义您的消息传递模式,然后运行protobuf编译器生成您的Swift代码。 您可以使用任何IDE或文本编辑器来创建.proto文件,包括Xcode。 但是,我不喜欢使用Xcode的原因是缺少适当的缩进和语法突出显示。 我最终在他们的市场中使用了VS Code以及免费的vscode-proto3 protobuf扩展。 现在,我们已经安装了protobuf编译器,让我们创建一个将通过网络发送的Movie类型。 创建一个名为movie.proto的新文件。 .proto文件的第一行是要使用的协议缓冲区版本。 在撰写本文时,我们将使用最新版本proto3。 语法=“ proto3” 接下来,我们使用message关键字声明消息类型。 对于我们来说,这将是Movie类型的消息。 消息电影{ } 接下来,为电影类型声明一个枚举。 您可以在全局范围内的message外部声明一个枚举,但是对于我们的示例,由于流派正在描述电影,因此我们将在Movie内部声明它。 消息电影{ […]

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

在上一部分中,我介绍了enum声明,并发现迅速地存在两个不同的枚举。 在这一篇中,我将一次涵盖两个实体: struc和class 。 它们彼此非常相似。 但是在运行时担任不同的角色。 结构 通常,让我们从语法的外观开始: 前面的部分已经介绍了很多此类字段,因此我们应该使用乐高积木来构建它。 我们将展平此类型,并用struct-member数组替换struct-body struct StructDeclaration { let属性:[Attribute] 让accessLevel:AccessLevelModifier? 命名:标识符 让genericParameters:GenericParameterClause? 让typeInheritance:TypeInheritanceClause? 让genericConstraints:GenericWhereClause? 让成员:[StructMember] } 枚举StructMember { 案例声明(声明) 案例editorStatement(CompilerControlStatement) } 作为struct的成员,我们可以具有一些声明或某些编译器指令。 类 类语法非常相似: 与struct选项的主要区别只是添加了final指令。 它可以在访问级别修饰符之后或之前。 因此,有两个类声明。 我们将跳过此差异,并始终假定final只能在访问级别修改器之后才能进行。 struct ClassDeclaration { let属性:[Attribute] 让accessLevel:AccessLevelModifier? 让final:final? 命名:标识符 让genericParameters:GenericParameterClause? 让typeInheritance:TypeInheritanceClause? 让genericConstraints:GenericWhereClause? 让成员:[ClassMember] } 枚举Final {case final} 枚举ClassMember { 案例声明(声明) 案例editorStatement(CompilerControlStatement) } 如我们所见,类和结构在语法方面非常相似。 但是,有一件事对我来说看起来很奇怪。 […]

代表团

佩基,代表尼迪尔? “代表,贝利里·比尔·奥莱·奥尔杜恩·unda·比尔·内斯宁宁·巴斯卡·比尔·内斯尼·哈伯·贡德梅西内·奥拉纳克·坦尼扬·比尔设计模式’dır’。” Bir A nesnesini hayal edin。 Bir eylemigerçekleştirmekiçinB nesnesiniçağırır。 Eylemtamamlandığında,一个nesnesi B’ningörevitamamladığınıve gerekli eylemigerçekleştirdiğinibilmelidir。 BuişlemRepresentative’lerin院子里的孩子! Sizlere buolayıbirörnekleaçıklayayım: 2yaşınızdaolduğunuzuhayal爱丁。 Yemek yemeyi buyaştabilemezsiniz,buyüzdenannenizinyardımınaihtiyacınızvardır。 Yemek yapabilen bir anne Annoluşturmadanönce,Anneninnasılyemekpişirileceğinibilmesi gerekiyor。 协议YemekPisirme { func yemekYemek() } Şimdi,“ YemekPisirme”protokolüneuyan bir anneyapısıoluşturalım: struct Anne:YemekPisirme { func yemekYemek(){ //Protokolümüzdekifonksiyonumuzayazıekledik。 打印(“ Pizza geliyor !!”) } } 我的名字是:安妮·内西内西(Omşturalım): // Anne NesnesiniOluşturma var anne = […]

可变参数和输入输出参数

经过一个半月的编码训练营,并在Apple文档中进行了许多搜索并发现堆栈溢出……我对Ygritte的口号有了全新的了解: 每当我觉得我开始理解一个概念的基础时,我进行这些搜索只是发现我只了解冰山一角,而且有很多我从未听说过或使用过的东西……带我进入我的主题:可变参数和输入输出参数。 当我们一直在讨论诸如函数类型和嵌套函数之类的主题时,我决定回过头来重新阅读Apple关于函数的文档。 快速搜索“函数”将显示我们已经学习的许多关键点:定义和调用函数,参数和返回值,参数标签等。我们还从Ben Su的博客“参数,非常功能”中了解了很多有关默认参数的知识 。 ”,我也希望从Jhantelle Belleza的博客“ Intro to Closures {}”中了解有关Closure的信息。 但是,正如我在文档中继续读到的那样,还有可变参数和输入输出参数。 这些到底是什么? 让我们看看苹果如何定义它: 可变参数 “ 可变参数接受零个或多个指定类型的值。 您可以使用可变参数来指定调用该函数时可以传递不同数量的输入值的参数。” 数量不定的输入值听起来很像……。 收藏? 那么,这是怎么回事? 我继续读…。 “传递给可变参数的值在函数体内可以作为适当类型的数组使用。” 嗯…好 然后让我们测试一下: 它打印: 好的,这很好,因为我们现在可以调用此函数并传递不带方括号[]的数组参数: 如果您说,我已经有了想要使用的数组怎么办? 理想情况下,在调用具有可变参数的函数时,您不想将数组作为参数传递。 但是,您可能会同时使用两者来创建更多选项。 假设我们要查找数字数组的平均值: 现在,我们可以将此函数用作带有可变参数的函数的参考点: 最后,我们将获得两个输入选项: 一种方法比另一种更好吗? 不一定…由于可变参数和数组参数都具有通过数组的相同目的,因此可能只是一个优先事项。 某些人可能会通过在调用函数时直接插入数组的输入值来发现可变参数更易读和直接。 以下是有关可变参数的一些要记住的要点: 在任何参数类型的末尾附加一个省略号(3个点) 可以与其他类型的参数结合使用,但每个功能仅允许一个 因为它是一个数组,所以值必须是同一类型 最后,如果您有片刻的déjàvu感觉像您之前已经看过这三个点,请在此处查看: 那还不错,不是吗? 继续学习输入输出参数!

Server Side Swift入门:1.0

完美的模板 欢迎来到我的“ Server Side Swift入门”系列。 在整个系列文章中,我将解释如何使用Perfect工具箱使用Server Side Siwft的各个不同方面进行入门和运行的基础知识。 今天,我将展示如何使用Perfect Assistant应用程序以Perfect Template为基础来启动新的Server Side Swift项目。 首先,如果您尚未获得Perfect Assistant应用程序,请访问https://www.perfect.org/en/assistant/以下载Perfect Assistant。 在“欢迎”屏幕上,单击“创建新项目”按钮。 这将在第一个按钮为“ Perfect Template App”的工作表上向下滑动。 这是我们想要的按钮,因此请继续单击它,然后按“下一步”。 使用“浏览按钮”为您的应用选择位置-并在需要时为其创建一个新文件夹。 请注意,在Perfect Assistant中,“项目名称”是您选择的文件夹的名称。 您可以根据需要更改此设置。 就本演示而言,请取消选中“将Linux构建与Xcode项目集成” -在我们构建或运行时,这会解锁其他linux二进制构建,从而使我们可以了解任何特定于Linux的构建问题。 但是,在这种情况下,这与我们无关。 按下“保存”后,Perfect Assistant将继续创建项目,下载所有依赖项并创建Xcode项目文件。 完成后,您的项目也将列在左侧的“项目”列表中。 它显示了项目中已经存在的依赖项,以及可以根据需要将其拖入其中的其他依赖项。 从这里,您还可以在查找器或终端窗口中打开项目目录,然后打开Xcode项目。 在Xcode中,首先让我们稍微整理一下工作区。 我建议关闭右侧的“实用程序”窗格,因为此处不相关,并打开“调试/控制台”空间。 让我们先看一下Package.swift: 您将看到它设置了项目的名称,并为我们提供了Perfect-HTTPServer的基本依赖关系。 但是,在展开Sources组时,您会看到许多其他依赖项已自动添加到我们的项目中,例如PerfectLib,Open SSL,Thread,Net。 之所以添加这些是因为它们是HTTP Server模块的子依赖项。 在我们的项目源目录中是一个main.swift文件。 其中包含用于运行Web服务器模板的代码-一种简单的处理程序,一种简单的路由。 该处理程序将仅输出标题和正文为“ Hello,world!”的HTML文档。

具有60个单元测试的示例iOS应用

无需功能性反应式编程的MVVM架构模式 在互联网上,缺少使用单元测试的应用程序的实际示例。 有一些关于抽象理论中的单元测试原理的文章,但是很少有明确的具体示例。 因此,我决定制作一个应用程序,以分享到目前为止我学到的一些知识。 这是运行该应用程序的视频: 代码覆盖率尚未完成,但没有必要达到100%: Xcode单元测试会在每个单元测试之前自动运行setUp()方法,并在每个测试方法之后自动运行tearDown()方法。 这些方法通常用于清除内存中状态,以防止其从一个测试传播到另一个测试。 在setUp()中,我调用resetState方法来清除数据事件状态。 在ShowsRepository中,我仅在内存中使用Realm来在每次运行测试时都不会在磁盘上保持状态,这可以防止单元测试相互干扰。 让配置= Realm.Configuration(inMemoryIdentifier:“ InMemoryRealm”) 假物件 进行单元测试必不可少的是假对象。 您可以在此处阅读更多有关为什么虚假物品如此重要的信息(“一种科学实验”)。 在应用程序中,我使用了虚假的应用程序委托。 委派的虚假应用只是对不执行任何操作的真实应用代表的空替代。 当启动应用程序时,检测目标是否与单元测试的目标匹配,如果是,我将实际的应用程序代表替换为伪造的应用程序代表。 因此,每次运行测试时,该应用程序都不会启动实际的View Controllers / Views。 这种方法允许以更隔离的方式运行单元。 这样对单元测试的运行时性能有好处,并且不会导致意外的后果。 单元测试中使用存根和模拟。 查看项目代码,您可以找到一个带有存根的文件夹,另一个带有存根的文件夹。 依赖注入 在单元测试中,可以用实物代替存根和模拟物,以具有更可控的环境。 当您在ViewController中执行tableView.delegate = self时,您将使用依赖项注入,因为委托是MapKit实现中的依赖项,因此您正在注入它。 是的,即使您还没有注意到,您多年来一直在整个UIKit进行依赖项注入。 代理和数据源隐藏在协议后面。 对于存根,程序员可以使用接口/协议以及带有覆盖的依赖项或子类的注入。 当然,接口选项比子类更好,因为它导致较少的耦合。 假设您有一个从A类继承的B类,而您的目标是测试B类。可以创建一个从B类继承的C存根。这里的主要问题之一是B类已经带来了很多A类行为,很难摆脱它们(或者它的僵化或不可预测性)。 您想要做的只是保持相似或可代表但可控制且可预测的行为。 为了进行依赖注入,我使用了一个名为Swinjec t (https://github.com/Swinject/Swinject)的第三方库。 没有第三方库也可以建立依赖关系。 但是,Swinject将依赖项存储在Container中,因此更容易将依赖项维护在中央位置,从而提高了代码的整洁度和凝聚力。 要获得实例,您可以执行以下操作: 让person = container.resolve(Person.self)! 这样,您无需在实例化时传递参数。 注册依赖项时传递参数。 使用Swinject,甚至可以将类转换为Singleton,而无需触摸该类的文件(开放式封闭原理)。 Swininject resolve方法尝试获取与特定协议关联的实例。 在单元测试中,我们使存根实现特定的协议(在实际应用中由实对象实现),并通过使用register方法将存根与协议相关联。 注入依赖关系允许更大的责任分离,有助于遵守Liskov原则,Open Close原则和依赖关系反转原则。 […]

在移动文本冒险中为OOC事件创建框架

编码开始:Systers的GSoC 我已经开始上一堂课,为我们的游戏PowerUp处理故事序列。 我认为它更多的是设计挑战,而不是编码挑战。 我想确保它易于使用,并且对其余应用程序功能的影响最小。 我希望其他人认为“嘿,还不错。 我可以为此创建内容并添加它。” 所以这是清单: 应该可以将其添加到应用程序中任何位置的视图层次结构中,并且这样做应该尽可能简单。 序列应可自定义,但数据模型应易于阅读和设计。 繁重的工作将由班级本身来完成。 另一个开发人员不需要三页的Wiki即可实现序列。 这没什么大不了的,但是需要正确地做。 我绝对不喜欢复杂的代码模式,这些模式会使很酷的工具体积庞大且无法使用。 1为了使它易于使用并立即被其他iOS开发人员所熟悉, StorySequencePlayer是UIView的子类。 即使类是控制器,也不必是UIViewController。 该计划是通过将视图呈现为叠加层并在完成呈现内容后使用委托来处理解雇来简化其生命周期。 在外部,唯一添加到现有视图控制器的代码应该看起来很熟悉: //获取模型,初始化并添加到视图 func startSequence(){ 警卫队让模型= getTheModel()其他{返回} let view = StorySequencePlayer(代表:自我,模型:模型) self.view.addSubview(view) } //取消最后一步时调用此委托方法 func sequenceDidFinish(发送者:StorySequencePlayer){ sender.removeFromSuperview() }

Swift:第一时间获得正确的MVC

开发人员为什么倾向于在其视图控制器中填充视图? 在Swift中使用MVC的更好方法呢? 我遇到的大多数开发人员都倾向于做真正奇怪的事情。 他们中有些人避免洗衣服和发臭,像地狱一样。 其他人则喝加盐和胡椒粉的咖啡。 但是我看到的最常见的行为是,开发人员不使用视图,而是喜欢在视图控制器中执行所有与视图相关的操作,例如填充,创建动画等。 首先,假设我们有一些描述用户的结构: 其次,我们的任务是用该用户的数据填充一些演示文稿。 许多开发人员的代码,甚至是Apple在其教程中提供的代码,看起来都像下面的怪兽: 罪人,听我说,谁会说:“哦。 嘿。 那就是我通常写的代码。 实际上,这没有什么错。”实际上在几个层面上确实是错的: 视图的内容取决于您何时设置模型。 它不可扩展且不可维护; 这是不可重用的。 而且,我们不应该忘记使@IBOutlets变弱,因为视图控制器的view属性可能会更改,并且我们将对视图持有强大的引用,对此我们不承担任何责任。 如果模型是在视图出现后设置的(例如,它是从Internet下载的),则不会在屏幕上显示,除非进一步进入导航层次结构然后返回。 有很多人在野外用viewDidLoad做到这一点,但更糟糕的是,更新内容的唯一方法是用新模型创建新的视图控制器。 即使这样,如果在将控制器推入导航控制器后设置模型,也不会显示该模型,因为在推入过程中已经调用了viewDidLoad 。 可扩展性和可维护性在这里也确实很痛苦。 您的视图控制器负责填充整个视图层次并为其设置动画。 目前,只有2个子视图,但是想象一下,您有20个甚至更多。 此外,您必须为它们设置动画或根据模型数据更改它们的外观。 viewWillAppear很快就会变成一团糟。 至于重用,请设想一下这种情况,当您希望在不同的视图控制器上呈现相同的视图和模型关系时,具有何时以及如何获取和处理模型的逻辑不同。 例如,在表格视图中将其显示为单元格,或者将其呈现给来自不同API端点的另一种用户。 在我看来,确实很奇怪,网络上没有那么多声音,他们提出了一种更好,更简单的选择。 在大多数情况下,开发人员不会反对编写任何东西: 让我们考虑一下。 UILabel具有一些复杂的绘制逻辑和字符串处理功能。 它的作用是显示字符串。 但是视图的字符串是什么? 这不是很明显吗? 这是模特!!! 那么,为什么我们的代码在使用自己的视图时却归结为我之前模拟的代码? 有些人会开始争论,那是做MVC的正确方法。 MVC是一种设计模式。 苹果公司在其代码中做到了这一点,我们应该承担义务。 你知道,这使我想起什么? 使用20个类和协议编写具有hello world的企业Java项目。 当然,Java开发人员可以简化事情,但是他们严格遵守GoF和其他主流设计模式,忘记了它们是建议,而不是严格遵循的规则。 让我们从我坚持的观点出发,对我们的代码和演示进行推理。 我们的每种观点都与某种模型紧密相关。 它的设计和呈现方式仅适用于一种模型。 证明? 您将无法使用用户展示屏幕展示考试数学问题。 它的设计不同,子视图也不同。 例如,同样适用于UITableView,但是在这种情况下,我们应该将其模型视为模型数组。 在那种情况下,它适合于1模型与1视图关系的相同方案。 同样适用于动画和其他内容。 View Controller不应在实现细节的底层上处理这些事情。 […]