前边以及陆陆续续的介绍了使用Swift3.0开发的服务端应用程序的Perfect框架。本篇博客就做一个阶段性的总结,做一个完整的实例,其实这个实例在《Swift3.0服务端开发(一)》这篇博客中已经简单的介绍过了,本篇博客就来详细的聊一下这个工程的具体实现细节。当然包括iOS端和服务端的代码。本篇博客的介绍顺序按照功能模块来划分的,如登录注册模块、记事本列表,记事本的增删改查等功能。在每个功能模块,我们先给出服务端代码的实现,然后给出客户端代码的实现。
本篇博客的前几部分主要介绍整个工程的公用模块,为工程的实现做准备,下方就是我们今天博客要做的东西。本篇博客iOS端的网络请求主要使用的NSURLSession来实现的,关于URLSession更详细的介绍请参考之前发布的博客《NSURLSession全家桶》
一、记事本数据库的设计
数据库的设计以及数据库表的创建我都使用Sequel Pro来实现的,关于Sequel Pro的使用请看上篇博客的介绍,本篇博客关于Sequel Pro的介绍就不做过多赘述了。首先我们先给出记事本数据库表的设计,以备使用。我们先创建一个名为perfect_note的数据库(步骤略),然后再创建相应的数据库表。因为我们的记事本比较简单,主要包括登录、注册以及记事本的增删改查。所以我们的数据库结构也是比较简单的,perfect_note数据库中只有两个表,一个是user表,一个是content表,下方会给出详细的介绍过程。
1.user表的创建
首先我们来创建user表,user表负责存储用户信息,当用户注册和登录时都会操作这个表。注册用户时就是往该表中插入用户,登录时就是查询相应的用户信息。当然,为了Demo的简洁性,我们的user表中的字段也是比较少的。下方就是创建user表的sql语句。其中有四个字段,主键id是整型而且是自增的,是用户的唯一表示。username字段存储的是用户名,password存储的就是用户密码。register_date存储的是用户注册时间,是时间戳,并且默认值是当前时间。
CREATE TABLE `user` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT,`username` varchar(30) CHARACTER SET latin1 NOT NULL DEFAULT '',`password` varchar(30) CHARACTER SET latin1 NOT NULL DEFAULT '',`register_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
2.content表的创建
创建完user表后,接下来就要创建我们的content表了。content表用来存储用户录入的笔记,下方就是content表的创建sql语句。从下方的sql语句中不难看出content表的字段包括自增的主键id,记录的标题title,记录的内容content,以及外键userID和创建时间create_time。
CREATE TABLE `content` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT,`title` varchar(30) CHARACTER SET gb2312 NOT NULL DEFAULT '',`content` text CHARACTER SET gb2312 NOT NULL,`userID` int(11) unsigned NOT NULL,`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`),KEY `USER_FOREIGN_KEY` (`userID`),CONSTRAINT `USER_FOREIGN_KEY` FOREIGN KEY (`userID`) REFERENCES `user` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
二、iOS端基于NSURLSession网络请求类的封装
创建完数据库后,接下来我们来封装iOS端网络请求的共用代码。也就是说,iOS端的网络请求就会调用本部分封装的内容。当然本部分封装的网络请求类是使用NSURLSession类封装的。
1.字符串常量、闭包回调类型以及枚举的定义
首先我们先来定义一些封装网络请求类要使用的字符串常量以及枚举闭包回调。下方代码段做的就是这件事情,第一个框中定义了解析响应数据时使用到的字符串常量。“SUCCESS”表示请求成功,“FAILE”表示请求失败等等。
第二个框中定义的是三个闭包变量,用来将请求结果回调给调用者。RequestStart就是开始请求要调用的闭包类型,RequestSuccess则是请求成功后调用的闭包类型,RequestFailed则是请求失败要调用的闭包类型。这三者是请求类对外交流的桥梁。
第三个框则是请求方式的枚举,主要包括GET、POST、PUT、DELETE,当然还留了CUSTOM()自定义的扩展类型。在该枚举中的description计算属性负责将当前的枚举对象转换成其对于的字符串,具体如下所示:
2、网络请求基类的创建
接下来网络请求的基类,所有与网络请求相关的类都要继承自此类,下方的BaseRequest就是我们网络请求的基类。该类比较简单,主要声明了上面定义的三个闭包类型的变量,然后给出了相应的构造器。具体如下所示。
3.网络请求类的封装
接下来我们使用NSURLSession来封装我们的网络请求类,下方的Request类就是我们封装的网络请求类,该类继承自BaseRequest。下方是Request的部分代码,下方每个方法对应着GET、POST、PUT等请求,可以结合者REST一起使用。在每个具体请求的方法中会调用sessionDataTaskRequest()方法。会给这个方法传入不同的请求方式以及路径和参数。稍后我们会给出sessionDataTaskRequest()方法的具体实现,sessionDataTaskRequest()方法其中就使用了NSURLSession相关的内容发起了网络请求,具体请看下方对sessionDataTaskRequest()方法的详细介绍。
下方这个代码段就是sessionDataTaskRequest()方法的整体结构,首先我们根据函数的请求路径和参数拼接URL字符串,也就是第一个框中的部分。在该部分中的query()函数是将参数进行URL编码转换,这个函数是从AlamoFire框架中摘过来的。然后创建请求用的URLRequest对象。最后是创建Session对象发起DataTask任务了。当然请求的结果是在completionHandler闭包中进行处理,稍后会给出completionHandler闭包中的处理方式。
接着,我们给出请求成功后,对json数据的解析以及对返回结果的处理。下方就是completionHandler闭包中的代码片段。首先对服务器返回的json数据进行解析,解析后将json数据转换成对应的数据类型。然后根据响应报文的result字段来进行相应的操作。如果报文响应正常,就调用success()闭包,否则调用failure()闭包,如下所示:
至此我们iOS客户端的网络请求部分就封装完了,其他具体业务逻辑的网络请求调用上述的Request类即可,稍后会用到Request。
上面的基础工作完毕后,接下来我们就要来做我们相应的业务模块了。首先我们来进行登录注册模块的开发工作。 首先给出服务端相应模块的代码,然后在给出相应模块的iOS端的实现。关于Swift3.0连接和操作MysqL的详细内容请参考上一篇博客《Swift3.0服务端开发(四) MysqL数据库的连接与操作》,数据库的连接在本部分就不做过多赘述了。
1、服务端代码
下方代码是用户登录或者注册的第一步,通过用户名来查询用户信息,从而来判断该用户是否注册,如果未注册则去注册,如果注册过就去登录。如果查询成功,那么就将查询的用户ID和UserName返回给客户端。用户登录的代码和下方差不多,就是通过Select语句来匹配该用户名的密码是否与用户输入的一致,在此就不做过多赘述了。
下方就是用户注册是调用的接口实现,主要是插入相应的用户信息,具体如下所示:
上面这些代码写完后,配置完相应的路由调用上述方法,我们的服务端代码就完成了。具体路由的配置因为篇幅有限,本篇博客就不做过多赘述了。
2、iOS客户端代码实现
接下来我们来实现iOS客户端的登录和注册的代码,下方就是登录或者注册的相关UI。用户输入用户后,点击下一步,会调用后台接口判断用户是否注册过,如果已注册输入密码登录,如果未注册就输入密码注册和登录。右边的UIViewController是共用的,两个页面,一个让用户输入用户名,一个则负责接收密码。UI比较简单,如下所示:
看完UI,我们来看一下登录或注册的相关网络请求的代码。下方的UserInfoRequest类就负责所有与用户信息相关的网络请求,从下方的代码截图中,我们可以看到UserInfoRequest的基类是BaseRequest。下方的queryUserInfo(userName)就是上面左边的页面所调用的方法,用来判断该用户是否是注册过的用户。在queryUserInfo()中对Request类进行了实例化,并且调用了相应的请求方法。并且对相应的事件回调做了处理,具体如下所示。
在我们相应的ViewController中会调用上述的方法,下方就是用户在输入相应的用户信息后点击next所调用的方法。通过相应的闭包事件,最终将网络请求的结果回调到了VC中。
至此我们iOS客户端的登录就实现完毕了。 其他的代码和上面的思路类似,在此就不做过多赘述了。
本篇博客,就先到这儿吧,其他代码和上述的思路一直,按照上述的思路去实现笔记的增删改查即可,在此就不多废话了。完整Demo请移步github相关链接。