本文主要分享下楼主在学习Swift编程过程中,对GitHub上的一个开源app BaiduFM的研究心得。
项目地址:https://github.com/belm/BaiduFM-Swift
一、项目简介
项目通过使用百度音乐的API实现了播放、下载与收藏音乐的FM基本功能。同时实现了歌词滚动,显示实时进度条,支持后台播放,锁屏显示歌曲信息及控制播放等附加功能并添加了对Apple Watch的支持。此APP可谓是功能十分强劲,下面楼主就来好好分析下这款APP的代码及功能实现原理。
二、APP IOS部分分析
与楼主之前分析的两款APP相似,这款BaiduFM同样也是基于调用API实现从网络端获取歌曲信息及Url并予以播放这一基本功能。在此我便不再具体分析这一功能的实现方法(不清楚的朋友可以参见楼主之前写的两篇博文:(IOS)Swift Music 程序分析 与 (IOS)Swift2.0 Radio 程序分析)。
这次,本文主要想通过对此APP程序及数据结构上的分析,来探究如何通过程序数据结构的设计,从而以一个高效简洁的代码来达到预期功能,并增强代码对其他功能的扩展性。同时顺带介绍下本APP所使用到的一些第三方库。
1、DataBase
本作与之前分析的两款FM类APP最显著的一个不同是,作者为这款APP设计了一个存储于本地的DateBase。通过对第三方库FMDB的调用,作者在本地建立了一个SQLite数据库用于存放歌曲信息列表。那么,首先楼主就先来介绍下FMDB这个第三方库。
FMDB是以Object-C封装了C语言API的用于在ios设备上生成并管理SQLite的这么一个第三方库。通过调用FMDB库,使用者可以可以很轻松的使用SQL命令来对数据库进行操作。
Github:https://github.com/ccgus/fmdb。
具体的使用说明可以参见博文:http://www.cnblogs.com/wendingding/p/3871848.html。
在本项目中,作者通过建立class BaseDb在本地建立了一个FMDataBase,并通过class SongList:BaseDb建立了本地音乐列表数据库。并通过调用FMDCB的方法创建了诸如func getAll()->[Song]?,func get(sid:String)->Song?等方法(具体参见上图代码)。代码都较为易懂,在此楼主就不做过多解释了,在这之中唯一需要注意的是,在FMDB使用过程中,任何除select以外的操作都视为更新。所以使用的是executeUpdate(,withArgumentsInArray)方法。通过对SongList类的定义,作者可以很容易的通过类的方法对本地数据库进行添加删除修改数据等操作。
2、Model
除了定义了数据库,定义一些APP中所要使用到的Data Model也是在设计程序中一个不可缺少的环节。在这个环节中,作者一共定义了5种Data类型。
其中三个struct分别是:SongInfo,SongLink与Channel。
SongInfo:包含了歌曲id,名字,表演者等信息。
SongLink:包含了歌曲链接,歌词链接,格式等信息
Channel:包含了频道id,名字,序列号等信息
两个class:class Song:NSObject 与 class DataCenter
Song:是数据库用于记录歌曲信息的数据类型
DataCenter:是用于提供存储当前电台信息,当前播放列表信息,播放歌曲信息,数据库操作等一系列操作的类似于model manager角色的对象。同时提供了一个shareDateCenter 的单例用于将自身共享。
3、Common
作者将一些时常会调用到的函数及常量统一装在了这个文件夹下。
Const.swift中就包含了两个通知名及APP所需用到的URL,像这样将常量统一管理,在日后对其进行调试修改及更新时会变得便捷许多。
Utils封装了一个获取文档路径的方法,这样做不但提高了代码的编写效率,同时也增加了代码的可读性。
Common类中则是包含了一些对歌曲url,歌曲图片 ,显示时间,本地歌曲删除及歌词处理的方法。在这之中,作者通过定义及使用了一个通过比对字符串正则表达式的方法来进行lrc歌词文件中时间部分与歌词部分的分割,并返回一个歌词时间一一对应的数组。
举个例子:上图是一个lrc格式的歌词文件。通过将此文件传入函数class func praseSongLrc(lrc:String)->[(lrc:String,time:Int)],最终返回的数组应如下:
ret[0] = (lrc:"Glad You Came", time:0)
ret[1] = (lrc:"The Wanted", time:0)
ret[2] = (lrc:"", time:0)
ret[3] = (lrc:"The sun goes down", time:1)
ret[4] = (lrc:"The stars come out", time:3)
...
最后,在Common文件夹中,作者在HttpRequest类中调用了Alamofire和SwiftyJson两个第三方库定义了一系列通过网络请求获取歌曲信息及下载等一系列方法。与之前楼主分析的两款APP不同,这款APP并没有使用swift自带的NSURLSession或NSURLConnect来进行网络请求,而是调用了第三方库Alamofire同提供的API来达成网络通信的。下面我就先来简单介绍下Alamofire这个第三方库。
Alamofire是用于Http网络开发的第三方库。它的作者就是大名鼎鼎的第三方库AFNetworking的作者。可以说Alamofire就是Swift版的AFNetworking。
Github:https://github.com/Alamofire/Alamofire
Alamofire的最新版本是3.0,支持iOS 8+, Mac OS X 10.9+, watchOS 2.0, Xcode 7 and Swift 2.0,可以通过CocoaPods,Carthage等多种工具倒入到项目文件。
这是一个简单的使用Alamofire.request方法获取响应并使用.responseJSON对响应结果进行处理的例子。更多的调用API的例子,各位看官可以去Github上观看Alamofire的详细教程指南。
通过上面的这一简单的例子,不难发现相比于NSURLSession,Alamofire对URL请求的及返回结果的处理是如此的方便。即无需繁复定义繁琐的session及task,同时再配合SwiftyJSON库使用时,可以极大的减少对Json格式数据的处理时所用到的拆包,转型等操作。
接着楼主就来介绍下这款同样非常好用的第三方库SwiftyJSON。
SwiftyJSON是一款用于处理JSON格式数据的第三方库。由于Swift语言设计的原因,在使用Swift语言获取JSON格式数据的内容时,需要手动的将AnyObject进行转型拆包等一系列繁复的操作。同时在这过程中,稍有不慎,很容易引起程序的崩溃。基于此,SwiftyJSON提供了一套简介的处理JSON格式数据的方法,并且不用担心因为在对JSON数据上处理的疏忽导致的数组越界,程序崩溃的情况发生。
Github:https://github.com/SwiftyJSON/SwiftyJSON
这是一个简单的调用JSON(data:)方法的例子。可以看到,仅需一步,就可以获取到想要的数据。这不但提升了代码编写的效率,也省去了在阅读代码时看到那一串串令人头疼不已的拆包步骤。
通过与Alamofire JSON response handler的配合使用,更是极大的简化了在处理网络返回JSON格式数据时的复杂过程。
4、View
Views文件夹中包含了7个文件,其中5个是TableViewController包含了大量对表格的设定处理和填充,这与之前在两个APP中的处理大同小异,在此,就不详细的展开讲述了。楼主将会拿出几处不同之处进行一下简单的分析。
这5个TableViewContorller的数据源都是通过DataCenter.shareDataCenter获取DataCenter实例再调用相应方法得到的。通过这个方法,避免了在类中大量定义变量及将数据反复传递。
在willDisplayCell方法中加入cell.layer.transform属性设定及调用UIView.animateWithDuration()方法从而达到cell显示时出现动画效果。
通过调用第三方库MJRefresh中的API是列表具有上下拉刷新的功能。MJRefresh是一款用于实现上下拉自动刷的第三方库。通过调用API能很轻易的实现想要的刷新功能。
Github:https://github.com/CoderMJLee/MJRefresh
Github上有着非常详细的调用例子。
Views中除了TableViewController还定义了一个RoundImageView类生成用于显示歌曲图片的圆形UIImage。通过设置图片圆角,将圆角半径设为正方形图片边长的1/2,从而使原本是正方形的UIImage显示成圆形。并定义了一个rotation()方法,用于使图片旋转起来。
最后,ViewControllere.swift中主要实现了歌曲播放,切歌,下载歌曲,收藏歌曲,更新数据库,锁屏显示及事件响应,显示歌词等一系列的功能。虽然代码比较长且有些杂乱,但是功能实现原理都较为简单,在此不做详细介绍了。主要想讲一下这里使用的三个第三方库:Async,LTMorphingLabel与Kingfisher。
Async是一个处理对线程问题的第三方库。通过调用Async可以较为方便的处理线程问题
GitHub:https://github.com/duemunk/Async
通过这个例子可以发现,使用Async库后,同样的执行一个后台操作及主线程操作的代码变得简洁许多。
LTMorphingLabel是一个实现Label显示动画效果的第三方库。使用时,直接将UILabel()实例为LTMorphingLabel(),之后就可以调用LTMorphingLabel类中所提供的各种动画效果了。
Github:https://github.com/lexrus/LTMorphingLabel
Kingfisher是一个使用异步下载装载及缓存图片的第三方库。所有的下载都是异步进行的,不会卡UI,下载完的同时不但提供了内存缓存,同时也提供了本地磁盘的备份。
Github:https://github.com/onevcat/Kingfisher
三、聊聊第三方库
在分析程序部分时,聊到了这款APP所用到的所有第三方库,并给予了简单的介绍。在此再做一下汇总
FMDB:https://github.com/ccgus/fmdb
Alamofire:https://github.com/Alamofire/Alamofire
SwiftyJSON:https://github.com/SwiftyJSON/SwiftyJSON
MJRefresh:https://github.com/CoderMJLee/MJRefresh
Async:https://github.com/duemunk/Async
LTMorphingLabel:https://github.com/lexrus/LTMorphingLabel
Kingfisher:https://github.com/onevcat/Kingfisher
现在想要聊一下如何在项目中引入这些库。
现如今,一般在Swift项目中都是通过CocoaPods和Carthage这两个工具来引用和管理第三方库。接着就来简单的说一下这两个工具及其使用方法。
1、CocoaPods
CocoaPods可以说是目前最为流行的ISO/MAC第三方库管理工具。最新版本是0.39。用户可以通过编写Podfile并执行,便可非常便捷的从Github上获取自己想要使用的第三方库并将其整合进自己的项目中。
官方网站:https://cocoapods.org
安装CocoaPods:
在终端中输入上图代码便可自动完成CocoaPods安装。
完成安装后就是该介绍下如何使用这款非常好用的工具了
首先是在项目目录下创建一个Podfile文件。将项目所需要的第三方库如上图格式写入Podfile。
接着使用终端进入项目目录运行命令pod install,稍等片刻后,屏幕会提示安装成功。
之后打开项目就用新生成的.xcworkspace文件就可以了。
CocoaPods的优势:
使用极其方便,除了需要编写Podfile外,其余操作都是自动完成的。
支持第三方库数量众多。
2、Carthage
相比于CocoaPods,Carthage的最大优势就是去中心化。在用Carthage调用第三方库时不会额外创建一个.xcworkspace,这也就意味着用户可以自由的控制如何将第三方库装入自己的项目中。
Github:https://github.com/Carthage/Carthage
安装Carthage:
在https://github.com/Carthage/Carthage/releases中下载Carthage.pkg并安装。
如何使用Carthage可以参考http://devtian.me/2015/08/11/translate-carthage-readme/这篇博文是Carthage使用说明的译文。
四、项目IOS部分可改进部分
项目在数据模型设计高效,布局合理,但是还是存在了如ViewController.swift这样将许多功能及方法整合于一身的巨大类。这不仅增加了代码的阅读难度,同时也加大了程序在更新及维护过程中困难程度。
五、总结
本项目制作较为精良,作者通过使用大量的第三方库如FMDB,Alamofire,SwiftyJSON不但降低了项目实现功能的难度,而且是代码的可读性有了一定的提升。通过对这些第三方库的介绍,楼主相信,在今后的实战过程中,合理的使用第三方库将会是提升代码及项目质量的很好的方法及手段。
本项目中还包含了Apple Watch部分,楼主将在日后对这块部分进行一个详细的分析。