您要创建同一iOS应用程序的多个版本,但会有些微差异吗? 对于我们的语言学习应用,我们确实遇到了这个问题。 我们有一个应用程序可以学习泰语,而我们希望使用同一应用程序的另一版本来学习汉语。 因此,这两个应用应具有以下差异: 不同的应用名称 不同的捆绑ID 加载了不同的数据库(中文和泰语) 不同的应用程序图标 应用程序中的不同资产,例如图标和音频文件 让我们逐步解决这些问题。 1.为每个应用程序创建新的配置 首先,您需要为应用程序的调试版本创建一个新的配置,为应用程序的发行版本创建一个新的配置。 在Xcode中,通过在项目导航器中单击项目根文件夹来打开项目信息。 在左侧,选择您的项目。 确保在顶部选择“信息”。 在“配置”下,按“ +”以复制您现有的调试配置。 然后再次按“ +”以复制您现有的发行版配置。 对于我们的示例,我们将配置命名为“ Thai Debug”,“ Thai Release”,“ Chinese Debug”和“ Chinese Release”。 2.调整配置设置 现在,我们需要调整每个配置的设置,以反映两个应用程序之间的差异。 为了简单起见,我们直接在Xcode中更改设置。 (如果您想改用.xcconfig文件,强烈建议您先阅读本教程。) 更改捆绑ID:确保在项目导航器中选择了根文件夹。 在左侧,选择您的项目目标。 选择顶部的“ Build Settings”,并确保选择“ All”(相对于“ Basic”)。 搜索“产品包”。 在“产品捆绑包标识符”部分中,为不同的配置提供以下标识符:“ com.example.thai”(用于“泰国发行”),“ com.example.thai.debug”(用于“泰国调试”),“ com.example”。 “中文”表示“中文发行版”,“ com.example.chinese.debug”表示“中文调试”。 更改应用程序名称:再次,选择顶部的“构建设置”,然后搜索“产品名称”。 在“产品名称”部分,将名称更改为“ Learn Thai”,“ Learn Thai Debug”,“ Learn Chinese”和“ […]
飞行猕猴桃 每种语言都有自己的规则。 查看语法,语言功能或语言类型都没关系。 一切都改变了,同时保持不变。 在我们的主题应用程序中,我们将使用swift,但是由于Objective-C是iOS开发的起源,因此我们绕了弯路,以了解此处的具体操作。 我不是XCTest的忠实拥护者。 不要误会我的意思,它确实可以正常工作..但是就这样..就可以了。 了解我们要测试的应用程序和操作的整体状态,取决于我们命名测试的能力。 总的来说,我们很讨厌这样做。 一种替代方法是基于RSpec的框架。 他们在我们的测试中添加了语法糖,可能只是为了了解测试2小时或解决问题而有所不同。 猕猴桃 对于Objective-C,这是猕猴桃。 它提供了我们可以描述,正在测试什么以及处于何种状态的不同阶段: describe :描述被测系统(sut) 上下文 :描述缝合状态 关于这些定义,我们可以编写一个带有演示类的简单测试文件结构: 私下入侵 我已经提到每种语言都有自己的怪癖。 在Objective-C中,私有不存在。 相反,它仅对编译器可见。 我们仍然可以通过运行时将消息发送到对象的特定实例,从而忽略可见性。 好的..我们不会开始使用objc_msgSend。 不用担心 相反,我们可以使用类别来完成这项工作。 在我们的测试类中,我们将添加一个类别,其中包含我们要测试的所有私有属性和方法,这些属性和方法无法从外部访问。 这样做将为编译器提供可见性,即使它们被认为是私有的,我们也可以将任何消息发送给对象。 存根 时不时地,我们不得不依靠我们无法控制的API。 这可以是简单的SDK API或网络服务。 想象一下有一个登录视图控制器。 您如何测试呢? 尤其是当您的测试应该在没有外部依赖性的黑暗洞穴中运行时,该怎么办? 欢迎进行嘲笑和存根。 在本文中,我们将讨论如何在单元测试和集成测试中对方法调用进行存根。 存根的想法是通过指定返回值或简单地替换它来定义方法调用的行为。要存根方法,我们需要对象对此方法做出响应。 有了这个,我们可以简单地通过编写以下代码来定义返回值: [testClass存根:@selector(方法)和Return:Value]; 该值可以是任何值:布尔值,NSString,NSInteger或您返回的任何值。 猕猴桃不在乎。 这样,我们可以简单地替换本地网络服务器的登录方法。 是不是很简单? 但是,当我们无法获得所用类的实例时,我们该怎么办? 铁杆存根 为此,有方法令人毛骨悚然。 这是Objective-C内的黑魔法的一部分,不应粗心使用。 但是,如果您知道自己在做什么,并且将其用于测试,则可能会从中获得一些价值。 那么什么是方法混乱? 基本上在运行时,您将另一个方法添加到一个类中,并用另一个方法切换该方法的签名。 关于我们先前描述的登录方法,让我们添加另一个名为swizzleLogin的方法。 通过使用swizzleLogin修改登录方法,现在每次登录都将执行swizzleLogin,反之亦然。 这很容易做错,所以让我们介绍一下JRSwizzle。 […]
我用swift做了一个简单的倒数功能: 我创建了一个runTimer函数,该函数使用Timer.scheduledTimer在每个周期调用updateTimer函数,当秒等于零时,计时器停止并且正在调用新的segue以显示播放UI。 func runTimer(){ timer = Timer.scheduledTimer(timeInterval:1,target:self,选择器:(#selector(self.updateTimer)),userInfo:nil,重复:true) } func updateTimer(){ 秒-= 1 如果秒== 0 { counterLabel.text =“ Empezar” timer.invalidate() self.performSegue(withIdentifier:“ startSegue”,发送者:self) }其他{ counterLabel.text =“ \(秒)” } } 我试图向其中添加一些动画,但是我认为没有动画的结果看起来还不错: 下一步是从字母表中选择随机字母,并将其显示在倒数计时器旁边3分钟,例如下一个原型: 我将继续发布…
最新图片或Alamofire图片 Olágalerinha。 Dessa vez vim mostrar算法和简单应用可用于下载iOS上的免费图像。 Para quem acompanhou um tempoatráscoloquei em 3 passos como criar uma pokedex。 Criando uma Pokedex com Swift零件:1/3 Instalando o CocoaPods medium.com Criando uma Pokedex com Swift零件:2/3 Consumindo um JSON medium.com Criando uma Pokedex com Swift零件:3/3 Mostrando nossos monstrinhos medium.com Nessa pokedex temos o列表下载口袋妖怪图片。 Eles foram feitos包含API Alamofire。 Alamofire / […]
作为iOS开发人员,我一直着迷于编写甚至自己的应用程序的后端,以完全控制它。 我在工作时选择的语言是Swift,这让我惊讶不已。 它提供了真正的多功能用途。 除了对移动和桌面应用程序进行编程之外,它还被证明非常适合脚本编写,并且也越来越多地用于后端编程。 对于后端,显而易见的选择是Node.js。 服务器上的Javascript非常强大,而且由于其用途广泛,因此也易于访问。 围绕Node.js形成了一个大型社区,该社区现在生成了大量的各种库。 那为什么还要尝试新的东西呢? Javascript的主要优势在于它的传播; 但是,它往往会承担并非总是最好的体系结构决策的负担。 由于Javascript是松散类型的,因此基于Javascript构建的程序容易出现各种错误。 另一方面,从第一天开始,Swift一直是一种强类型语言。 但是,它仍然带有精心设计的类型推断系统,在该系统中,编译器可以从上下文派生类型。 Swift消除了我们从其他语言中知道的一些传统语法项,例如分号和多余的括号,从而产生了简洁,易读和有效的代码段。 Swift最初是一种仅限Apple平台的语言。 但是,它已经在版本2中向世界开放。新的开源方法使开发人员可以在主要的Unix平台上使用Swift,因此没有任何办法可以利用它进行后端编程。 旨在将Swift部署到服务器上的项目之一就是Vapor。 它不仅在其上构建的项目数量上非常出色,而且在它使用Swift的泛型和协议扩展等高级功能方面也非常出色。 让我们看一个例子。 以下代码显示了用于简单CD目录的REST API的一部分。 import Vapor import HTTP final class ArtistController { // Routes registration func addRoutes(drop: Droplet) { let group = drop.grouped(“artists”) group.get(handler: index) group.get(Artist.self, “albums”, handler: albumsIndex) let searchGroup = group.grouped(“search”) searchGroup.get(handler: search) } // GET […]
我决定写这篇关于如何构建watchOS应用程序来解决一个小问题的快速文章。 这里有一个随附的Stack Overflow帖子。 当我从隔夜充电的Series 0 Apple Watch升级到Series 3时,我切换到了更零星的充电时间表。 由于Series 3在一天结束时不会像原来的Apple Watch一样耗尽电池电量,因此我开始过夜佩戴它来跟踪睡眠。 然后,我会每天将其扔到充电器上一次或两次,以保持良好的充电效果。 随机充电足以使它在大多数时间都能正常运行,但有时我会把它留在充电器上而忘了。 有时候在我上床睡觉时会发生这种情况,所以我会入睡,错过了睡眠追踪。 2018年8月,我获得了Oura戒指以追踪我的睡眠。 它通过蓝牙连接到iPhone。 充满电且在有效范围内时,本地通知会到达iPhone: 我喜欢此功能,因此我想尝试将其复制到Apple Watch。 想法是在iOS应用中设置阈值(例如80%),并在配对手表的电池电量达到该水平时在iPhone上收到通知。 可能的解决方案#1 iOS上已经有“今日”小部件,可以显示配对的Apple Watch的电池电量,因此似乎已经有些频繁地将数据发送到iPhone: 这让我认为我可以构建一个简单的iOS应用程序,该应用程序要么观察此电池百分比值,要么使用后台任务定期检查该值,并基于对该值的更改发送通知。 在WatchKit中,有WKInterfaceDevice对象,可在iOS和watchOS上使用,其中包含有关已配对手表的信息。 虽然可以使用WKInterfaceDevice.current().batteryLevel在watchOS应用上获取充电百分比,但该属性在iOS端不可用。 看来Today小部件必须使用私有API才能读取配对手表的电池电量。 可能的解决方案2 我探索的下一条途径是让手表定期将其充电百分比发送到配对的iPhone。 我的计划是在watchOS应用程序中安排后台刷新任务( WKApplicationRefreshBackgroundTask ),以使用WatchConnectivity定期将消息发送到watchOS应用程序。 在佩戴Apple Watch时,这些任务似乎每小时至少运行约一次。 手表会将当前的电池电量发送到配对的iPhone,如果电量超过阈值,则会立即向用户显示本地通知。 在许多情况下,当iOS应用使用sendMessage从手表接收消息时,它将在后台启动,而当使用transferUserInfo则不会。 根据我的测试,这些WatchConnectivity消息将通过Internet而不是仅通过蓝牙发送,因此从理论上讲,当手表的充电超出蓝牙范围时,这也将起作用。 在这一点上,电池电量转移到通知部分是非常可靠的,但是后台任务被调用的频率却不是。 通过添加并发症,可以使手表更频繁地报告其百分比。 这可能会导致watchOS更加频繁地为应用程序后台任务分配时间,例如每15分钟左右一次,但这可能会浪费复杂的空间,除非将其用作系统电池电量消耗的替代品。 官方的并发症可能会更频繁地更新,因为它是操作系统的一部分。 不过,所有这些都不相关。 在充电器上时,这些后台刷新任务似乎完全暂停了。 这意味着当手表在充电器上达到或超过指定水平时,此消息传递解决方案无法将通知发送到iPhone。 佩戴手表时的电池电量通知对于管理电量仍然很有用,因为可以设置比系统的10%通知更高的阈值,并在手表电池电量不足时发出警报。 至少在电量很低之前,这至少可以让人们更加警惕,但是并不能解决最初的问题,即不主动检查“今日”小部件或手表本身而知道何时将Apple Watch充电到一定水平。 因此,这个想法现在几乎是死胡同。 我认为这有几种可能的路径: 苹果内置本机电量级别通知(似乎不太可能) Apple更改了WatchKit,以使必要的变量可以在iOS中访问(也许,但是除了我的通知提示,我无法想到其他用于该目的的其他应用程序) 如果后者发生了,也许我会重新考虑一下这个想法。 如果您还有其他解决方案,请在此处查看Stack Overflow帖子。 可能的解决方案#3 […]
如何将MVVM用于与RxSwift绑定的经典登录屏幕? 这就是我们的方法..(歌曲♩♪♫♬) 让我们玩! ..但首先,什么是MVVM? 你应该在这里看看.. Swift中的MVVM – Artsy Engineering 模型视图ViewModel已成为我在iOS上编写应用程序的默认方式–它使编写iOS应用程序变得很有趣。 我写了…… artsy.github.io 对于RxSwift? 您应该在这里找到所有提示和技巧https://github.com/ReactiveX/RxSwift 所有我们需要的.. 6个步骤 .. !! 现在..让我们开始: 1-模型 我们创建一个使用用户名和密码登录的模型 然后,我们需要创建将处理此模型的viewModel .. 2-FieldViewModel 我们定义此协议VSFieldViewModel来表示条目数据字段(在我们的示例中,我们有emailField和passwordField) 我们使用一些属性来描述字段,例如title和errorMessage 然后,我们定义observables( 变量值:Variable )以在需要时处理和更新用户界面,在本例中,字段值和errorMessage是稍后绑定的变量。 我们定义一个validate()函数来使用定义的模式来validate()数据 我们添加扩展名,以使用一些实用程序方法来调整字符串或有效模式的有效大小(以后用于有效的密码和电子邮件输入值) 3-密码字段viewModel 除了基本的FieldViewModel外,我们还添加了另一个协议以自定义密码输入的类型(安全文本) viewModel定义validate()方法(字符串输入stize) 我们对下一个viewModel进行相同操作。 4-电子邮件字段viewModel 这里的viewModel定义变量和自定义模式以验证输入值(电子邮件模式格式:xx@xx.xx) 如您所见,某些变量可以为空(甚至为nil),例如errorMessage ,该条目应在无效输入后进行更新 现在..我们应该创建我们的控制器viewModel,它将处理下面的这些字段。 5-登录视图模型 在这个viewModel中,我们将model emailFieldViewModel用来处理电子邮件数据的字段 passwordFieldViewModel用于处理密码数据的字段 validForm()验证所有输入数据 disposeBag附带的disposeBag来管理内存 isLoading isSuccess和errorMessage是由控制器管理并用于绑定UI的变量RxSwift 用于调用身份验证服务的signin()方法 我们从viewModel字段使用模型电子邮件和密码提出了请求 我们在响应时更新可观察对象以处理UI,首先更新isLoading状态,然后在成功更新isSuccess状态时更新 相同的错误时,我们将错误消息放在errorMessage 6-最后! 控制器 ViewController将配置UI和viewModel之间的绑定 […]
一个带有UserDefaults的。 永久数据存储是iOS应用程序开发人员掌握的一项重要技能。 对于大多数应用程序,您将要存储一些有关用户的信息,以便当他们返回应用程序时,即使关闭应用程序后,它们也可以立即获取其数据。 有多种方法可以实现此目的,我们正在研究本节中最简单的方法之一。 因此,我们将在这里使用的是UserDefaults。 您可能会想到,它用来存储有关该特定用户的一些基本信息,但实际上您可以用来在设备上本地存储任何类型的信息。 这也很容易设置。 打开一个新的xcode项目,然后在viewDidLoad()方法中键入以下代码。 我们将字符串(或任何项目)保存到UserDefaults中,该变量设置了一个名为“ key”的变量,并为其提供了一个值“ String”,当我们想取回该值时,我们只需使用“ key”即可。 当您运行该应用程序时,您将在日志中打印出您的字符串。 现在,为了确保已确实保存了数据,让我们再次运行该应用程序,但是这次我们将注释掉保存部分。 这样做完全相同(再次检查日志),这意味着数据将永久存储,并且只要加载应用程序就可以访问数据。 基本上,您可以将任何类型的数据存储到UserDefaults中,例如在对象中,如String,Date,Array,Integer甚至是Boolean。 为此,我们需要创建UserDefaults的全局实例。 再次注释掉存储值部分并运行应用程序,您将发现数据已永久存储。 检索对象时,结果是可选的。 这意味着您可以接受可选性,也可以将其类型转换为非可选类型,然后使用nil合并运算符处理缺失值。 例如: 因此,我们在这里检查的是,我们能够获取名称对象并将其转换为字符串,如果存在,则将其打印出来。 这是检查对象是否存在以及对象是否为某种类型的更安全的方法。 这就是关于UserDefaults的全部内容。 注意: NSUserDefaults已重命名为UserDefaults standardUserDefaults()已重命名为standard
当我第一次学习iOS编程时,我在Objective-C语法上苦苦挣扎。 但是一旦我适应了自己,它就会成为我最喜欢的语言之一。 它包含了很多哲学! 但是,由于Objc具有自己的样式,因此有时代码写得有点冗长。 对我来说,使用Objc语言制作通用模块时遇到了问题。 一旦完成,就需要一堆样板代码才能使用它。 当时,我决定制作自己的DSL(特定于域的语言)来解决我的代码问题,结果得到了结果! 这是东西! 这是我面临的情况 我的任务是制作一些“ 超级易用的团队成员 ”模块,该模块可测量用户首次交互的时间。 例如,当用户进入某个页面时,他们会执行某些操作,例如触摸加载显示,但是一旦该页面花费的时间太长,用户期望的操作仅仅是离开该页面,他们就不够耐心! 那么,名为TTI的交互模块的时间是几点? 它确定哪个组件需要花费多少时间来加载。 我知道Facebook如何请求API,但这是一个简单的示例。 为了显示下面的供稿页面,让我们假设它需要一个API请求来获取全部数据,并需要一个请求来从CDN服务器检索图像数据。 在这种情况下,TTI模块会测量这两个网络请求,并计算获取每个信息所需的时间。 好,那就开始吧! 通用Objective-C风格 我采用的模块设计是在加载页面之前声明所有API调用。 使用上面的示例, “哦,此页面将显示一个供稿。 为此,它需要一个api请求和一个image请求。 然后测量这两件事!” 可以将其更改为类似的代码; //一些viewController -(void)loadView { [TTICenter targetPage:@“ newsFeedPage”]; [TTICenter measureRequest:API_REQUEST名为:@“ feedRequest”]; [TTICenter measureRequest:IMAGE_REQUEST名为:@“ attachedImage”]; [TTICenter启动]; } 使用它似乎有些复杂,因为它必须遵循从“ targetPage”到“ start”的某些“方法调用过程”,这并不方便。 因此,我将其放在如下所示的一个方法调用中; //一些viewController -(void)loadView { [[TTICenter targetPage:@“ newsFeedPage” measureRequests:@ [API_REQUEST,IMAGE_REQUEST] 命名:@ [@“ feedRequest,@” […]
投资改写 在软件工程领域,没有什么比“重写”更令人恐惧的了。因此,当我们的技术团队提出理由这样做时,您可以想象在管理人员眼中的喜悦。 但是像图片一样,表情符号有时值一千个字… 移动开发发展迅速。 每年秋天,开发人员都会面对其操作系统和新工具的新版本。 加快您的古老代码库的速度变得更具挑战性。 开发团队发现自己正在为过时而战,而不是尝试新功能。 我们支持的一些过时的供应商代码位于Restkit和XML提要之上。 其中一些依赖于NSURLConnection,该类在iOS 7中不推荐使用NSURLSession。如果您提前计划,并且您的应用程序支持良好的抽象水平,则可以缓解此类问题。 但这并非总是如此。 我们设定了一个目标,以构建可满足我们未来需求的可扩展移动应用架构。 我们仔细查看了当前的旧版应用目录。 我们问自己,什么有效,什么无效,什么有效,足以改进和重用。 像许多旧版软件一样,逐个功能也变得复杂。 随之而来的是不断增加的技术债务。 我们的前端软件已接近极限。 我们的Feed架构也在遭受痛苦。 这些主要因素,再加上新的,现代的和灵活的API,巩固了我们的立场。 我们从利益相关者那里获得了绿灯,重新开始。 我们与内部API开发人员和产品经理合作,共同创造了未来的证明。 适合我们自己的技术,生产和业务需求的东西。 迅速还是不迅速? 大约一周前,Swift Evolution邮件列表上一个有趣的话题引起了我的注意。 它在主题行中承诺“认真! 在3.0版发布后将Swift冻结两年!” 我们从Swift 1.1开始我们的项目。 目前,Swift 2.3和3.0(又名“ The Grand Renaming”)正在测试中。 每次发布都很痛苦。 语言经常在我们下面改变。 调试和重构变得更加困难。 并且由于删除了本机方法curring,因此编译时间增加了。 但从积极的方面来说,Swift减少了项目中的文件数量。 简明扼要。 它的类型系统和泛型使我们无法编写大量代码。 它也是函数式反应式编程的重要合作伙伴。 标准的Swift库已经提供了您需要的很多东西。 而且,我们都可以同意,Swift比其冗长的前身Objective-C容易得多。 尝试现代概念和框架 大多数iOS应用程序使用MVC架构模式。 我们的要求使我们走了一条通往MVVM(模型视图ViewModel)的道路。 我们还将功能性反应式编程引入了我们的代码中。 在大多数情况下,我们在数据层中使用了它。 我们采用了Promises,这是并发编程语言(例如Scala)中使用的概念。 PromiseKit和ReactiveCocoa是必不可少的两个库。 PromiseKit是帮助程序功能的实现和集合。 它将Promises的概念引入了iOS,并阐明了复杂代码的意图。 使用干净的语法,可以轻松管理工作单元的顺序和/或并行执行。 它是线程安全的,并带有用于并发管理的出色实用程序。 […]