1.设计模式
本项目采用MVC设计模式
2.组织架构
项目组织架构如下图
3. 授权登录
登录授权页面点击按钮后通过URLSchema的方式唤醒智汀家庭云App进行授权
loginBtn.clickCallBack = { [weak self] _ in
guard let self = self else { return }
if let url = URL(string: "zhiting://operatioaction=diskAuth"), UIApplication.shared.canOpenU(url) {
print("跳转成功")
UIApplication.shared.open(url, options: [:]completionHandler: nil)
} else {
self.showToast("请先安装 \"智汀家庭云\" APP")
print("跳转失败")
}
}
智汀家庭云授权部分代码片段:
/// 授权当前绑定SA的家庭
private func requestAreaScopeToken() {
let scopes = authItems.filter({ $0.isSelected }).map(\.name)
if !authManager.currentArea.is_bind_sa {
showToast(string: "当前家庭未绑定SA".localizedString)
confirmButton.selectedChangeView(isLoading: false)
return
}
ApiServiceManager.shared.scopeToken(area: authManager.currentArea, scopes: scopes) { [weak self] response in
guard let self = self else { return }
let area = self.transferToAuthedArea(from: self.authManager.currentArea, scopeTokenModel: response.scope_token)
if self.authManager.isLogin {
self.returnAuthResult(cloud_user_id: self.authManager.currentArea.cloud_user_id, cloud_url: cloudUrl, areas: [area])
} else {
area.id = 0
self.returnAuthResult(cloud_user_id: nil, cloud_url: nil, areas: [area])
}
} failureCallback: { [weak self] code, err in
self?.showToast(string: err)
self?.confirmButton.selectedChangeView(isLoading: false)
}
}
private func returnAuthResult(cloud_user_id: Int?, cloud_url: String?, areas: [AuthedAreaModel]) {
let result = ResultModel()
result.cloud_url = cloud_url
result.cloud_user_id = cloud_user_id
result.nickname = authManager.currentUser.nickname
result.areas = areas
if let cloudUrl = cloud_url, let cookie = HTTPCookieStorage.shared.cookies?.first(where: { cloudUrl.contains($0.domain) }) {
result.sessionCookie = cookie.value
}
guard let json = result.toJSONString(),
let data = try? NSKeyedArchiver.archivedData(withRootObject: json, requiringSecureCoding: true)
else {
confirmButton.selectedChangeView(isLoading: false)
return
}
try? data.write(to: shareTokenURL, options: .atomic)
confirmButton.selectedChangeView(isLoading: false)
if let url = URL(string: "zhitingcloud://operation?action=auth") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
dismiss(animated: true, completion: nil)
}
智汀App授权成功后将结果归档并写入到AppGroup共享空间的shareToken.plist文件中,然后通过URLSchema的方式唤醒智汀网盘App,智汀网盘App解析URLSchema,判断是授权成功响应时获取AppGroup共享空间的shareToken.plist文件内容并进行解档得到授权的家庭、用户和云端账号cookies等信息,储存信息并跳转页面.(详情见OpenUrlManager类)
OpenUrlManager类:
import Foundation
// MARK: - OpenUrlManager
class OpenUrlManager {
enum Action: String {
/// 网盘授权
case auth
}
static var shared = OpenUrlManager()
private init() {}
func open(url: URL) {
let urlString = url.absoluteString
print("--------------------- open from other app ----------------------------------")
print(Date())
print("---------------------------------------------------------------------------")
print("open url from \(urlString)")
print("---------------------------------------------------------------------------\n\n")
guard
let components = urlString.components(separatedBy: "zhitingcloud://operation?").last,
let action = components.components(separatedBy: "&").first?.components(separatedBy: "=").last
else {
return
}
switch Action(rawValue: action) {
case .auth:
// zhitingcloud://operation?action=auth
let shareTokenURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.zhiting.tech")!.appendingPathComponent("shareToken.plist")
/// 读取授权成功响应并解密
guard
let data = try? Data(contentsOf: shareTokenURL),
let json = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? String
else { return }
if let authResult = AuthModel.deserialize(from: json) {
let user = User()
user.nickname = authResult.nickname
/// 存储家庭信息
AreaManager.shared.cacheAreas(areas: authResult.areas)
if let area = AreaManager.shared.getAreaList().first {
AreaManager.shared.currentArea = area
user.user_id = area.sa_user_id
}
if let cloudUserId = authResult.cloud_user_id,
let cloudUrl = authResult.cloud_url,
let cookieValue = authResult.sessionCookie { /// 云端的授权
user.user_id = cloudUserId
UserManager.shared.isCloudUser = true
UserManager.shared.cloudUrl = cloudUrl
/// 写入云端账号cookie
if let cookie = HTTPCookie(properties: [
HTTPCookiePropertyKey.domain : cloudUrl,
HTTPCookiePropertyKey.value : cookieValue,
HTTPCookiePropertyKey.path : "/",
HTTPCookiePropertyKey.name : "_session_"
]) {
HTTPCookieStorage.shared.setCookie(cookie)
}
}
UserManager.shared.currentUser = user
UserManager.shared.cacheUser(user: user)
}
SceneDelegate.shared.window?.rootViewController = TabbarController()
default:
break
}
}
}