从头开始swift2.1 仿搜材通项目(七) 封装后台交互框架

火地岛的咖啡真的不错。希望在打烊前我可以写完这篇文章。
这一节我们来做本项目中的第一次网络请求,并将其封装成为自己的网络请求框架,这里使用到的有Alamofire、JSONHelper,感谢这两款开发利器。
首先在pod中添加,之后不要忘记update。

pod 'Alamofire' #网络请求 https://github.com/Alamofire/Alamofire

之所以没有直接pod JSONHelper,是因为我修改了其中一点点代码,后面我会上传到git,同学们直接使用即可。
现在在Library下新建Http目录,放入修改后的JSONHelper文件,并新建一个HMRequest,后面用来封装我们网络请求的方法。




首先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, 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,这里我们就算封装好了,现在我们回去找个地方调用一下。恩,就这个幻灯片吧,这个可恶的广告,我早就想换了。



回到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.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,怎么回事呢?



我们看看服务器返回的结果:

{
    "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.get(url, params: params) { (news, error) -> () in
            
            var thumbUrls:[AnyObject?] = []
            for data in (news?.data)! {
                thumbUrls.append(data.thumb!)
            }
            
            anotherBanner.images = thumbUrls
            anotherBanner.startRolling()
        }

再次运行看下结果:



图片已经正常的显示出来了。这只是一个简单的网络请求步骤,也是一个通用的步骤,前面我们做的工作确实比较多,但这些工作都需要做第一次,在UI层调用的时候,仅仅需要这一段代码即可,并且回调中的data已是json解析好的对象直接使用,这样是不是非常方便呢?

        HMRequest.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

你可能感兴趣的:(从头开始swift2.1 仿搜材通项目(七) 封装后台交互框架)