原文在我的博客:一份非常详尽的 Objective-C 到 Swift 的迁移指南

国际友人可以看这里:A guidebook for migrating from Objective-C to Swift

运行环境:Xcode 9,Swift 4.0/4.1

按照惯例先说几句废话,Swift 在刚发布的时候,我学过一点点,写了几行代码,第二年发现以前的代码不能跑了,就弃坑 Swift,再加上实习过的公司主要用 OC,确实没机会系统的学一下 Swift,近来发现一些想要的第三方库,都只提供 Swift 版本,以及一些其他原因,决定把公司的项目完全用 Swift 改写。

认识我的朋友可能知道,我在去年年底发过一篇文章,叫《从重构到吐血 - 我是如何删掉 6 万行代码并且不删减原有功能的》,当时花了几周时间重构了所有代码,三个项目。

最近也一样,花了三四天时间,重写了其中一个项目,并且整理出来一些经验。目前除了一些必须依赖的第三方库比如 AliyunOSS,全部转到 Swift 了,可以说是 Almost Pure Swift。

如果写太详细的话,篇幅就太大了,所以有些地方会省略一点写。

先大概列个提纲,我打算讲讲可选类型,重写的顺序,网络层,数据层,UI 层。

可选类型

我认为一门语言,语法奇怪不是很大问题,熟悉下就好,但是 Optional 类型是真的难理解,! ? ?? 这类符号傻傻看不懂,最开始解析个 json 到处都是 ?,再加上网上各种文档,素质参差不齐,越学越迷茫。

Optional 类型很好理解,只是区分了下 nil 和 非 nil,如果这个 property 不一定存在,比如后端传来的 json,有时候格式是空数组 [],有时候是 null,这两种在语义上理解都是空,但是对 Swift 语言是完全不同的。具体的我会在数据层详细写下。

重写的顺序

最开始的打算是慢慢迁移到 Swift,先从最边缘的模块开始写,UI 改版再重写以前的代码,后来越写越上瘾,感觉找回了本科做项目的感觉,通宵写代码,就索性全部重写了。

还有一个原因是写着写着发现有些通用的部分,和之前的 OC 代码有关联,新的模块用 Swift 写会有无法混编的情况,比如 Swift 的结构体,非继承自 NSObject 的类,在 OC 无法正常用。

总的顺序还是从边缘到中心,先写最边缘的业务代码,比如某个刷新列表,这个时候就要写 Swift 的网络层,数据层,这两块也可以和 UI 层掺杂着写。

网络层

Swift 的网络层一般做法是用 Alamofire,我们的 app 不算复杂,只是对 Alamofire 做一个封装就够了。最开始我执着于遵循 Alamofire 的链式调用,发现好鸡儿难,然后惊喜的发现 Swift 也有 block,于是用了模仿 OC 网络层封装的方式,做一个单例,封装下 request 方法。

单例的写法,有好几种,篇幅限制,我直接贴出最佳实践,至少是 Swift 4.0/4.1 的最佳实践。

class APIService {static let shared = APIService()
}
复制代码

然后就可以往里面添枝加叶了,比如在 init() 方法设置一些网络状态监控,一些通用的设置,我就贴一个精简版的,然后可以按照官方文档,写一个 AccessTokenAdapter,用来处理头部的授权信息。

lazy var sManager: SessionManager = {let l = (UserDefaults.standard.object(forKey: "AppleLanguages") as! Array<String>)[0]var defaultHeaders = Alamofire.SessionManager.defaultHTTPHeadersdefaultHeaders["User-Agent"] = "Customized UA"defaultHeaders["Accept-Language"] = "\(l),en;q=0.8"let configuration = URLSessionConfiguration.defaultconfiguration.httpAdditionalHeaders = defaultHeaderslet sManager = Alamofire.SessionManager(configuration: configuration)return sManager
}()
复制代码

在有些开源项目里面,网络层分层过于详细,url 封一层,每个请求写一个函数,而且写的还贼鸡儿丑,仍然一大堆重复代码,重复的 hard code 字符串,不知道这种封装的意义何在,每加几个 api,要新建一个类,然后写 url 层,再写每个 request 的函数,封这么多层,仍然到处可见字符串硬编码,还都是重复的。

关于 request 的封装,我做了非常基础的封装,毕竟 app 没那么复杂,Alamofire 的部分太长了,大概思路就是根据传过来的参数,设置请求的序列化方式,设置 headers,设置参数等等,为了方便一些不需要传参的 get 方法,我做了这么一个操作:

func request(path: String, success: ((Any) -> Void)?, failure: ((ErrorModel) -> Void)?) {request(method: .get, path: path, params: nil, paramsType: nil, requireAuth: true, success: success, failure: failure)
}func request(method: HTTPMethod, path: String, params: [String: Any]?, paramsType: ParamsType?, success: ((Any) -> Void)?, failure: ((ErrorModel) -> Void)?) {request(method: method, path: path, params: params, paramsType: paramsType, requireAuth: true, success: success, failure: failure)
}
复制代码

调用的时候大概就是这样:

APIService.shared.request(path: "/get/some-list/api", success: { (data) inlet array = data as? [[String: Any]] ?? []let data = try! JSONSerialization.data(withJSONObject: array, options: [])guard let items = try? JSONDecoder().decode([ItemModel].self, from: data) else {return}tableView.reloadData()
}) { (error) in}
复制代码

as? 是为了防止后端返回 null 而不是 [],如果真返回了 null,?? 的作用是给 array 一个默认值,保证 array 一定是 Array 类型,而不是 Optional,方便后面的解析。

数据层

Swift 在结构体方面真是强大了太多了,篇幅关系不写那么多,Swift 4 引入了一个原生 json 转模型的方法,而且我还发现一个国人,翻译了老外的文章,不注明原地址,当原创了。

原文:Ultimate Guide to JSON Parsing with Swift 4

原文写的很详细,代码不再贴了,需要注意的是,如果后端返回数据不够规范,多用几个 ? 避免 crash。

同样的,数据放在数据层处理,善用计算属性,举个例子

struct ActivityModel : Codable {let createTime: Datevar createTimeString: String {return createTime.formattedString(withDateFormat: "yyyy-MM-dd")}
}struct OrderModel : Codable {let currency: Stringlet status: OrderStatusvar statusString: String {switch status {case .deleted:return "Deleted"case .created:return "Created"case .paid:return "Completed"case .cancelled:return "Cancelled"}}
}
复制代码

UI 层

UI 层其实是最简单的,lazy load 直接用 lazy var get 重写,Masonry 布局代码可以很方便的转成 SnapKit 代码,UIKit 框架的代码直接翻译即可。

[aView mas_makeConstraints:^(MASConstraintMaker *make) {make.top.mas_equalTo(self);make.bottom.mas_equalTo(self).offset(-5);make.leading.mas_equalTo(self);make.trailing.mas_equalTo(self);
}];[bView mas_makeConstraints:^(MASConstraintMaker *make) {make.top.mas_equalTo(self.aView.mas_bottom);make.leading.mas_equalTo(self.aView).offset(12);make.height.mas_equalTo(45);make.width.mas_equalTo(45);
}];
复制代码

方法名用 copy paste 解决,然后开启编辑器的替换功能,将 mas_e 替换成 eTo(self. 替换成 To(mas_ 替换成 snp.); 替换成 )。老实说,这部分改写,是最轻松的?

其他

在重写的过程中,把 AppDelegate 改成 Swift 之后,发现不再需要 main.m 了,查询资料得知这是正常的,@UIApplicationMain 帮我们做了这件事情。

再就是有些函数可以用 extension 的方式写,可以写的很优雅。

总之,Swift 上面还是有着很多 Cocoa 的影子,尽管他有很多新特性,在设计模式方面,跟 OC 差异不大,也可能我入门时间短,写法太 OC 化,所以如果有类似的,还请多多指正。

重写工作也没那么难,我们的 app 虽然不大,其实也不小,有完整的用户模块,有购物模块,订单模块,支付模块,推送模块,几天时间就全部改写完毕,并且已经在测试,目前还没发现有很大问题。

最基础的模块先搭建,比如主题颜色管理,API 模块,一些工具类,基础框架搭好之后,因为 OC 代码可以被 Swift 调用,在开始的时候,做好计划,小的模块先调用 OC,避免一下重写很多模块,导致没有动力写下去。后面几乎全是体力活,就是时间问题了。

一份非常详尽的 Objective-C 到 Swift 的迁移指南相关推荐

  1. figma转换html,一份详细的从Sketch切换到Figma的迁移指南

    越来越多的设计团队开始从 Sketch 迁移到 Figma 这是大势所趋.但是这两个工具之间有差异,这篇文章我会为你分享一些无缝切换的技巧.  这篇文章来自 Buninux.com. 为什么要用这两款 ...

  2. 一份来自区块链行业的《高考志愿填报指南》

    来源 | 白话区块链 责编 | 晋兆雨 头图 | 付费下载于视觉中国 随着国内外越来越多主流机构.企业的热捧,区块链这一科技变革显然成为未来备受瞩目的一个信息技术新兴行业. 许多专业人士直言不讳地表示 ...

  3. 云盘数据库设计mysql_一份最实用的云数据库架构设计与实践指南(内含PPT)

    原标题:一份最实用的云数据库架构设计与实践指南(内含PPT) Tips:点击文末[阅读原文]或登陆云盘:http://pan.baidu.com/s/1bo9Ni7l 即可下载5月21日DBAplus ...

  4. 零伽壹解析 | 一份来自区块链行业的《高考志愿填报指南》

    随着区块链产业的蓬勃.政府的扶持,也吸引了学生及家长的关注,不少高考学子对区块链领域颇有兴趣,而区块链领域也恰恰需要相关的人才资源.最近这几年互联网相关的专业已经成为大学热门专业,也成为了不少学子的高 ...

  5. delphi控件切图界面闪烁_一份最详尽全面的UI界面切图命名规范

    关于UI界面的切图命名规范,U妹觉得关键是在于团队能够有一个统一的规则,所以这里只介绍一种通用的命名规则,当然大家也可以根据自己的实际情况去制定,这里只提供一种方法与思路,仅供参考. 规范的命名方式可 ...

  6. Objective中调用Swift代码

    原创Blog,转载请注明出处 http://blog.csdn.net/hello_hwc 步骤比较简单: 1.在Objective C工程中,创建新文件(command+N),选择Swift文件 2 ...

  7. 一份能帮你通过大厂之路的简历指南 | 算法岗

    点击上方"AI算法与图像处理",选择加"星标"或"置顶" 重磅干货,第一时间送达 文 |AI_study 前言 大多数人为了求职辛辛苦苦准备 ...

  8. 【科学派】一份关于「如何刷题」的究极指南

    刷题指南 我经常会在后台收到不少小伙伴的留言,不知道该如何开始刷题. 确实人的精力是有限的,盲目地从「任意起点」出发很难到达目的地. 因此一份[科学派]的刷题指南尤其重要. 我花了两周的时间,将写过的 ...

  9. 最详尽的 Swift 代码规范指南

    1. 代码格式 1.1 使用四个空格进行缩进. 1.2 每行最多160个字符,这样可以避免一行过长. (Xcode->Preferences->Text Editing->Page ...

最新文章

  1. C++ 笔记(36)— 接收输入字符串的几种方法
  2. golang signal 信号简介
  3. 四、编译安装php-5.5.34
  4. 不同表_不同电脑剪视频的速度对比表20200617更新;附素材和方法
  5. Python错误:TypeError: 'list' object is not callable
  6. 什么是Shell,Shell教程
  7. js点击图片查看大图,并可以拖动,且滚动滑轮放大缩小
  8. Mac 编译安装 Redis-3.2.3
  9. 2 django系列之django分页与templatetags
  10. java怎么判断按钮是否被点击_怎么判断肌肉训练后是否已经恢复
  11. 开发工具IDEA的使用
  12. DockOne微信分享( 一零二):基于容器的日志管理实践
  13. 43. Pandas查询数据的简便方法df.query
  14. turtle绘制禁烟标志
  15. 【H5】 svg的 defs用法 渐变
  16. 如何用计算机测量图片景深,用比较仔细的测量搞清楚“景深”(1.实测景深与公式比较)...
  17. CodeForces 949A Zebras
  18. NBA球员数据爬虫练习
  19. 2、用PHP求圆的面积和周长
  20. 试编程判断输入的正整数是否既是5又是7的整数倍,若是输出“yes”,否则输出“no”。

热门文章

  1. Ansible9:条件语句【转】
  2. Mysql安装及入门
  3. C语言嵌入式系统编程修炼之(六)性能优化
  4. Windows 8 应用商店应用开发 之 画刷
  5. 大型网站技术架构(八)网站的安全架构
  6. 腾讯斥资3.17亿美元增持B站 持股增至12%
  7. iOS符号表恢复逆向支付宝
  8. WiFi密码分享有妙招 不必口头相传
  9. 排插老化再酿祸端,安全新国标排插首选品胜
  10. phoneGap+jquery mobile项目经验