一、设计模式本项目采用MVC设计模式二、组织结构项目组织结构如图3所示授权登录登录授权页面点击按钮后,通过URLSchema唤醒智听家庭云App授权loginBtn.clickCallBack={[弱自]_inguardletself=selfelse{return}ifleturl=URL(string:"zhiting://operatioaction=diskAuth"),UIApplication.shared.canOpenU(url){print("跳转成功")UIApplication.shared.open(url,options:[:]completionHandler:nil)}else{self.showToast("请安装\"智庭云\"APP")print("跳转失败")}}智庭云授权部分代码片段:///授权当前绑定到SA的家庭privatefuncrequestAreaScopeToken(){letscopes=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){[weakself]responseinguardletself=selfelse{return}letarea=self.transferToAuthedArea(from:self.authManager.currentArea,scopeTokenModel:response.scope_token)如果self.authManager.isLogin{self.returnAuthResult(cloud_user_id:self.authManager.currentArea.cloud_user_id,cloud_url:cloudUrl,areas:[area])}else{area.id=0self.returnAuthResult(cloud_user_id:nil,cloud_url:nil,areas:[area])}}failureCallback:{[weakself]code,errinself?.showToast(string:err)self?.confirmButton.selectedChangeView(isLoading:false)}}privatefuncreturnAuthResult(cloud_user_id:Int?,cloud_url:String?,areas:[AuthedAreaModel]){letresult=ResultModel()结果.cloud_url=cloud_urlresult.cloud_user_id=cloud_user_idresult.nickname=authManager.currentUser.nicknameresult.areas=areasifletcloudUrl=cloud_url,letcookie=HTTPCookieStorage.shared.cookies?.first(where:{cloudUrl.contains($0.domain)}){result.sessionCookie=cookie.value}guardletjson=result.toJSONString(),让数据=试试?NSKeyedArchiver.archivedData(withRootObject:json,requiringSecureCoding:true)else{confirmButton.selectedChangeView(isLoading:false)return}试试?data.write(to:shareTokenURL,options:.atomic)confirmButton.selected(isLoading:false)ifleturl=URL(string:"zhitingcloud://operation?action=auth"){UIApplication.shared.open(url,options:[:],completionHandler:nil)}dismiss(animated:true,completion:nil)}智听App授权成功后,将结果归档写入AppGroup共享空间的shareToken.plist文件,然后wakeup智庭网盘App通过URLSchema,智庭网盘App解析URLSchema,判断是授权成功时获取AppGroup共享空间的shareToken.plist文件内容并响应解包授权家庭、用户和云账号cookies等信息,存储信息并跳转到页面。(详见OpenUrlManager类)OpenUrlManagerclass:importFoundation//MARK:-OpenUrlManagerclassOpenUrlManager{enumAction:String{///网盘授权案例auth}staticvarshared=OpenUrlManager()privateinit(){}funcopen(url:URL){leturlString=url.absoluteStringprint("------------------从其他应用打开--------------------------------")print(Date())print("------------------------------------------------------------------------")print("从\(urlString)"中打开url)print("----------------------------------------------------------------------\n\n")guardletcomponents=urlString.components(separatedBy:"zhitingcloud://operation?").last,letaction=components.components(separatedBy:"&").first?.components(separatedBy:"=";).lastelse{return}switchAction(rawValue:action){case.auth://zhitingcloud://operation?action=authletshareTokenURL=FileManager.default.containerURL(forSecurityApplicationGroupIdentifier:"group.zhiting.tech")!.appendingPathComponent("shareToken.plist")///读取授权成功响应并解密guardletdata=try?Data(contentsOf:shareTokenURL),让json=试试?NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data)作为?Stringelse{return}ifletauthResult=AuthModel.deserialize(from:json){letuser=User()user.nickname=authResult.nickname///存储家庭信息AreaManager.shared.cacheAreas(areas:authResult.areas)if让area=AreaManager.shared.getAreaList().first{AreaManager.shared.currentArea=areauser.user_id=area.sa_user_id}ifletcloudUserId=authResult.cloud_user_id,letcloudUrl=authResult.cloud_url,letcookieValue=authResult.sessionCookie{///云端的授权user.user_id=cloudUserIdUserManager.shared.isCloudUser=trueUserManager.shared.cloudUrl=cloudUrl///写入云端账号cookieifletcookie=HTTPCookie(properties:[HTTPCookiePropertyKey.domain:cloudUrl,HTTPCookiePropertyKey.value:cookieValue,HTTPCookiePropertyKey.path:"/",HTTPCookiePropertyKey.name:"_session_"]){HTTPCookieStorage.shared.setCookie(cookie)}}UserManager.shared.currentUser=userUserManager.shared.cacheUser(user:user)}SceneDelegate.shared.window?.rootViewController=TabbarController()default:break}}}