在 iOS 开发中,写一个 App 很容易,但是要写好一个 App,确是要下另一番功夫。首先,我们来看一个 App 的开发要求:

写一个 App,显示出 Spotify 上 Lady Gaga 相关的所有音乐专辑,相关信息可以通过以下网址查到:
https://api.spotify.com/v1/search?q=lady+gaga&type=album

需求分析

首先拿到开发要求,最重要的是明确开发细节。这里面有很多我们不清楚的地方需要与产品经理和设计师交流:显示是要用 TableView 还是 CollectionView?每个音乐专辑的哪些信息需要显示?如果专辑数量过多,我们优先显示哪些专辑?这个 App 除了显示信息以外,还需要哪些拓展功能?这个产品的大小是否有要求?需要多少天完成?

经过讨论之后,大家的一致意见是做个如下的 App:

于是我们就清楚了,是要做一个 tableView,每个 Cell 对应一个专辑信息,左边是图片,右边是专辑名。点击 Cell,可以看到相应的专辑大图。

构建架构

首先这个 App 比较简单,我们只要用最基本的 MVC 就可做好。

  • Model 部分:

    只需要一个 Model, 为 Album,对应每一个专辑的信息;

  • View 部分:

    主体的部分可以在 Storyboard 里面完成;

    最好单独新建一个 UITableViewCell 的子类,用来对应设置专辑的UI;

  • ViewController 部分:

    其中一个 ViewController 为 TableViewController,负责现实所有专辑的信息;

    另一个 ViewController 负责展示 detail info,比如专辑的大图;

  • Network 部分:

    负责从网络上 fetch 专辑信息;以及根据专辑的图片网址,fetch 图片数据;

细节实现

Network 部分:

fetchAlbums 和 downloadImage 都用Apple 自带的 URLSession 和 JSONserialization 就可以实现,或者也可以用优秀的第三方库 AlamoFire。因为这个 App 比较简单,AlamoFire 优势不明显,且引入第三方库会增加 App 的体积,故而推荐使用前者。基本上就是实现下面两个函数:

func fetchAlbums(with url: String, completion : @escaping (_ albums: [Album]?, _ error : NSError?) -> Void)

func downloadImage(_ url: String) -> UIImage?

对于第一个函数 fetchAlbums,因为网络请求是耗时耗力的工作,我们一般会将它们用后台线程而非主线程(UI线程)来处理,这样可以保持UI的流畅运行。用闭包则是为了异步多线程完成后可以回调,同时 error 是为了监视网络请求是否出错。

对于第二个函数 downloadImage,最简单的方法是通过 url 拿到对应的 data,然后通过相应的 data 拿到 image。返回为 optional 的原因是有可能 URL 有问题或者网络请求出错,此时返回 nil。

从API设计的角度来说,以上的downloadImage并不是最佳设计。最佳的设计是我们能知道哪里出错了,比如下面这样:

enum DownloadImageError: Error {

case InvalidURL

case InvalidData

}

func downloadImage(_ url: String) throws -> UIImage {

guard let aUrl = URL(string: url) else {

throw DownloadImageError.InvalidURL

}

do {

let data = try Data(contentsOf: aUrl)

if let image = UIImage(data: data) {

return image

} else {

throw DownloadImageError.InvalidData

}

} catch {

throw DownloadImageError.InvalidURL

}

}

  • ViewController 部分:

对于 AlbumsController,我们用到了代理模式(Delegate),即将 tableView 代理到了 AlbumsController 上。我们只要实现相应的 dataSource 和 delegate 方法即可。其中对于 dataSource 而言,有两个方法是必须实现的,它们是:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

同时,AlbumsController 里面,还有两个数组,一个用来装专辑([Album]),一个用来装图片([UIImage?]),这样我们只需下载数据一次,并将其存入相应数组,之后就无需再次进行相关的网络请求了。也就是说,这两个数组起到了缓存的作用。

具体的实现是:首先在 viewDidLoad() 中请求服务器取出相应的数据。之后根据专辑数量设定 TableView 的相应行数。在具体的一行当中,我们可以根据 indexPath 确定相应的专辑。根据相应专辑的图片 URL ,我们可以拿到相应的图片,之后缓存进图片数组。由于我们复用了 TableView 的 Cell,所以如果不缓存图片而每次去进行网络请求,会因为延时很严重而会造成图片闪烁的后果。

最后两个 ViewController 之间的跳转可以用 navigationController 来实现。

  • View 部分:

自定义 AlbumCell 可以保证 App 的扩展性很好。同时,为了处理有些专辑名字过长 Label 显示不了的问题,可以用 autoshrink 来处理。

优化拓展

上面的设计和实现比较理想化,现在我们要考虑一个边界情况,假如网络不稳定,怎么办?

一个简单的解决方法就是,当网络好的时候把数据下载下来,存入 cache 和 storage 中,之后即使网络中断、App 崩溃,我们都能从 storage 中拿到相应数据。

这里引入外观模式(Facade),创建一个新的 class 名为 LibraryAPI,提供两个接口:

func getAlbums(completion : @escaping (_ albums: [Album]?, _ error : NSError?) -> Void)

func getImage(_ url: String) throws -> UIImage

这里的方法跟之前 Network 的不同之处在于:getAlbums 方法会先尝试从 storage 中取出相应数据,如果没有,则去访问 Network,之后再把从 Network 中拿到的值存入 storage 中。这里面的实现有点复杂,牵涉到两大模块和多线程操作,但是我们并不必关心方法内部的实现,而仅仅关心接口,这就是外观模式的优点。同时,LibraryAPI 这个 class 最好用单例模式(singleton),因为它应该被当做是全局 API 被各个 ViewController 来访问,同时这样设计也节省资源。

优化后的 App 流程

另外一个优化点在于,假如我们一开始拿到很多数据 —— 例如10000 个专辑,那么我们该怎么操作?

正确的做法是分页。我们可以先只拿20个,显示在 TableView 上。当用户快滑到底端的时候,我们可以再取下面20个,然后我们总共有40个在内存中可以显示,以此类推。这样做的好处是,我们无需下载所有的数据,以最快、最流畅的方式布局 TableView,同时根据用户的需求增加相应的专辑数据。

最后一个优化点在于,假如用户上下滑动很快,我们如何能够用最快速度加载图片?

答案是用 operationQueue 来处理,当前 cell 是可见的时候,我们就 resume 下载图片的进程,否则就 suspend。这样保证了我们用有限的内存和 CPU 去最高效的下载用户需要、当前要见的图片。

值得一提的是,大家还可以借鉴 ASDK 的思路来进一步优化程序。

总结

本文从一个简单的 tableView App 说起,谈论了开发一个 App 的4个步骤:需求分析、构建架构、细节实现、优化拓展。简单介绍了多线程和几种设计模式,希望对大家有所帮助。

App 开发步骤的更多相关文章

  1. ios app开发步骤

    虽然开发一个app的任务看上去可能很艰巨,但是整个过程可以抽象成几个相对简单的步骤,下面这些步骤会在你开发第一个app时帮你步入正途. 定义Concept 每个好app都是从一个concept开始. ...

  2. 打造理想的Windows 10 APP开发环境的5个步骤

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:微软即将发布Windows 10手机版,实际上很多人现在已经开始在开发Windows ...

  3. 安卓app开发-03-项目的基本开发步骤

    android项目的基本开发步骤 这里分享一下开发 安卓 app 的流程,当然有些感觉不必要,其实不然,前期工作也是极为重要的额,就像开发的时候如果目标不对的话,到后期后很迷的,所以一定要提前做好规划 ...

  4. ios app 开发中ipa重新签名步骤介绍-备

    作为一个app应用程序开发者,在app应用程序在苹果商店上架前总需要将安装包安装到ios机器上进行测试,这个时候我们就需要打包in house版本的ipa了,打包in house实际上是一个将ipa应 ...

  5. ios app 开发中ipa重新签名步骤介绍

    作为一个app应用程序开发者,在app应用程序在苹果商店上架前总需要将安装包安装到ios机器上进行测试,这个时候我们就需要打包in house版本的ipa了,打包in house实际上是一个将ipa应 ...

  6. windows8 APP开发的远程调试

    Win8上面的App开发远程调试: 这里先简单介绍一下对于win8的app的远程调试,环境设置:开发机器装有vs2012的win8系统,目标机器为虚拟机,预装win8系统. 1. 在虚拟机上安装vis ...

  7. SharePoint Server 2013开发之旅(三):为SharePoint Server配置App开发、部署、管理环境

    上一篇我讲解了如何利用微软提供的Office 365开发人员网站,进行在线的SharePoint App开发,这当然是不错的一个选择,因为你可以快速地进行开发和调试.(仅仅针对App开发而言).但是, ...

  8. SharePoint Server 2013开发之旅(二):使用在线的开发人员网站进行SharePoint App开发

    上一篇我已经介绍了新版本的SharePoint Server提供了四种主要的开发场景,其中一个全新的App开发模型让我们眼前一亮.这一篇我将介绍如何在线进行SharePoint App开发. 谈到Sh ...

  9. App 开发:Hybrid 架构下的 HTML5 应用加速方案

    在移动 App 开发领域,主流的开发模式可分为 Native.Hybrid.WebApp 三种方式.然而 2013 年,纯 WebApp 开发模式的发展受到一定挫折,以 Facebook 为代表的独立 ...

随机推荐

  1. 微信小程序显示html格式内容(wxParse使用及循环解析数据渲染)

    小程序默认是不支持html格式的内容显示的,那我们需要显示html内容的时候,就可以通过wxParse来实现. 首先我们下载wxParse,github地址:https://github.com/ic ...

  2. vue按需引入echarts

    下载安装echarts包:npm install echarts -D 一.全局引入 main.js中配置 import echarts from 'echarts' //引入echarts Vue. ...

  3. JAVA中如何将一个json形式的字符串转为json对象或对象列表

    import java.util.*; import java.text.SimpleDateFormat; import org.json.JSONObject; import org.json.J ...

  4. 【Scala】Scala-循环与遍历

    Scala-循环与遍历 scala for 1000_百度搜索 Scala 2.8的for表达式:性能与运行顺序的改进 - 51CTO.COM scala List集合的用法 - CSDN博客

  5. capwap学习笔记——初识capwap(二)

    2.5.1 AC发现机制 WTP使用AC发现机制来得知哪些AC是可用的,决定最佳的AC来建立CAPWAP连接. WTP的发现过程是可选的.如果在WTP上静态配置了AC,那么WTP并不需要完成AC的发现 ...

  6. C++ 函数适配器

    1.考虑下面的需求,在一个int的vector中,找出一个比5的元素,容易想到的解决办法,定义一个方法对象,使用模板,如下:vector<int>::iterator iter = fin ...

  7. 海马模拟器连不上ADB的解决方法

    http://yunpan.cn/c3xMeYhvVsEIq  访问密码 fa8d先暂时用这个工具吧,官方提供的,不过目前不是最终版,后面会加入到模拟器中 adb connect 127.0.0.1: ...

  8. Android 八款开源 Android 游戏引擎

    原文地址 本文内容 Angle Rokon LGame AndEngine libgdx jPCT Alien3d Catcake 最近无意间看到一篇关于 Android 搜索引擎的文章,于是搜索了, ...

  9. NAS 网络附属存储

    本文内容 NAS NAS 功能 SAN 与 NAS 区别 NAS 底层协议 NAS 全球主要厂商 参考资料   NAS NAS(Network Attached Storage,网络附属存储)一种特殊 ...

  10. 【Android界面实现】使用PagerTabStrip实现有滑动标签的Viewpager

    在ViewPager这样的能够滑动的控件上,总是有非常多的文章能够做.上次的文章,我们实现了一个自己定义的ViewPager的指示器,这篇文章,我们主要是想利用Android自带的控件,实现一个指示器 ...