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
        }
    }
}
03-05 20:37