最近被公司那个架构松散,底层混乱,缺少规范的代码烦死了,决定把公司的项目重新弄一个,将之前的MVC改成MVVM,并将代码有OC迁移到Swift,搭建新项目的iOS框架,一个紧凑高效的App框架,可以为你以后的代码路省下很多麻烦。
前银联移动支付首席产品架构师这样说:
我做(开发)架构的几个原则,根据优先次序高低排列:1. (逻辑)拆分越细越好 2. 依赖关细越少越好 3. 交互越少越好 ... 相互矛盾时,如果没有特殊理由,以优先权高者胜出。
一、Idea
首先根据产品需求和设计图,脑中先建立一个产品架构(当然你最好就是给它弄一个思维导图,我比较推荐XMind和MindNode):
-
MVVM or MVC?
架构和建筑很相似,只是前者会随着时间的推移进行演变,好的架构催生好的代码,就像赶紧整洁的房子里,会想到其家具、电器、摆设等都是很有条理的。
MVC:模式简单,标准粗放,容易滋生捷径;
MVVM: 某种程度上比MVC好,但是很多场景没有覆盖到,比如缺少页面间的跳转/通信、数据获取等。
(还有种很少用的VIPER:细致,但是臃肿)
那么问题来了:这么多系统框架,我改选哪个?
个人觉得,无论哪种都是可以的,关键是要简洁、结构清晰、职责明确、符合GUI编程的特点。
这次我要说的是来自微软的MVVM:
ViewModel:相对于MVC引入的试图模型。是试图显示逻辑、验证逻辑、网络请求等代码存放的地方。任何试图本身的引用都不应该放在ViewModel中。这样解决的Controller的臃肿,不用什么逻辑,网络的东西都丢给试图控制器来完成,使其耦合性降低
- 让你的代码覆盖更多的场景
根据你的产品的类型,预计未来的需求,比如IM:语音,视频,音乐:播放器,放小点范围,比如一个个人中心页面,你就要考虑很多东西,比如头像,而头像你要考虑上传照片等等,需求会越来越多,但这都无所谓,只要你的框架包容性比较强,要不然越到后面,你就忍不住要重写你的代码了。
二、搭建目录结构
1.应用入口
1.AppDelegate是应用的代理,应用级的事件都委托它处理,包含启动退出、推送等事件,以及IM、支付等第三方的回调,这使得AppDelegate内代码庞大,错综复杂,十分不利于阅读和维护,swift里面有一个extension,学会使用这个让你的代码看起来更加有条理。
2.功能模块
- View可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
-
Model对应服务端的 Objects。
3.ViewModel我们处理业务逻辑的核心层,在这里我们需要发起网络请求(如果网络请求较多,可以抽出来,只在ViewModel里调用)、解析数据、转换数据给前端,还有就是一些逻辑的处理,比如下拉刷新的控制等等。
3.工具类
- Tools文件夹内主要包含全局通用工具,来源于对三方框架的二次封装,或是自己写的工具类。在这个项目里,我封装了网络请求工具,下拉刷新,一些控件的扩展,还有一些空间的封装等。
4.基类
- Base文件夹用来存放项目的基类,基类作用包含一些定制化的内容,例如页面样式,空数据页面等,使用基类来实现,可以统一控制,利于维护,减少冗余,也为更清晰。
5.Pods三方管理
- CocoaPods是iOS项目的依赖管理工具,开发iOS项目不可避免地要使用第三方开源库,CocoaPods的出现使得我们可以节省设置和第三方开源库的时间。网络请求:AFNetworking、Alamofire;自动布局:Masonry、SnapKit;数据库:Realm、FMDB;解析数据:YYModel、SwiftyJSON;图片处理:SDWebImage。
还有很多优秀的三方库,可以上github搜搜,没事看一下大神些的三方库也是受益匪浅的。
程序猿长得可以保守,思想一定不能太保守。代码还是不要重复造轮子,有现成的就用现成的。
6.桥接文件
由于用swift来写代码,但是有时候你也要用到OC的三方库,这个时候你就需要用到桥接文件来进行混编了,步骤很简单:
- common+n选择Header File
- targest->buildsetting->搜索bridg
- 更改Objective-C Bridging Header的值,格式为:项目名/header文件名.h
- 在你的桥接文件里面导入OC文件的头文件,这样你就可以在你的项目里面愉快的使用OC方法了
7.封装网络工具
这里我用AFNetworking来进行的一次封装的网络单例,还可以对齐进行二次封装,具体根据和后台的通信来处理
class YanNetworkManager: AFHTTPSessionManager {
/// 静态区/常量/闭包
/// 在第一次访问时,执行闭包,并且将结果保存在shared中
static let shared: YanNetworkManager = {
// 实例化对象
let instance = YanNetworkManager()
// 设置响应反序列化支持的数据类型
instance.responseSerializer.acceptableContentTypes?.insert("text/plain")
return instance
}()
/// 使用一个函数封装 AFN 的 GET / POST 请求
///
/// - Parameters:
/// - method: GET/POST
/// - URLString: URLString
/// - parameters: 参数字典
/// - completion: 完成回调(json,是否成功)
func request(method: WBHTTPMethod = .GET, URLString: String, parameters: Any?, completion: @escaping (_ json: Any?, _ isSuccess: Bool)->()) {
// 成功回调
let success = { (task: URLSessionDataTask, json: Any?) in
completion(json, true)
}
// 失败回调
let failure = { (task: URLSessionDataTask?, error: Error) in
if (task?.response as? HTTPURLResponse)?.statusCode == 403 {
print("Token 过期了")
// 发送通知(本方法不知道被谁调用,谁接收到通知,谁处理!)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: WBUserShouldLoginNotification), object: "bad token")
}
print("网络请求错误 \(error)")
completion(nil, false)
}
if method == .GET {
get(URLString, parameters: parameters, progress: nil, success: success, failure: failure)
} else {
post(URLString, parameters: parameters, progress: nil, success: success, failure: failure)
}
}