Swift Compatibility Platform Compatibility Codacy Code Quality Codacy Coverage

Cloud Access Swift

该库定义了 Cryptomator for iOS 使用的云访问 API。

每个云存储服务都需要实现一次该 API。它也构成了应用各种 vault 格式的装饰层的基础,以便为云存储的 vault 提供明文视图。

要求

安装

Swift Package Manager

您可以使用 Swift Package Manager

.package(url: "https://github.com/cryptomator/cloud-access-swift.git", .upToNextMinor(from: "1.12.0"))

用法

核心

核心包包含几个协议、结构体和枚举,它们构成了该库的基础。异步调用使用 Promises 库实现。CloudProvider 是定义云访问的主要协议。

func fetchItemMetadata(at cloudPath: CloudPath) -> Promise<CloudItemMetadata>
func fetchItemList(forFolderAt cloudPath: CloudPath, withPageToken pageToken: String?) -> Promise<CloudItemList>
func downloadFile(from cloudPath: CloudPath, to localURL: URL, onTaskCreation: ((URLSessionDownloadTask?) -> Void)?) -> Promise<Void>
func uploadFile(from localURL: URL, to cloudPath: CloudPath, replaceExisting: Bool, onTaskCreation: ((URLSessionUploadTask?) -> Void)?) -> Promise<CloudItemMetadata>
func createFolder(at cloudPath: CloudPath) -> Promise<Void>
func deleteFile(at cloudPath: CloudPath) -> Promise<Void>
func deleteFolder(at cloudPath: CloudPath) -> Promise<Void>
func moveFile(from sourceCloudPath: CloudPath, to targetCloudPath: CloudPath) -> Promise<Void>
func moveFolder(from sourceCloudPath: CloudPath, to targetCloudPath: CloudPath) -> Promise<Void>

加密

Vault 提供程序装饰云提供程序,并允许透明地访问基于 Cryptomator 加密方案的 vault。 它依赖于 cryptolib-swift 进行加密功能,并依赖于 GRDB 进行线程安全缓存。 有关 Cryptomator 加密方案的更多信息,请访问 docs.cryptomator.org 上的安全架构页面。

为了创建一个 vault 提供程序,您需要来自 cryptolib-swift 的一个 Masterkey 实例。 查看它的文档,了解如何创建一个主密钥。 并且自从 vault 格式 8 以来,您还需要一个 UnverifiedVaultConfig 实例。

let provider = ... // any other cloud provider
let vaultPath = ...
let masterkey = ...
let token = ...
let unverifiedVaultConfig = try UnverifiedVaultConfig(token: token)
let cryptoDecorator = try VaultProviderFactory.createVaultProvider(from: unverifiedVaultConfig, masterkey: masterkey, vaultPath: vaultPath, with: provider)

以及创建 vault 版本 6 或 7 的旧版 vault 提供程序

let provider = ... // any other cloud provider
let vaultVersion = ... // use `version` from the `MasterkeyFile` instance
let vaultPath = ...
let masterkey = ...
let cryptoDecorator = try VaultProviderFactory.createLegacyVaultProvider(from: masterkey, vaultVersion: vaultVersion, vaultPath: vaultPath, with: provider)

⚠️该库支持 vault 版本 6 及更高版本。

Box

以下常量必须设置一次,例如,在您的 app delegate 中

let clientId = ... // your Box client identifier
let clientSecret = ... // your Box client secret
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `BoxCloudProvider` with a background `URLSession` in an app extension 
BoxSetup.constants = BoxSetup(clientId: clientId, clientSecret: clientSecret, sharedContainerIdentifier: sharedContainerIdentifier)

开始身份验证流程

let tokenStore = BoxTokenStore()
let credential = BoxCredential(tokenStore: tokenStore)
let viewController = ... // the presenting `UIViewController`
BoxAuthenticator.authenticate(credential: credential, from: viewController).then { 
  // authentication successful
}.catch { error in
  // error handling
}

然后,您可以使用凭据创建一个 Box 提供程序

let provider = BoxCloudProvider(credential: credential)

或者使用后台 URLSession 创建一个 Box 提供程序

let sessionIdentifier = ...
let provider = BoxCloudProvider.withBackgroundSession(credential: credential, sessionIdentifier: sessionIdentifier)

Dropbox

按照 官方 Dropbox Objective-C SDK 中的描述设置 Info.plist。 此外,以下常量必须设置一次,例如,在您的 app delegate 中

let appKey = ... // your Dropbox app key
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `DropboxProvider` in an app extension and set `forceForegroundSession = false`
let keychainService = ... // the service name for the keychain, use `nil` to use default
let forceForegroundSession = ... // if set to `true`, all network requests are made on foreground sessions (by default, most download/upload operations are performed with a background session)
DropboxSetup.constants = DropboxSetup(appKey: appKey, sharedContainerIdentifier: sharedContainerIdentifier, keychainService: keychainService, forceForegroundSession: forceForegroundSession)

开始身份验证流程

let dropboxAuthenticator = DropboxAuthenticator()
let viewController = ... // the presenting `UIViewController`
dropboxAuthenticator.authenticate(from: viewController).then { credential in
  // do something with `DropboxCredential`
  // you probably want to save `credential.tokenUID` to re-create the credential later
}.catch { error in
  // error handling
}

在您的 app delegate 中处理身份验证流程完成后,重定向

func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
  let canHandle = DBClientsManager.handleRedirectURL(url) { authResult in
    guard let authResult = authResult else {
      return
    }
    if authResult.isSuccess() {
      let tokenUID = authResult.accessToken.uid
      let credential = DropboxCredential(tokenUID: tokenUID)
      DropboxAuthenticator.pendingAuthentication?.fulfill(credential)
      } else if authResult.isCancel() {
        DropboxAuthenticator.pendingAuthentication?.reject(DropboxAuthenticatorError.userCanceled)
      } else if authResult.isError() {
        DropboxAuthenticator.pendingAuthentication?.reject(authResult.nsError)
      }
    }
  return canHandle
}

使用凭据创建一个 Dropbox 提供程序

let tokenUID = ... // the `tokenUID` you saved after the successful authentication flow
let credential = DropboxCredential(tokenUID: tokenUID)
let provider = DropboxCloudProvider(credential: credential)

Google Drive

按照 AppAuth 中的描述修改您的 app delegate。 此外,以下常量必须设置一次,例如,在您的 app delegate 中

let clientId = ... // your Google Drive client identifier
let redirectURL = ...
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `GoogleDriveProvider` with a background `URLSession` in an app extension 
GoogleDriveSetup.constants = GoogleDriveSetup(clientId: clientId, redirectURL: redirectURL, sharedContainerIdentifier: sharedContainerIdentifier)

开始身份验证流程

let tokenUID = ... // optional: you might want to give this credential an identifier, defaults to a random UUID
let credential = GoogleDriveCredential(tokenUID: tokenUID)
let viewController = ... // the presenting `UIViewController`
GoogleDriveAuthenticator.authenticate(credential: credential, from: viewController).then { 
  // authentication successful
}.catch { error in
  // error handling
}

然后,您可以使用凭据创建一个 Google Drive 提供程序

let provider = GoogleDriveCloudProvider(credential: credential)

或者使用后台 URLSession 创建一个 Google Drive 提供程序

let sessionIdentifier = ...
let provider = GoogleDriveCloudProvider.withBackgroundSession(credential: credential, sessionIdentifier: sessionIdentifier)

OneDrive

按照 MSAL 中的描述设置 Info.plist 和您的 app delegate。 此外,以下常量必须设置一次,例如,在您的 app delegate 中

let clientApplication = ... // your `MSALPublicClientApplication`
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `OneDriveProvider` with a background `URLSession` in an app extension
OneDriveSetup.constants = OneDriveSetup(clientApplication: clientApplication, sharedContainerIdentifier: sharedContainerIdentifier)

开始身份验证流程

let viewController = ... // the presenting `UIViewController`
OneDriveAuthenticator.authenticate(from: viewController).then { credential in
  // do something with `OneDriveCredential`
  // you probably want to save `credential.identifier` to re-create the credential later
}.catch { error in
  // error handling
}

然后,您可以使用凭据创建一个 OneDrive 提供程序

let provider = OneDriveCloudProvider(credential: credential)

或者使用后台 URLSession 创建一个 OneDrive 提供程序

let sessionIdentifier = ...
let provider = OneDriveCloudProvider.withBackgroundSession(credential: credential, sessionIdentifier: sessionIdentifier)

pCloud

以下常量必须设置一次,例如,在您的 app delegate 中

let appKey = ... // your pCloud app key
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `OneDriveProvider` with a background `URLSession` in an app extension
PCloudSetup.constants = PCloudSetup(appKey: appKey, sharedContainerIdentifier: sharedContainerIdentifier)

开始身份验证流程

let viewController = ... // the presenting `UIViewController`
PCloudAuthenticator.authenticate(from: viewController).then { credential in
  // do something with `PCloudCredential`
  // you probably want to save `credential.user` to re-create the credential later
}.catch { error in
  // error handling
}

然后,您可以使用凭据创建一个带有 pCloud 客户端的 pCloud 提供程序

let client = PCloud.createClient(with: credential.user)
let provider = PCloudCloudProvider(client: client)

或者使用后台 URLSession 创建一个带有 pCloud 客户端的 pCloud 提供程序

let sessionIdentifier = ...
let client = PCloud.createBackgroundClient(with: credential.user, sessionIdentifier: sessionIdentifier)
let provider = PCloudCloudProvider(client: client)

S3

创建一个 S3 凭据

let accessKey = ...
let secretKey = ...
let url = ... // Note: the URL should not already contain the bucket name
let bucket = ... // Note: the bucket should already exist
let region = ...
let identifier = ... // optional: you might want to give this credential an identifier, defaults to a random UUID
let credential = S3Credential(accessKey: accessKey, secretKey: secretKey, url: url, bucket: bucket, region: region, identifier: identifier)

然后,您可以使用凭据创建一个 S3 提供程序

let provider = try S3Provider(credential: credential)

或者使用后台 URLSession 创建一个 S3 提供程序

let sharedContainerIdentifier = ... // optional: only needed if you want to create a `S3CloudProvider` in an app extension 
let provider = try S3CloudProvider.withBackgroundSession(credential: credential, sharedContainerIdentifier: sharedContainerIdentifier)

理论上,您可以不经过进一步检查直接使用该提供程序。 但是,您应该使用 S3 验证器验证 S3 凭据

let credential = ...
S3Authenticator.verifyCredential(credential).then {
  // credential validation successful
}.catch { error in
  // error handling
}

WebDAV

创建一个 WebDAV 凭据

let baseURL = ...
let username = ...
let password = ...
let allowedCertificate = ... // optional: you might want to allowlist a TLS certificate
let identifier = ... // optional: you might want to give this credential an identifier, defaults to a random UUID
let credential = WebDAVCredential(baseURL: baseURL, username: username, password: password, allowedCertificate: allowedCertificate, identifier: identifier)

然后,您可以使用凭据创建一个带有 WebDAV 客户端的 WebDAV 提供程序

let client = WebDAVClient(credential: credential)
let provider = WebDAVProvider(with: client)

或者使用后台 URLSession 创建一个带有 WebDAV 客户端的 WebDAV 提供程序

let sessionIdentifier = ...
let sharedContainerIdentifier = ... // optional: only needed if you want to create a `WebDAVProvider` in an app extension 
let client = WebDAVClient.withBackgroundSession(credential: credential, sessionIdentifier: sessionIdentifier, sharedContainerIdentifier: sharedContainerIdentifier)
let provider = WebDAVProvider(with: client)

理论上,您可以不经过进一步检查直接使用该提供程序。 但是,您应该使用 WebDAV 验证器验证 WebDAV 客户端及其凭据

let client = ...
WebDAVAuthenticator.verifyClient(client: client).then {
  // client validation successful
}.catch { error in
  // error handling
}

此外,对于允许证书,您可以使用 TLS 证书验证器

let baseURL = ...
let validator = TLSCertificateValidator(baseURL: baseURL)
validator.validate().then { certificate in
  // certificate of type `TLSCertificate` contains several properties for further handling
}.catch { error in
  // error handling
}

本地文件系统

由于本地文件系统实际上不是云存储服务,因此命名可能令人困惑。 但是,可以通过本地文件系统访问 iCloud Drive,并且此提供程序包含用于处理卸载项的代码。

使用根 URL 创建一个本地文件系统提供程序

let rootURL = ... // rootURL.isFileURL must be `true`
let provider = LocalFileSystemProvider(rootURL: rootURL)

在调用此提供程序的功能时,应提供相对于根 URL 的云路径。

此提供程序使用 NSFileCoordinator 进行其操作,并支持异步访问。

日志记录

此 SDK 使用 CocoaLumberjack 进行日志记录。 CocoaLumberjack 是一个灵活、快速的开源日志记录框架。 它支持许多功能,包括为每个输出目标设置日志记录级别,例如,将简洁的消息记录到控制台,并将详细的消息记录到日志文件。

CocoaLumberjack 日志记录级别是可加的,这样,当级别设置为 verbose 时,将记录来自 verbose 以下级别的所有消息。 也可以设置自定义日志记录以满足您的需求。 有关更多信息,请参见 CocoaLumberjack

更改日志级别

dynamicCloudAccessLogLevel = .verbose

以下日志记录级别选项可用

定向日志输出

定义日志输出目标的工作方式与 CocoaLumberjack 相同,唯一的区别是使用 CloudAccessDDLog.add() 而不是 DDLog.add() 添加 logger。 例如

CloudAccessDDLog.add(DDOSLogger.sharedInstance) // Uses os_log

let fileLogger: DDFileLogger = DDFileLogger() // File Logger
fileLogger.rollingFrequency = 60 * 60 * 24 // 24 hours
fileLogger.logFileManager.maximumNumberOfLogFiles = 7
CloudAccessDDLog.add(fileLogger)

集成测试

您可以 在此处 了解有关云提供商集成测试的更多信息。

贡献

如果您想报告错误、提出问题或帮助我们进行编码,请阅读我们的 贡献指南

该项目使用 SwiftFormatSwiftLint 来强制执行代码样式和约定。 如果您尚未安装这些工具,请安装它们。

请确保您的代码格式正确并传递 linter 验证。 最简单的方法是设置一个 pre-commit hook。 在 .git/hooks/pre-commit 创建一个文件,内容如下:

./Scripts/process.sh --staged
exit $?

并使您的 pre-commit hook 可执行

chmod +x .git/hooks/pre-commit

行为准则

帮助我们保持 Cryptomator 的开放性和包容性。 请阅读并遵守我们的 行为准则

许可证

该项目在 AGPLv3(针对 FOSS 项目)以及源自 LGPL(针对独立软件供应商和经销商)的商业许可证下获得双重许可。 如果您想在根据 AGPL 许可的应用程序中使用此库,请随时联系我们的 销售团队