我编写了一个简单的命令行工具,用于从macOS终端将屏幕快照更新为Imgur。 在开发此工具时,我做了一些注释以供将来参考。 API整合 在花哨的项目中使用API是很常见的。 需要我们实施的真正的API转换实际上是有限的。 对于这个项目,我们只需要: 用户授权 刷新用户授权 开机自检图片 因此,最好列出所需的API,然后仔细编写可重用的函数。 束 处理项目的资源文件的路径非常令人困惑,因为二进制文件的工作目录在编译时和运行时可能会有所不同。 使用Bundle对构建项目的全面结构非常有帮助。 对于迅速的框架,macOS应用程序或iOS应用程序,Xcode将为您创建plist和一个简洁的Bundle。 但是,对于快速的CLI,我们可能需要对其进行一些配置。 如果要在运行时访问文件,则需要:1.转到项目2.选择目标3.在“ Build Phases添加“ New Copy File Phase 4.添加所需的文件 然后,您可以使用Bundle.main.path()搜索路径。 Bundle.main.path(forResource: “config”, ofType:”plist”) 读取配置文件? 再想一想 我当时是从Swift SPM构建项目的,但后来更改为使用XCode命令行工具项目,因为SPM不支持资源。 在实现Imgur的API时,我尝试使用网络框架Alamofire ,但是在CLI中安装它时遇到了很多麻烦。 此外,我发现“复制文件阶段”没有复制回文件,因此更新的配置将根本不会保存。 然后,我意识到我不需要从头开始读取用户配置,只要二进制文件可以访问我长期存储的配置文件即可。 最后,我放弃了XCode以使用SPM,并使用’Boundle`方法在产品目录中写入用户配置文件。 Bundle.main.bundleURL.appendingPathComponent(“config.plist”) 具有OAuth的API 对我来说,这个项目中最困难的部分是弄清楚如何在我的快速代码中转换Imgur的API。 我强烈推荐API模拟应用Postman。 使用独立的GUI测试API逻辑和参数要容易得多。 但是,即使我对POST请求使用了完全相同的标头和正文,我遇到的问题是“ URLRequest”和“ URLSession”不起作用。 这是我关于Stackoverflow的问题。 事实证明,主体数据有许多编码格式。 这是有关Stackoverflow的有用参考。 application / x-www-form-urlencoded→默认 multipart / form-data→输入文件时使用 文字/纯文字→不推荐 我在Postman中使用了“表单数据”格式,但在“ […]
在过去的九个月里,我一直在学习Swift。 说我正在爆炸是轻描淡写。 但是,每当我要举行快速聚会时,街上都会有一辆警车在等着将其关闭。 噢,封锁,谁伤害了你? 所以我最终决定学习闭包,并考虑记录我的奋斗 , 学习和最后的阿哈时刻 。 这些大多数都是我从不同来源学到的东西,但发现自己希望自己在一个地方找到。 这些内容包括但不限于Bob the Dev的博客,快速的文档以及有关Medium的惊人文章。 为什么叫名字? 闭包可以从定义它们的上下文中捕获和存储对任何常量和变量的引用。 这被称为关闭它们,因此被称为closures。 您看起来很熟悉,我以前在哪里见过您? 闭包由于其干净且经过优化的语法而处在许多地方。 您可能在什么时候遇到过他们; 使用动画; 这是因为某些功能是异步执行的。 这是使用escaping闭包的绝佳机会。 从第三方API获取数据; 这也是使用escaping闭包的另一个绝佳机会。 在视图控制器之间传递数据 Swift的许多内置函数都需要关闭。 这是一个; 闭包函数 Apple将闭包定义为传递的自包含功能块。 在我们过于技术化之前,我们可以了解如何将函数转换为闭包。 这是一个简单的函数,用于查找必须学习闭包的时间。 它需要两个参数, days和name然后返回一个字符串,告诉我我还剩下多少天。 优雅吧? 😃 将其转换为闭包; 除去花括号 当然,如果您尝试按原样运行上述代码段,则会收到错误消息。 这是介于闭包和函数之间的东西,也许我们可以称其为乐趣。 没有? 好的! 😄 在参数列表和正文之间(即在String和return之间)添加关键字 这仍然有一个错误。 尽管这是一个有很多观点和观点的有争议的声明,但闭包本质上是无头函数。 因此,我们取出头部😈。 在这种情况下,头部很func learnClosures 还是不运行? 用花括号将其包围 most。 这是最冗长形式的所谓闭包表达式。 稍后我们将看到,您可以将其精简为一行。 现在,我们基本上已经声明了一个闭合,但是没有一种调用它的方法。 解? 我们将其分配给变量。 […]
最近我一直不安。 通常,我一直在寻找一种途径来传播我的创造力。 因此,在过去的几周中,我一直在思考我可以创建的应用程序的想法。 我希望这段旅程能帮助我提高编程技能。 我将在博客中记录我的进展,希望可以与其他志趣相投的开发人员建立联系。 工具类 Xcode 9和Swift 4用于前端开发 MindNode集思广益 Evernote做笔记 Todoist用于任务跟踪 后端的Node.js 数据库仍然是一个问号 用于后端开发的Atom GitHub用于源代码控制 对我而言,开始使用Node.js非常容易。 虽然学习曲线绝对是选择技术时要考虑的重要方面,但我担心我最终会被JavaScript的泥潭所困扰。 (#sorrynotsorry) 第一步 第一步是让我在Node.js上运行API,并将其连接到我的客户端应用程序。 在获得了选择了正确技术的概念证明之后,我将开始手工进行一些接线。 想帮忙吗? 该应用程序将围绕目标设定。 我希望收集有关人们为自己设定的目标的信息。 请留下您自己设定的目标类型的评论。
在开发我们的iOS应用程序“ Lines”时,我遇到的最大问题之一是创建可靠的图像上传队列。 该应用程序支持离线使用,因此,如果用户保存图像,则照片的本地标识符存储在CoreData中。 连接设备后,将立即获取并上传所有图像。 为了创建有效的上传队列,我必须使用以下框架: Alamofire用于http网络。 为了避免在获取多个图像时出现内存泄漏,我使用了OperationQueue来限制要处理的并发图像的数量。 而且BrightFutures将保证整个过程的顺利进行。
最近,我被问了很多以下问题:“为什么还要使用Objective-C?” 或“为什么不使用Swift?” 在Slack频道,Twitter,Podcasts上,所以我决定在我的Medium帐户上开设一个更详尽的答案,说明为什么我仍在使用Objective-C。 免责声明:我不是在这里说什么是对还是错,应该做什么和不应该做什么,我只是在阐述为什么Swift不适用于我 。 简介 最近,我们迎来了iPhone SDK诞生10周年的纪念日,这实际上使我意识到我已经使用iOS已有10年了。 在发布SDK的那一刻,我下载了它并开始使用Xcode,发布了一些应用程序,并在一两年后获得了我的iOS开发人员正式职位。 这就是说我对Objective-C感到很自在,而且当新技术问世时我也大肆宣传,并面临这样做的后果,这使我在开始使用新技术之前至少思考了3倍。科技火车。 迅捷的自己 首先要考虑的是Swift本身,它是一种语言。 好不好吗? 比Objective-C好还是坏? 这些都是复杂的问题,由于涉及很多个人喜好,因此很难客观地回答。 我喜欢它,我认为语法是好的,它用途广泛,并且易于阅读(这通常对开发人员本身造成的负担比对语言的负担更大)。 总的来说,我认为这是一种不错的语言,我真的很想更频繁地使用它,这是我将开始列出不这样做的原因的部分。 可怕的更新 这变得越来越不成问题了,但是Swift是一种新语言(2014年),与它初次出现的Objective-C相比,更新自然会很快(而且非常生气),并且会出现重大变化,新语法等,因此非常迅速。 1984年。每年,Swift更新都会破坏以前的代码,有时需要大量工作,有时需要简单的快速修复,但几乎像我的消遣方式一样,阅读有关Slack / Twitter的评论,人们对其项目未编译感到焦虑/紧张。使用某些库已更新到最新的Swift版本的库,而另一些则没有,必须管理所有这些库。 在Objective-C世界中不存在的问题。 几周前,我在Mac(iOS 4/5)上发现了一些非常老的项目,为了娱乐,我决定打开并尝试运行。 令我惊讶的是,一切正常。 讨厌的纹理和UI不能很好地老化,但是可以正常运行。 当然,获得一个有6年历史的项目并运行它不是一个非常常见的用例,但是,另一方面,我最近帮助了一位继承了Swift项目的朋友,除了项目本身,该项目在大约2年内没有更新。如果不进行编译,则所有依赖项也都已过时,其中一些依赖项从未见过新的Swift版本支持。 但是正如我之前写的那样,Swift越来越稳定,并且很快将不再是问题,但是直到今天,我仍然看到Slack上的人们由于Swift版本的迁移而遇到了问题。 尽管如此,如果这是我考虑在新项目中使用Swift的唯一问题,不幸的是,并非如此。 迅捷工具包 这就是我为什么不使用Swift的最大原因所在。 当我说“工具包”时,我指的是与Swift一起使用的所有东西,以拥有一个不错的开发环境:Xcode,LLDB,LLVM,Instruments等。 Xcode对Swift的支持几乎是可笑的,语法突出显示不起作用,编译时间非常长,缺少Swift重构,最重要的是,LLDB根本不起作用。 LLDB是我工作流程的重要组成部分,不仅用于调试,而且还用于实际编写代码,自动化流程,编写日志等。我写了一篇关于葡萄牙语使用LLDB的事情的帖子(葡萄牙语),但它们并没有在Swift中无法使用。 使用Objective-C,我可以使用调试器命令执行很多断点操作,有时我会使用LLDB表达式编写整个方法,然后对结果感到满意时,甚至对某些我喜欢使用的日志,都会对实际代码进行转录LLDB +断点操作,而不是NSLog。 现在您可能正在考虑“但是您可以在Swift中做所有事情”,我说“您不能”,这不是因为该功能不存在,不是因为LLDB在Swift中不存在,而是因为它们是超级越野车 。 一个简单的po命令几乎立即在Objective-C项目上运行,在Swift上,它最多可能需要4秒钟才能返回(在最大容量的2015 MacBook Pro上),而当它很幸运时,它就会变慢,因为更多通常,调试器连接只会丢失,而我知道解决此问题的唯一方法是重新启动Xcode 和模拟器。 我相信,这通常是许多对Xcode的抱怨的源头,我看到人们抱怨Xcode有错误,这种方法不起作用,我只是不能与此相关,而不是在我使用Objective-C时。 去斯威夫特还是不去斯威夫特? 总之,我认为Swift是一种很棒的语言,我可以在macGist这样的业余项目中自己使用它,这是一个很棒的示例,如果您想看到一个不熟悉Swift的人开发的Swift项目以及一个AppKit项目一个不了解AppKit的人,但是每次我打开一个Swift项目时,都要花几分钟的时间,直到我想念Objective-C世界。 不是因为语言,不是因为我的语言效率更高,而是因为该工具包可以正常工作。 有些人可能会发现过去,为了利用Swift的出色优势和性感的语法,我做不到。 我提到的所有这些问题,语言更新和工具包都不是Objective-C的问题,这对我来说很难证明更改的合理性。 我真的希望今年可以为Swift改进和优化新的开发工具,希望我可以使用Xcode而不会使代码完成或语法突出显示崩溃,我可以在LLDB上运行表达式而不必等待音高下降的等效时间。 在那之前,我将对所有我认为“关键任务”的东西都使用Objective-C。 我真的很喜欢Swift作为一种语言,但是Swift作为开发解决方案仍然不是我的节奏。
在Coletiv的当前项目之一中,我们需要不断保持数据在多个移动设备之间的同步。 我们想到的使状态保持最新的第一个解决方案是每分钟与推送通知相关的服务器轮询服务器。 显然,就性能和可靠性而言,这并不是最佳的解决方案。 由于我们的后端是使用Phoenix制造的,因此最初的天真解决方案自然就演变为使用Phoenix Channels。 在本文中,我们将介绍我们使用游戏“史莱姆足球”实现概念验证以测试Phoenix Channel的经验。 对于两个玩家来说,这是一个简单的游戏,每个人控制一个粘液,目标是进球。 概念证明已在Swift中作为Mac应用程序实现,但可以轻松地以您选择的任何其他语言或平台实现。 这个想法是让游戏的两个实例在不同的计算机上运行,每个实例都将通过Phoenix频道连接到Phoenix服务器。 服务器负责管理游戏得分并处理两个玩家之间的通信。 有几个Swift开源项目可以帮助进行渠道沟通: PhoenixWebSocket 鸟鸣 Swift Phoenix客户 我们之所以选择使用Birdsong,只是因为我们认为目前最适合我们需求的项目。 它具有简洁的代码样式,对服务器执行定期ping操作,并允许进行一些自定义操作,例如处理断开操作。 如果要继续学习,可以在此处找到服务器源代码,在此处找到客户端源代码。 下图描述了游戏过程中Mac客户端与服务器之间的一般通信流程。 服务器 要在Phoenix中定义一个新通道,我们只需将此行添加到UserSocket.ex中: 频道(“游戏:*”,SuperSlimeGameWeb.GameChannel) 我们的连接功能非常简单,因为我们选择忽略身份验证步骤,因为这将意味着需要进一步的开发,而这并不是我们进行此测试的目标,但是如果您需要身份验证,则可以在此进行检查。 客户 在connect方法中,我们定义了onConnect和onDisconnect处理程序。 连接/断开套接字后,将调用这些闭包。 在我们的测试中,我们将在连接套接字后立即加入通道。 当插座断开连接时,将在短暂的延迟后执行重新连接。 服务器 该应用程序的主要部分是加入功能,玩家可以在其中加入游戏。 信道术语和有效载荷结构定义如下: 通道命名 “游戏:” 代码 有效载荷结构 %{“电子邮件” =>电子邮件} 游戏代码game:[a_code]用于检查游戏是否已经存在于为管理游戏而创建的GameState代理进程中。 如果游戏已经存在,我们将添加通过有效负载结构传递的新玩家,否则将创建新游戏并添加新玩家。 GameState代理用于保存游戏和比赛玩家,它作为受监督的子进程与服务器一起启动。 要启动代理,将工作器添加到Application.ex中,如下所示: 工作者(SuperSlimeGame.GameState,[]) 游戏状态代理实施 客户 在join方法中,我们定义了通道事件监听器。 如果从通道返回了特定的操作(即播放器已移动),则将执行这些侦听器。 验证套接字的状态以确保维持连接的持久性很重要。 服务器 收到playerAction命令后,服务器会将其广播给所有加入该频道的播放器。 客户 每次更新场景时,都会向通道发送一条消息,其中包含本地玩家的坐标和动作。 身份验证方法-连接与连接 在此示例中,我们没有在通道连接中添加任何安全性,但这在现实环境中是必须具有的。 在Coletiv开发的其他项目中,我们需要验证用户身份以允许其访问其资源。 […]
虚拟屏幕,注释,模板,命名,静态表格… 上一部分在这里 您可能会注意到,在连接速度较慢的情况下,您的Facebook应用程序屏幕上充满了上图所示的虚拟对象。 看起来很奇怪,但比旋转的加载迹象要少刺激性。 Tumblr应用程序,存储和加载您最后存储在本地存储中的卡。 这比上面的怪异屏幕要聪明得多。 为什么? 在惰性癌症综合征中,人们可以做这样的事情 func getUserData(回调:@转义(_结果:字符串)->()){ let myUrl = URL(string:“ https://www.google.com”); var request = URLRequest(URL:myUrl!) 让会话= URLSession.shared; request.httpMethod =“获取” request.addValue(“ application / json”,对于HTTPHeaderField:“ Content-Type”) request.addValue(“ application / json”,对于HTTPHeaderField:“ Accept”) // request.httpBody = jsonData DispatchQueue.global()。async(){ let task = session.dataTask(with:request){数据,响应,错误 警卫让结果=数据,错误==无其他{ print(“ \(错误?.localizedDescription ??“未知错误”)”)); 返回; } 如果让httpStatus = response为? HTTPURLResponse,httpStatus.statusCode!= 200 { print(“ […]
欢迎回到我的Swift开发日记的另一个条目。 如果您关注我以前的记录,您可能已经注意到我不再关注每日报告。 这既是因为不可能一次完成多个任务,而是因为我认为这种内容毫无意义。 然后,我决定切换到“里程碑”模型-也就是说,每当我完成一个项目或取得一些突破时,我都会在这里报告。 如前所述,我正在学习Paul Hudson的Hacking with Swift系列文章,今天,我完成了第一个名为Storm Viewer的项目。 来自带有Swift的App Development课程的非常顺畅和渐进的路径,这令人震惊,因为同时展示了多少功能。 与做事相比,理解似乎排在第二位! 作为古典音乐家,我完全同意! 我在镜子前度过了几个小时,练习打开的琴弦,改变位置等等,而不必了解正在发生的事情。 我的老师曾经说过: «重复并重复一次,直到发现一些东西! » 。 现在我坐在完成的项目前面,感觉是成就的一种,也是困惑的一种。 根据我在最初的几个月的代码学习中了解到的,似乎需要多次遍历该项目的文件,以便一步一步地解决问题。 这当然很合理,但我感到有些迷茫,无法追溯自己的脚步。 因此,我将在此处概述项目建设的进展情况,以便对我进行回顾,并为您提供可能有趣的阅读材料。 通常在这些“简单”项目中会发生这种情况,默认模板是iOS的Single View App 。 解决了Xcode中的初始步骤和Simulator的选择之后,就可以将一些图片导入项目了。 我曾经通过asset.xcassets目录来执行此操作,但是显然这将在以后的课程中进行介绍。 到目前为止,我只是将整个文件夹拖到Xcode项目中,注意选择“如果需要复制项目”复选框,并选择“创建组”而不是“创建文件夹引用” 。 到目前为止,这对我来说还很模糊,但我决定继续。 那里的图像本身几乎没有任何作用,因此是时候了解ViewController.swift文件了。 它包含ViewController类以及其内部的viewDidLoad()和didReceiveMemoryWarning()方法。 在viewDidLoad()内部,我们需要添加一些代码以使应用能够获取图像,使用hasPrefix(:)方法对其进行过滤,然后迭代结果,将其添加到由这些图像名称组成的字符串数组中。 在viewDidLoad()方法中编写一些步骤,然后返回并在其上面编写数组,这有点奇怪,但这似乎是正确的做法。 因此,此设置的第一部分看起来像这样(我已隐藏了以后将添加的部分)。 现在,我们需要使自己熟悉Interface Builder , Auto Layout等。 默认场景被删除,我们代替它绘制了TableViewController 。 当然,仅将其拖动并不会带来很多好处。 需要一些额外的步骤: 告诉Xcode这个新场景将由ViewController.swift文件控制(这是通过将声音更改为Identity Inspector的Class下拉菜单来完成的)。 更改入口优先级(我们希望此View Controller成为应用程序启动时第一个出现)。 在TableView中更改单元格的标识符(以便我们以后可以重新使用它以连接一些代码)+将Style选项更改为Basic。 将整个表视图控制器嵌入到导航控制器中。 默认情况下,TableViewController具有0(零)行,因此我们需要通过实现一些方法( tableView函数的所有子级)来更改该行: numberOfRowsInSection告诉Xcode我们将要显示多少行(该行等于图像数组的.count属性)-区段的数量为1使我们现在跳过该区段。 当我们滚动浏览可能无限数量的行时, […]
接口隔离原理说: “许多特定于客户端的接口比一个通用接口要好。” 在iOS中,该主题中的Interface可以转换为Protocol。 它说,许多协议要比具有一个通用目的的一个大协议(或类,结构或枚举)更好。 客户特定协议将是某些客户只需要的协议。 假设您有两个课程: 猫与人 : 类Human {func work(){} func sleep(){} func GiveFood()-> Food {return Food()} //其他功能} class Cat {var owner:Human var foodBowl:[Food] = [] func requestFood(){print(“ Meow,gimme food”)let foodScoop = self.owner.feedPet()self.foodBowl.append(foodScoop)} func eat(){self.foodBowl.removeAll()} init(owner:Human){self.owner = owner}} 如您所见, Cat是Human的客户。 它消耗人类的数据。 猫不能告诉它的主人去工作,也不需要知道那件事。 在此示例中,所有者需要的只是食物。 这就是隔离的来历。 正如我们所说,猫只在乎他的食物。 Cat没有理由暴露于类似于work()或sleep()之类的人类功能。 所以我们可以声明一个PetFoodProvider协议: 协议FoodProvider {func feedPet()-> Food} 因此,我们的客户( Cat )将仅使用(并将暴露给它)所需的东西: […]
在编程中,关于注释的使用一直在进行讨论。 一位出色的Swift博主兼书籍作者Erica Sadun过去写了很多有关评论的文章。 最近,安德鲁·沃纳(Andrew Warner)写了一篇文章,说用代码编写注释通常已过时,甚至对您的代码库有害。 评论衰减。 它们不会被编译,并且永远不会在运行时执行。 如果它们过时或不正确,则不会有测试失败,也不会有用户抱怨。 程序员绕着它们工作是因为担心“有人可能需要此注释,或者它将来可能会提供一些价值”,从而使它们在有用之后就走了很远(如果您甚至可以说它们一开始就有用)。 埃里卡(Erica)对这篇文章做出了很好的回应,强调了注释在代码库中的重要性。 注释不仅仅涉及不良的设计和编码。 他们记录了正确编写代码的过程,支持将来的阅读和修改。 好的评论减少了读者每次查看您的资源时所需的精力,使他们可以将更多的精力放在特定的任务上,例如“我如何添加此功能”,而不是“到底发生了什么”。 我完全同意Erica,并且我想强调一种特殊的方式,其中注释可以帮助您的代码读者。 注释不仅针对错误的代码 过去,我主要与比我聪明得多的高级开发人员一起工作,并且在代码中几乎没有注释。 我们在iOS方面经验丰富,通常不会在同一文件上工作。 因此,我们不会经常遇到代码可读性问题。 但是,在我们最近的项目中,我们招募了新员工,一个精明的Java开发人员仍在学习Swift和iOS。 这是我第一次发现我们的代码有时不可读。 在进行配对编程并看到他遍历我们的代码库时,我可以看到他很难辨别Swift或iOS的组成部分以及什么是我们的代码。 看起来他似乎可以很容易地理解一种方法及其作用,但是很难看到所有部分如何组合在一起。 对于UIViewController中更复杂的代码,尤其如此。 由于UI代码的性质,动画,生命周期方法,样式和表示之类的不同部分都混合在一起并且相互依赖。 在这里,我注意到我们的格言“注释仅针对错误的代码”散开了。 不管代码有多好,阅读代码都很困难。 弄清楚所有这些不同部分如何组合在一起并非易事。 作为开发人员,使阅读代码更容易是我们的工作。 提供背景 在写文章时,我总是特别注意要写的简介。 其目的是为读者建立上下文和动机,以使其了解文章中的查找内容。 如果您阅读的文章没有前几段,那么您将不得不花前几分钟来试图辨别本文的目的。 它是教程吗? 是新闻文章吗? 是广告吗? 在前几段中,您将注意力集中在尝试分析文章本身而不是实际内容上 。 您也没有必要的上下文来理解本文要说的内容。 一篇文章不仅仅是其段落的总和。 这些段落的流动方式和适合的方式建立了共同的线索,并达到了您想要实现的目的。 代码也一样。 一个类不仅仅是其公共方法的总和。 每个类都有责任(一个要点),并与周围的代码(上下文)相适应。 尽我们所能尝试,使用类总是会影响我们编写它们的方式。 没有方法与呼叫站点完全隔离。 每个类都有适合的上下文。 在读取代码时,如果没有类的上下文,则必须仔细阅读几次,以弄清其中的依赖关系以及它们如何组合在一起。 就像没有介绍的文章一样,这会浪费读者的时间,并且在其他开发人员(或将来的您)尝试更改某些内容时会导致错误。 这就是为什么我绝对建议在每个类(或复杂方法)上使用一个大的注释块,以实质上描述该代码的用途。 每段文字都需要一个简介,为什么一个班级会有什么不同? 永远记住,代码的读取远不止于编写。 针对读者而非作家进行优化。