第四天
今天我们主要练习使用OAuth授权相关, 我们可以在新浪的官方找到我们所需要的所有接口和返回数据格式,具体网址:新浪微博接口文档
今天我们关注的是access_token
的作用,access_token
,我们可以从新浪官方获取自己应用的access_token
,获取方法如下:
- 进入新浪官方的开发者网站,http://open.weibo.com
- 点击文档
- 接口测试工具
- 左边就是我们这可以使用的
access_token
, 通过这个access_token
我们可以进行各种网络请求,但是在实际开发中,这个access_token
是由用户请求授权以后获得的 - 另外
Appkey
和AppSecret
在以后的开发中也会用到,同样在我们的应用这里可以找到
下边我们就开始今天的任务!
1.首先使用cocoapods导入AFN,SDImage等第三方库
- 具体的方法这里就不做介绍了,大家自行百度
###2.新建一个XQWBNetWorkManager.swift
封装网络请求类,管理网络请求
- 1.首先我们创建一个网络请求的单例
static let shared = XQWBNetWorkManager()
- 2.声明一个
access_token
变量
var accessToken:String? = "xxxxxxxxxxxxxxxx" // 这个就是我们从接口测试工具中得到的access_token
-
3.我们封装一个方法,包含POST和GET请求 具体代码如下:
/// 封装 GET/POST 请求 /// /// - Parameters: /// - method: 请求方式 /// - URLString: url /// - parameters: 参数 /// - completion: 回调 func request(method:XQWBHTTPMethod = .GET, URLString: String, parameters:[String:Any], completion:@escaping (_ json:Any?, _ isSuccess:Bool)->()) { // 成功回调 let success = { (tast:URLSessionDataTask, json:Any?)->() in completion(json, true) } // 失败回调 let failure = { (tast:URLSessionDataTask?, error:Error)->() in // 处理403 if (tast?.response as? HTTPURLResponse)?.statusCode == 403 { // print("token过期") // 发送通知,重新登录(谁接受谁处理) NotificationCenter.default.post(name: NSNotification.Name(rawValue: XQWBUserShouldLoginNotification), object: nil) } print("网络请求错误\(error)") completion(nil, false) } if method == .GET { get(URLString, parameters: parameters, progress: nil, success: success, failure: failure) }else { post(URLString, parameters: parameters, progress: nil, success: success, failure: failure) } }
-
观察新浪的官方接口我们不难发现,几乎所有的接口都需要传入一个参数
access_token
,我们就可以将网络请求的方法再次封装,加入access_token
// 负责拼接token func tokenRequest(method:XQWBHTTPMethod = .GET, URLString: String, parameters:[String:Any]?, completion:@escaping (_ json:Any?, _ isSuccess:Bool)->()) { guard let token = accessToken else { // token 不存在 // FIXME: 发送通知,重新登录(谁接受谁处理) completion(nil, false) return } // 判断参数字典是否存在 var parameters = parameters if parameters == nil { parameters = [String:AnyObject]() } parameters!["access_token"] = token // 调用request 发起真正的请求 request(method: method, URLString: URLString, parameters: parameters!, completion: completion) }
-
另外声明一个枚举类型,实现请求的类型
// 请求方式的枚举 enum XQWBHTTPMethod { case GET case POST }
3.新建一个XQWBNetWorkManager+Extension.swift
封装具体的网络请求
- 封装一个专门负责请求微博首页数据的方法
func statusList(completion:@escaping (_ list:[[String:Any]]?, _ isSuccess:Bool)->()) { // 加载微博数据 let urlStr = "https://api.weibo.com/2/statuses/home_timeline.json" tokenRequest(URLString: urlStr, parameters: nil) { (json, isSuccess) in let tempJson = json as AnyObject let result = tempJson["statuses"] as? [[String:AnyObject]] completion(result, isSuccess) } }
4.新建一个微博数据的model
// 目前我们只需要 id, text 2个属性做测试,等以后需要的时候在添加
class XQWBStatus: NSObject {
var id:Int64 = 0 // id
var text:String? // 内容
// 重写description计算性属性
override var description: String {
return yy_modelDescription()
}
}
5.建立 XQWBStatusListViewModel, 使用MVVM架构处理数据
// 模型数组懒加载
lazy var statusList = [XQWBStatus]()
// 上拉次数限制
fileprivate var pullupErrorTime = 0
// 用来加载网络数据,以及回调
func loadStatus(pullup:Bool, completion:@escaping (_ isSuccess:Bool, _ shouldRefresh:Bool)->()) {
if pullup && pullupErrorTime > maxPullTryTimes{
completion(true, false)
return
}
// since_id 下拉,去最大的(第一个)
let since_id = pullup ? 0 : (statusList.first?.id ?? 0)
let max_id = pullup ? (statusList.last?.id ?? 0) : 0
XQWBNetWorkManager.shared.statusList(since_id: since_id, max_id: max_id) { (list, isSuccess) in
// 1.字典转模型
guard let array = NSArray.yy_modelArray(with: XQWBStatus.self, json: list ?? []) as? [XQWBStatus] else {
completion(isSuccess, false)
return
}
if pullup {
// 上拉
self.statusList += array
}else {
// 下拉刷新
self.statusList = array + self.statusList;
}
if pullup && array.count == 0 {
self.pullupErrorTime += 1
completion(isSuccess, false)
}else {
completion(isSuccess, true)
}
}
}
6.修改主页之前写的模拟数据加载方法,这里我们就可以使用ViewModel
来实现
listViewModel.loadStatus(pullup: self.isPullup) { (isSuccess, shouldRefresh) in
self.isPullup = false
self.refreshControl?.endRefreshing()
self.tableView?.reloadData()
if shouldRefresh {
self.tableView?.reloadData()
}
}
7.再次使用access_token
请求接口,获取未读微博数量
-
1.封装专门请求未读数量的网路请求方法
/// 返回微博未读数量 func unReadCound(completion:@escaping (_ count:Int)->()) { guard let uid = uid else { return } let urlStr = "https://rm.api.weibo.com/2/remind/unread_count.json" let params = ["uid":uid] tokenRequest(URLString: urlStr, parameters: params) { (json, isSuccess) in print(json!) let tempJson = json as AnyObject let count = tempJson["status"] as? Int completion(count ?? 0) } }
-
2.开启一个定时器,每个一定时间请求未读数量,用来实时监测微博数量
// 设置定时器 func setupTimer() { timer = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(updataTimer), userInfo: nil, repeats:true) } // 定时器方法 @objc func updataTimer() { XQWBNetWorkManager.shared.unReadCound { (count) in print(count) } }
-
3.修改首页
tabbar
badge和应用图标badge// 设置首页tabbar self.tabBar.items?[0].badgeValue = count > 0 ? "\(count)" : nil // APP的badgeNum(授权) UIApplication.shared.applicationIconBadgeNumber = count
-
PS: 在设置应用的badge的时候,我们需要用户的权限
// 判断系统版本 if #available(iOS 10.0, *) { UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .carPlay, .badge]) { (success, error) in print("授权") } } else { // Fallback on earlier versions // 授权显示通知(状态栏/声音/number) let notifSettings = UIUserNotificationSettings(types: [.alert, .sound, .badge], categories: nil) application.registerUserNotificationSettings(notifSettings) }
8.细心的同学会发现新浪微博官方应用有一个功能,就是当你的首页有未读微博的时候,点击首页的item
,tableView
就会滚动到最顶部,下边就实现这个功能
- 1.实现这个功能,我们就需要用到
UITabBarControllerDelegate
的代理方法,我们整理下思路,写一个伪代码// 1.判断是否在主页 // 2.在主页,点击item就滚动到顶部,并且刷新数据 // 3.如果不是在主页,跳转到主页
- 2.具体代码如下:
/// 将要选择item /// /// - Parameters: /// - tabBarController: tabbar /// - viewController: 目标控制器 /// - Returns: 是否切换到控制器 func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { // 1. 获取控制器所在的索引 let index = (childViewControllers as NSArray).index(of: viewController) // 2. 判断当前是首页,点击是首页 if selectedIndex == 0 && index == selectedIndex { print("点击首页") // 让表格滚动到顶部 let nav = childViewControllers[0] as! UINavigationController let vc = nav.childViewControllers[0] as! XQWBHomeViewController vc.tableView?.setContentOffset(CGPoint(x: 0, y: -64), animated: true) // 刷新表格 DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { vc.loadData() }) } return !viewController.isMember(of: UIViewController.self) }