![a858de6779150615bdfefc879b6d065d.png](https://img-blog.csdnimg.cn/img_convert/a858de6779150615bdfefc879b6d065d.png)
这是我们第一次写较多代码。当我们写过一些代码,我们就知道,写文档往往比写代码更复杂更费时间。这也是我们看到很多程序没有文档、文档不全和文档不祥的原因。但是一个程序如果没有文档,就无法好好的继承、好好的被使用,所以文档是必须的。程序文档有很多种,但对于我们开发人员来说,有功能说明、业务流程、数据结构等就可以了。但当我们写好文档,回头一看程序又变了,看怎么办,得把文档找出来再改呗。如此反覆,烦不胜烦,大部分程序员是这样死掉的。那怎么办,许多程序员使用的是折中的方法是:先把程序里的注释写得尽量详细再说,免得一回头,自己都不知道以前在干嘛,哈哈,我们也先这样。首先,一个类的头部注释是说明这个类能够干什么,要写细哟;然后,各属与方法也得写清楚;但不要以为这样就完了,你还有N多东西要说明,这些放在哪里我就不知道了,所以最后,我们还是需要补上文档。
另外,动手之前,我们还需要熟悉一下Swift 编码规范。
文件模块是我们需要攻下的第一个城池。它有文件读写功能,解压与压缩功能,数组、字典与数据模型互转功能,JSON编码与解码功能。这些功能是一个个碉堡,我们要逐个击破,然后一举拿下城池。最后,积兵储粮,继续挥帜北上,去完成我们的春秋霸业。
〜〜〜〜〜 概述 〜〜〜〜〜
基于“iWriter”设计,我们需要保存作品信息(info.txt)、作品目录(catalog.txt)、作品角色(role.txt)、作品符号(symbol.txt)、章节正文(chapter章节ID.txt)、正文的备注(note备注ID.txt)等数据内容。其中前四者为随新建文件时建立存储文件,后两者由使用者在作业过程中创建。
- 功能及流程。文件模块有新建文件、打开文件、保存文件、另存文件等一系列跟文件操作有关的功能。下面就主要功能作流程说明如下:
新建文件:
- 获取用户指定的保存位置path及文件file;
- 在缓存区清空或新建临时文件夹iwriter;
- 在iwriter文件夹内新建文件info.txt、catalog.txt、role.txt、symbol.txt;
- 为info.txt写入初始内容;
- 将iwriter压缩到用户指定位置path,并命名为file;
- 完成整个新建文件过程。
打开文件:
- 获取用户指定打开的文件file(含位置path);
- 在缓存区清空或新建临时文件夹iwriter;
- 将file解压到缓存区iwriter。注意,本程序只保持一份缓存,也就是说只能打开一个编辑实例;
- 在iwriter打开Works,即加载其数据;
- 完成整个打开文件流程。
保存文件:
- 保存Works数据到缓存区iWriter的各文件里;
- 将缓存区iwriter打包,覆盖到用户指定的保存位置path,覆盖其文件file。
- 完成整个保存文件流程
另存文件:
- 重新获取用户指定的保存位置path及文件file;
- 保存Works数据到缓存区iWriter的各文件里;
- 将缓存区iwriter打包,覆盖到用户指定的保存位置path,覆盖其文件file。
- 完成整个另存文件流程
- 单元测试。单元测试是针对程序模块进行的正确性检验的工作,以保证其正确性与健壮性。单元测试在开发中极为重要,是程序质量的重要保证。一般程序开发中会使用PDCA来管理其质量,我们大多会以模块为输出节点,在进行PDCA时,单元测试是其Check部分。
下面我们从数据逻辑与业务逻辑两个方面来实现文件模块的功能。并通过单元测试Check我们的输出是否符合我们的设计要求。
〜〜〜〜〜 正文 〜〜〜〜〜
到这里,单靠以前我们所学的Swift基本知识还是远远不够的。我们还需要跟据我们实现的功能去查找并了解一些类与库,一般使用Google搜索与看文档是我的主要方法。对于我们一个新手来说:首先,要做一个“搜索程序员”;然后,再去做一个“文档工程师”。
我预计了一下,文件模块所用的类与库有这些:
- NSOpenPanel,显示窗口,供用户选择打开的文件;
- NSSavePanel,显示窗口,供用户选择文件保存位置与设置文件保存名称。
- FileManager,文件操作。
- String、Data、JSON类,方便将数据写入到文件或从文件里读出来,还有是跟数据模型间的转换。
- ZipArchive库,第三方提供的zip压缩与解压功能的库,所以我们这里会涉及到第三方库管理的问题。
第三方库管理:大家都用CocoaPods管理第三方库,这应该是个非常好用的工具,但是由于某些原因实际上非常不好用。如想使用它,请参考墨彻的CocoaPods详细安装及使用教程。
一、配置程序对系统权限。
在开发文件模块之前,需要为应用程序开启对本地资源应用的权限。请按下列步骤操作:其中第5步是获取打印权限,后续会用到;第6步是获取用户选择的文件夹的读写权限,另外,还有获取下载文件夹的读写权限,下载文件夹的读写权限是为了下面的单元测试用,“iWriter”中用不着,后期会关闭。
![2b9100311e00679564cf35d26ae96b74.png](https://img-blog.csdnimg.cn/img_convert/2b9100311e00679564cf35d26ae96b74.png)
- 添加第三方库。
以ZipArchive为例,点击打开Github相应页面,选择右边“Clone or download”的绿色按钮,在下拉项里点击“Download Zip”,等待下载完成后,请解压备用。
下面是说说自己管理与应用第三方库:
- 在Navigator Area里选择Subject栏的Venders项,点击右键。然后选择右键菜单中的‘Add Files to “iWriter”...’项;
- 在打开的文件流览器中,找到备用处,选择“ZipArchive.xcodeproj”,确认添加;
- 将“ZipArchive.framework”添加各”TARGETS“的第三方链接库:
- 在Navigator Area的Subject栏里,选择第一个“iWriter”后,再选择Editor Area的”TARGETS“中的”iWriter“项,同时选择上方水平栏“Build Phases”项;
- 在页面中的“Link Binary With Libraries”栏里,点击其下面的“+”,跳出的framework流览窗口,请选择“ZipArchive.framework”,注意选择带“mac”那项,确认添加“ZipArchive.framework”。
- 按该方法,请为”TARGETS“的”iWriterTests“和”iWriterUITests“,同样添加“ZipArchive.framework”。
![6c07f4b3da4b9a04833d478288e5d1ea.png](https://img-blog.csdnimg.cn/img_convert/6c07f4b3da4b9a04833d478288e5d1ea.png)
![e8e7b9463dd66f478133b93d203da2a2.png](https://img-blog.csdnimg.cn/img_convert/e8e7b9463dd66f478133b93d203da2a2.png)
![f3869dc156b9f3e1851487be9177526b.png](https://img-blog.csdnimg.cn/img_convert/f3869dc156b9f3e1851487be9177526b.png)
使用CocoaPods管理与应用第三库:
- 如果没有网络问题,CocoaPods管理与应用第三库非常简单。在安装好CocoaPods后,我们需要准一个Podfile的文件,并放在工程根目录下;
- 在文件里写上我们需要的第三方库(请确认该库是否支持CocoaPods),并设置是否用在主工程、单元测试工程、UI测试工程等;
- 在Terminal里,进入到工程根目录,运行:$ pod install,如果运行成功就完成了;
- 如果想移除某第三方库,在Podfile文件里,使用“#”符注释掉该行,然后再在Terminal里,进入到工程根目录,运行:$ pod instal,如果运行成功就移除完成了。
- Podfile文件:
source 'https://github.com/CocoaPods/Specs.git'
#inhibit_all_warnings!
platform :macOS, '10.14'
set_arc_compatibility_flag!
def import_pods
pod 'SSZipArchive'
end
target :iWriter do
import_pods
end
target :iWriterTests do
import_pods
end
target :iWriterUITests do
import_pods
end
二、数据模型与数据逻辑支持。
我们为“iWriter”设计了四种数据模型,分别是:Info、Catalog、Role、Symbol。要说明的是这些数据完全可以使用Array、Dictionary代替,而且还简单。我也不知道为什么要这么做?为了MVC模式?MVP?MVVM?不知道。同时为了好让这些数据模型转为Array、Dictionary,我们又设计了一个"WorksDelegate"协议。最后又为了数据的更新我们设计了一个“Status”。
- 建立Struct,步骤如下,后续章节不再讲:
- 按住“command+N”,在“Choose a template for you new file”窗口中选择菜单“macOS”,在“Source”栏选择“Swift File”项,确定“Next”;
- 在文件流利器中,选择“Models”文件夹,输入文件名称,确定“Creat”,完成Struct文件的创建。
- WorksDelegate协议,Models文件夹下WorksDelegate.swift。
import
- Info、catalog、role、symbol等数据模型,Models文件夹下对应Info.swift、catalog.txt、role.txt、symbol.txt等文件。
数据模型的实现方式一样。
其中catalog有属性level: Int、title: String、info: String、creation: Int、number: Int,
而role与symbol有属性name: String、info: String、creation: Int。
import
- Status结构体,Models文件夹下的Status.swift。
保存最近Works数据的MD5,判断基于新数据是否需要保存。
import
三、数据逻辑与业务逻辑的实现。
逻辑的实现分三部分:首先,建立菜单项的Action,也就是要有监听与处理用户操作行为的方法;其次,实现Action,也是业务逻辑部分,同时需要为部分操作提供后续用户交互;最后,处理Action所产生数据及保存问题,即数据逻辑部分。
- 建立Action。
文件模块是支持与实现菜单操作,所以需要为对应的菜单项建立建立Action。本例将最后一次讲述如何创建Action,后面所有章节中不再讲述其操作流程。
- 将Editor Area切换到Assistant模式,左为Main.storyboard,右为AppDelegate.swift;
- 选其中一菜单项,按住“control”,拖菜单项到右边类主体的方法区,在跳出窗口中设置Action方法;
- 所有Action建好后,将其划分为extension里,以便于管理与查看。
![80a20f3cd9acda8c0a1161c41bf5c885.png](https://img-blog.csdnimg.cn/img_convert/80a20f3cd9acda8c0a1161c41bf5c885.png)
- 实现业务逻辑,所有代码均在AppDelegate.swift。
文件模块中打开文件、新建文件、别存文件涉及到了文件浏览器窗口,对应Swift分别需要NSOpenPanel与NSSavePanel类的支持。其中新建文件、另存文件调用的是NSSavePanel类。
//
其中openFile()、newFile()、saveAsFile()需要打开文件流览器,让用户选择或设置文件(含路径),以传给数据逻辑进行处理,而saveFile()直接启动数据逻辑处理。由于涉及到文件操作与JSON数据转换,所有以数据逻辑在处理旱均会抛出异常。我们需要接收并处理异常,本案的方法是将异常中的错误信息输出(后期输出到log),话术信息则显现给用户。
//
- 实现数据逻辑,所有代码均在Works.swift。
Works.swift的主要内容是Works类。创建Class文件时,需要按创建Struct文件的步骤,只是在第一步中不选择“Swift File”,而选择“Cocoa Class”。我在实现Works类之前,还需要定义两个枚举,一个WorksError用来处理异常,一个OperateCode用来表明引起异常的原因。
/**
这里我们使用了带参数的枚举,以便将出错的信息传递出去。
Works类。公共部分,是映射Action,并按业务流程处理数据;私有部分,处理数据文件的读写;其余的,提供额外支持。其中,文件读取没有使用FileHandle,因为每次数据更新写入之后需要压缩为“.iw”文件,造成反复打开关闭FileHandle,比较麻烦。
/**
四、单元测试。
单元测试所做的事情,我们以前靠Print与断点调试来解决,现在当然还在用。我们之所以需要使用单元测试,更多的原因有三点:首先,单元测试可以使用更小单位,这意味着测试更加便捷与有效;其次,单元测试有更灵活的输入,这意味着我们不仅可输入即定数据,还可以输入脏数据,以检验程序的正确性与可靠性;最后,单元测试有更完备的输出验证手段。
写本章时,因为建立与完成单元测试,耽误了一整天的时间。Xcode在第三方库管理与应用上并不复杂,我们按上面所说的做就行了。但我在Works类测试时,一直出现测试失败,于是各种找方案、各种修改“.iw”保存文件夹的权限,都行不通。最后,原来是测试时预定的“.iw”保存路径的有问题,由于不是用户选择、也不是在App Sendbox里,所以没有作何操作权限。这也是为什么一开始,我们需要在App Sendbox里为Downloads Folder添加读写权限的原因,测试时我们只能将“.iw”保存路径设置在Downloads Folder里。
- 了解单元测试的结构。
由于我们新建“iWriter”工程时勾选了"Include Unit Tests"和"Include UI Tests"项,所以现在可以在Navigator Area的Test栏里,看到“iWriterTests”与“iWriterUITests”项。打开“iWriterTests”主件有如下四个方法:
import
- 数据模型的测试。
测试了数据模型与Dictionary的转换、接收脏数据是否符合预期,下面以Info为例。
/// 测试Info数据模型
- 数据逻辑的测试。
基于操作进行了测试,操作有新建、保存、另存、打开。
- 新建,断言缓存区默认的文件是否创建,是否按用户指定路径创建文件;
- 保存,断言修改的数据是否保存,章节是否创建;
- 另存,断言是否按用户指定路径保存文件;
- 打开,断言章节内容是否相同,断言数据是否确。
/// 测试Works数据模型
- “iWriter”原码,请访问我的Github的iWriter工程。
下一篇,布局模块的实现。
让我们在这里,遇见明天的自己!姜友华