公司的一个项目中的所有 API 接口无论是否登录都要带一个 token,如果没有 token 的话请求就会失败,因此在没有 token 或者是 token 失效的情况下要调用获取 token 的 API 将获取到的 token 存储到本地。
多次调用接口
写完代码后一测试,后台发现用户数量呈不正常的状态在增长,一查,发现每请求一次 token 都会生成一个用户。那么只能限制获取 token 的接口的调用次数了,一旦获取到 token,后续请求 token 的接口都取消。很自然地就想到用 OperationQueue 来做这个事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   | private let operationQueue: OperationQueue = { 	let queue = OperationQueue() 	queue.maxConcurrentOperationCount = 1 	return queue }() 	 private let group = DispatchGroup() 	 func getAccesstoken() { 	operationQueue.addOperation { [weak self] in 		guard let this = self else { return } 		this.getToken() 	} }
  func getToken() { 	func handleResult(_ item: String) { 		 		operationQueue.cancelAllOperations() 		group.leave() 	}
  	func handleError(_ error: Error) -> Void { 		group.leave() 	}
  	group.enter()
  	webService.getToken() 		.done(handleResult) 		.catch(handleError) 		 	group.wait() }
  | 
 
这样就解决了多次调用获取 token 请求的问题。
Moya 重试请求
在 token 失效之后还得解决一个问题,那就是由于 token 失效导致的请求失败的问题,得让失败的请求进行重试,由于用的是 Moya 框架,搜索了许多重试的解决方案,无奈的是试过之后都不起作用,最后只能自己子类化一个 provider 来实现重试的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | class CustomProvider<T: TargetType>: MoyaProvider<T> {     @discardableResult     override func request(_ target: T, callbackQueue: DispatchQueue? = .none, progress: ProgressBlock? = .none, completion: @escaping Completion) -> Cancellable {         super.request(target, callbackQueue: callbackQueue, progress: progress) { result in             switch result {             case .success(let response):                                  ...                 if statusCode == xxx {                                          let queue = DispatchQueue(label: "com.network.retry", attributes: .concurrent)                     queue.asyncAfter(deadline: .now() + 3) {                         self.request(target, callbackQueue: callbackQueue, progress: progress, completion: completion)                     }
                  } else {                     completion(.success(response))                 }
              case .failure(let error):                 completion(.failure(error))             }         }     } }
  |