我最终如何完成我的第一个副项目?
我编写了一个简单的命令行工具,用于从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中使用了“表单数据”格式,但在“ URLRequest”中使用了常规字符串或JSON格式。 似乎在使用base64编码进行POST时,Imgur仅支持“ form-data”。 当我实现refreshToken()
, x-www-form-urlencoded
格式可以工作。 参考代码在这里。
Postman还有一个很酷的功能来生成Swift代码。 尽管语法看起来有点怪异和混乱,但它的效果很好。 我发现自动代码生成现在非常流行。 我知道一个JSON解析器生成器,它真正利用了Swift 4 encodable
功能。 在现实生活中的项目中,使用现有的自动化方法比重新发明轮子或编写乏味的代码总是更方便。
秘密永远是秘密
secret
是在Imgur的API注册阶段提供的特殊代码。 我将使用的唯一情况是刷新用户的访问令牌。 Imgur(OAuth)使用它来动态保护用户的隐私。 因此,开发人员应自己跟踪到期时间并使用“ refresh_token”刷新令牌。 一旦“ access_token”被刷新, refresh_token
也将被更新。
为了跟踪到期日期,我使用了Swift的Date类型,尤其是timeIntervalSinceReferenceDate
表示形式,因为Imgur响应在几秒钟内到期。
secret
不应该共享(Github等),相关讨论在这里。 然后,我决定制作两个版本的代码。 一种是包含refreshToken()
功能的二进制文件,另一种是在Github上共享的源文件(没有refreshToken()
和secret
)。 Swift中有一个超强的功能,可以让我做到这一点:
// Define a protocol to hide refreshToken implementation
@objc protocol Imgurable{
@objc func postImage(from image: String, anony: Bool) -> String
@objc func authorizeUser()
// Use optional to build with/without refreshToken function
@objc optional func refreshToken()
}
if (self.api.isExpire()){
if ((self.api as Imgurable).refreshToken != nil){
// refreshToken is implemented (binary file)
(self.api as Imgurable).refreshToken!()
} else {
// Did not implement refreshToken (compiled from source by user)
self.api.authorizeUser()
}
}
使用此协议,我可以在一个单独的swift文件( gitignored
扩展可选功能。 在运行时,我只需要检查此optional function
是否已实现并决定刷新或重新授权。
Swift 4中的访问控制
从Swift 4开始,我们可以访问扩展名中的private成员,但仅限于同一文件扩展名。 换句话说,如果我在单独的swift文件中扩展refreshToken()
,则无法访问ImgurAPI
类的私有成员。 因此,我将一些私有定义更改为internal
。 回顾Swift如何处理访问控制将很有帮助。
开放访问和公共访问使实体可以在其定义模块的任何源文件中使用,也可以在导入定义模块的另一个模块的源文件中使用。 指定框架的公共接口时,通常使用开放访问或公共访问。 开放和公共访问之间的区别如下所述。
内部访问使实体可以在其定义模块的任何源文件中使用,但不能在该模块外部的任何源文件中使用。 在定义应用程序或框架的内部结构时,通常使用内部访问。
文件专用访问将实体的使用限制为自己定义的源文件。 当在整个文件中使用特定功能的实现细节时,使用文件专用访问权限可以隐藏这些细节。
专用访问将实体的使用限制为封闭的声明以及该声明在同一文件中的扩展名。 当仅在单个声明中使用特定细节的实现细节时,使用私有访问权限可以隐藏这些细节。
分发产品
最令人满意和最麻烦的步骤肯定是结束项目并交付它。 这是我第一次真正在Github上“发布”辅助项目。
自述文件
拥有良好的形象永远不会错。 我录制了该项目的gif演示。 许多人建议使用QuickTime Player录制视频,然后使用ffmpeg
将其转换为高质量的gif。 对我来说,扭曲ffmpeg
的配置非常烦人,所以我改用LICEcap。 就质量和图像大小而言,结果比我的ffmpeg
结果更好。
在自述文件上拥有Travis CI的构建状态标志就像一种趋势。 我也很喜欢 要使用Travis CI,我只编写了以下简单的.travis.yml
。 如果我添加了很多测试,Travis CI将非常强大。
os:
- osx
language: swift
osx_image: xcode9
install: swift package update
script:
- swift build
家酿
自制绝对是macOS上最酷的东西之一。 现在,它支持每个第三方开发人员通过brew tap
分发他们的项目。 要发布您的项目,我们需要在另一个名为homebrew-projectName
上托管一个公式,并添加一个公式文件。 您可以在公式中编写非常复杂且功能强大的构建/测试脚本,但我只是告诉自制程序链接我的预编译二进制文件。 这里有一个教程。
class Clip2imgur < Formula
desc "A simple macOS command line tool for uploading your copied image to Imgur"
homepage "https://github.com/xiaohk/clip2imgur"
url "https://github.com/xiaohk/clip2imgur/releases/download/v0.9.0/clip2imgur-0.9.0.tar.gz"
sha256 "a1d4dbf13b91ef53d3fe1ec77accad331d44705e2b91000e3177c84764657dc1"
bottle :unneeded
def install
bin.install "clip2imgur"
end
test do
system "#{bin}/clip2imgur", "--help"
end
end
到底
编写此CLI真的很有趣,而且我学到了很多有关Swift和SPM的新知识。 借助出色的macOS API Cocoa
,我们确实可以轻松实现魔术。 Imgur将该项目正式列在了不起的工具列表中。
显然,上面的屏幕截图是由clip2imgur
和❤️生成的。