从头开始swift2.1 仿搜材通项目(七) 封装后台交互框架
火地岛的咖啡真的不错。希望在打烊前我可以写完这篇文章。
这一节我们来做本项目中的第一次网络请求,并将其封装成为自己的网络请求框架,这里使用到的有Alamofire、JSONHelper,感谢这两款开发利器。
首先在pod中添加,之后不要忘记update。
pod 'Alamofire' #网络请求 https://github.com/Alamofire/Alamofire
之所以没有直接pod JSONHelper,是因为我修改了其中一点点代码,后面我会上传到git,同学们直接使用即可。
现在在Library下新建Http目录,放入修改后的JSONHelper文件,并新建一个HMRequest,后面用来封装我们网络请求的方法。
QQ20151222-1@2x.png
QQ20151222-2@2x.png
首先import,再根据大家自己公司的后台结构创建一个协议,假如接口返回的结构像这样
{
"error": 0,
"msg": "",
"data": [
{
"id": "11012",
"title": "这些年蹲马桶的姿势真的正确吗?",
"link": "http://mp.weixin.qq.com/s?__biz=MjM5OTc4MDQ2NA==&mid=222471269&idx=1&sn=2835c1c321ca244b72eb40e3c3056864&scene=5#rd",
"descr": "【健康】实际上人类发明的马桶,可能并没那么好……",
"refinfo": "微信",
"thumb": "http://mmbiz.qpic.cn/mmbiz/hFdY52oozXj2E7IECSq83s864qdPka6WaOaYGK0PDUcPAWq7dpibAj4qQrzFQiaRfGW4G9QhFtqowzNM2qdK6t9Q/0?wx_fmt=jpeg&wxfrom=5",
"time": "2015-04-29"
}
]
}
data里面的数据我们先不理会,我们先定义一个int的error,一个String的msg,再定义一个从json转换对象的方法。
import Alamofire
protocol HMConvertible{
var error:Int { get set }
var msg:String? { get set }
static func convertFromData(data:String!) -> (Self,NSError?)
}
接下来我们写一个带参数post请求的例子,大家可以参考Alamofire的用法实现get\put等等。
class HMRequest< T:HMConvertible> {
static func post(url :String , params: Dictionary<String,AnyObject>, completionHandler:(T?,NSError?) -> ()){
//组装log,只是在控制台输出请求的地址,可以省略
if params.count > 0{
var str:String = "?"
for param in params {
str += "\(param.0)=\(param.1)&"
}
str = (str as NSString).substringToIndex(str.characters.count-1)
debugPrint("--------------------post--------------------")
debugPrint("\(url)\(str)")
}
Alamofire.request(.POST, url, parameters: params).responseString { response in
//打印出接口响应的数据,可以省略
debugPrint("result:\(response.result.value)")
if response.result.isFailure {
var domain:String?
//对异常信息作出判断,这里简单的判断一下网络问题
switch response.result.error?.domain{
case NSURLErrorDomain?:
domain = "网络不佳"
default :
domain = "未知错误"
}
let error = NSError(domain: domain!, code: (response.result.error?.code)!, userInfo: nil)
completionHandler(nil, error)
return
}
let(object, converError) = T.convertFromData(response.result.value)
completionHandler(object, converError)
}
}
}
OK,这里我们就算封装好了,现在我们回去找个地方调用一下。恩,就这个幻灯片吧,这个可恶的广告,我早就想换了。
QQ20151222-3@2x.png
回到HomeController中的initBanner方法,还记得吗,之前我们是直接写死的图片:
anotherBanner.images = [
"http://pic9.nipic.com/20100817/4845745_124224113296_2.jpg",
"http://pic26.nipic.com/20121223/11613623_164837493315_2.jpg",
"http://pic2.ooopic.com/10/79/67/75b1OOOPIC15.jpg"
]
现在我们换成一个查询新闻的API,查询新闻,咦,它居然是get请求的,好吧,我们先在HMRequest中加入支持get请求:
static func get(url :String, completionHandler:(T?,NSError!) -> ()){
Alamofire.request(.GET, url).responseString { response in
let(object, converError) = T.convertFromData(response.result.value)
completionHandler(object, converError)
}
}
static func get(url :String ,params: [String : AnyObject]?, completionHandler:(T?,NSError!) -> ()){
Alamofire.request(.GET, url, parameters: params).responseString { response in
let(object, converError) = T.convertFromData(response.result.value)
completionHandler(object, converError)
}
}
现在我们需要创建一个类作为json序列化的对象,android那边叫实体,iOS叫模型,其实都是一回事。不过我们现在使用struct值传递,不再使用class。
struct NewsDomain: HMSerializable ,HMConvertible{
var error: Int = 0
var msg: Int = 0
var data: NewsData?
static func convertFromData(data: String!) -> (NewsDomain, NSError?) {
var hm : NewsDomain?
hm <-- data
return (hm!,nil)
}
init(data: [String: AnyObject]) {
error <-- data["error"]
msg <-- data["msg"]
self.data <-- data["data"]
}
//对应data字段,而且可以只取我们需要的字段,不需要全部解析
struct NewsData: HMSerializable {
var link: String? //点击跳转的url
var thumb: String? //缩略图地址
init(data: [String: AnyObject]) {
link <-- data["link"]
thumb <-- data["thumb"]
}
}
}
OK,现在万事具备,可以直接使用了,我们回到initBanner中调用:
//请求地址
let url = "http://apis.baidu.com/cd_boco/chinanews/testnewsapi"
//如果需要传参
let params = ["query":"{'device':'android','catid':1,'pagesize':3,'sid':'11142'}"]
HMRequest<NewsDomain>.get(url, params: params) { (news, error) -> () in
print(news)
}
OK,现在运行一下看看print出什么:
Optional(sctong.NewsDomain(error: 0, msg: 0, data: nil))
news为nil,经过调试后发现,{"errNum":300202,"errMsg":"Missing apikey"},原来是这个api需要设置一下header,如果你的服务器不需要设置,那请跳过这一步:
static func get(url :String ,params: [String : AnyObject]?, completionHandler:(T?,NSError!) -> ()){
//设置headers
let headers = ["apikey":"be910c69ec688ba099d0091e19c21033"]
Alamofire.request(.GET, url, parameters: params, headers:headers).responseString { response in
let(object, converError) = T.convertFromData(response.result.value)
completionHandler(object, converError)
}
}
再次运行,发现有结果返回了,但data依然为nil,怎么回事呢?
QQ20151222-4@2x.png
我们看看服务器返回的结果:
{
"error": 0,
"msg": 1,
"data": [
{
"id": "11012",
"title": "这些年蹲马桶的姿势真的正确吗?",
"link": "http://mp.weixin.qq.com/s?__biz=MjM5OTc4MDQ2NA==&mid=222471269&idx=1&sn=2835c1c321ca244b72eb40e3c3056864&scene=5#rd",
"descr": "【健康】实际上人类发明的马桶,可能并没那么好……",
"refinfo": "微信",
"thumb": "http://mmbiz.qpic.cn/mmbiz/hFdY52oozXj2E7IECSq83s864qdPka6WaOaYGK0PDUcPAWq7dpibAj4qQrzFQiaRfGW4G9QhFtqowzNM2qdK6t9Q/0?wx_fmt=jpeg&wxfrom=5",
"time": "2015-04-29"
},
{
"id": "10923",
"title": "一样东西配枸杞功效翻倍 怎样吃?",
"link": "http://fashion.ifeng.com/a/20150421/40101843_0.shtml",
"descr": "【药饮】原料:黑豆100克,枸杞5克,红枣10个。",
"refinfo": "凤凰网",
"thumb": "http://y0.ifengimg.com/cmpp/2015/04/21/08/1e430f73-3630-4e53-9ab6-6ef6c4c865e7_size25_w550_h366.jpg",
"time": "2015-04-21"
},
{
"id": "10922",
"title": "吃烤串要烤熟 当心患\"懒汉病\"",
"link": "http://m.news.cn/html/712/114376.html",
"descr": "【疾病】大快朵颐的时候,一定不要忘了把肉烤熟,否则您就有可能因此患上“懒汉病”。",
"refinfo": "新华炫闻",
"thumb": "http://static.xhw.feedss.com/uploadfile/xwimg/89/6d/127713171_1429575260241.jpg",
"time": "2015-04-21"
}
]
}
找到原因了,data应该是一个集合,于是修改
struct NewsDomain: HMSerializable ,HMConvertible{
var error: Int = 0
var msg: Int = 0
var data: [NewsData]?
///...
}
再次运行,print结果是:
Optional(sctong.NewsDomain(error: 0, msg: 1, data: Optional([sctong.NewsDomain.NewsData(link: Optional("http://mp.weixin.qq.com/s?__biz=MjM5OTc4MDQ2NA==&mid=222471269&idx=1&sn=2835c1c321ca244b72eb40e3c3056864&scene=5#rd"), thumb: Optional("http://mmbiz.qpic.cn/mmbiz/hFdY52oozXj2E7IECSq83s864qdPka6WaOaYGK0PDUcPAWq7dpibAj4qQrzFQiaRfGW4G9QhFtqowzNM2qdK6t9Q/0?wx_fmt=jpeg&wxfrom=5")), sctong.NewsDomain.NewsData(link: Optional("http://fashion.ifeng.com/a/20150421/40101843_0.shtml"), thumb: Optional("http://y0.ifengimg.com/cmpp/2015/04/21/08/1e430f73-3630-4e53-9ab6-6ef6c4c865e7_size25_w550_h366.jpg")), sctong.NewsDomain.NewsData(link: Optional("http://m.news.cn/html/712/114376.html"), thumb: Optional("http://static.xhw.feedss.com/uploadfile/xwimg/89/6d/127713171_1429575260241.jpg"))])))
已经有值了,现在修改之前写死的图片,
HMRequest<NewsDomain>.get(url, params: params) { (news, error) -> () in
var thumbUrls:[AnyObject?] = []
for data in (news?.data)! {
thumbUrls.append(data.thumb!)
}
anotherBanner.images = thumbUrls
anotherBanner.startRolling()
}
再次运行看下结果:
QQ20151222-5@2x.png
图片已经正常的显示出来了。这只是一个简单的网络请求步骤,也是一个通用的步骤,前面我们做的工作确实比较多,但这些工作都需要做第一次,在UI层调用的时候,仅仅需要这一段代码即可,并且回调中的data已是json解析好的对象直接使用,这样是不是非常方便呢?
HMRequest<Domain>.get(url, params: params) { (data, error) -> () in
//use data to do something
}
OK,咖啡店打烊了,你帮我结账如何?
Git地址:https://github.com/bxcx/sctong
本节分支:https://github.com/bxcx/sctong/tree/6th_Http