Skip to content

Commit

Permalink
Merge branch 'release/2.2.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
tobihagemann committed May 23, 2022
2 parents 853aecc + acc2e2c commit 74d302b
Show file tree
Hide file tree
Showing 128 changed files with 3,046 additions and 398 deletions.
125 changes: 113 additions & 12 deletions Cryptomator.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions Cryptomator/Purchase/PurchaseAlert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,16 @@ enum PurchaseAlert {
return showAlert(title: title, message: message, on: presentingViewController)
}

static func showForNoRestorablePurchases(on presentingViewController: UIViewController) -> Promise<Void> {
return showAlert(title: LocalizedString.getValue("purchase.restorePurchase.fullVersionNotFound.alert.title"),
message: LocalizedString.getValue("purchase.restorePurchase.fullVersionNotFound.alert.message"),
on: presentingViewController)
static func showForNoRestorablePurchases(on presentingViewController: UIViewController, eligibleForUpgrade: Bool) -> Promise<Void> {
if eligibleForUpgrade {
return showAlert(title: LocalizedString.getValue("purchase.restorePurchase.eligibleForUpgrade.alert.title"),
message: LocalizedString.getValue("purchase.restorePurchase.eligibleForUpgrade.alert.message"),
on: presentingViewController)
} else {
return showAlert(title: LocalizedString.getValue("purchase.restorePurchase.fullVersionNotFound.alert.title"),
message: LocalizedString.getValue("purchase.restorePurchase.fullVersionNotFound.alert.message"),
on: presentingViewController)
}
}

private static func showAlert(title: String, message: String, on presentingViewController: UIViewController) -> Promise<Void> {
Expand Down
2 changes: 1 addition & 1 deletion Cryptomator/Purchase/PurchaseCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class PurchaseCoordinator: Coordinator {
self.unlockedPro()
}
case .noRestorablePurchases:
_ = PurchaseAlert.showForNoRestorablePurchases(on: navigationController)
_ = PurchaseAlert.showForNoRestorablePurchases(on: navigationController, eligibleForUpgrade: UpgradeChecker.shared.isEligibleForUpgrade())
}
}

Expand Down
19 changes: 12 additions & 7 deletions Cryptomator/Settings/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ class SettingsViewModel: TableViewModel<SettingsSection> {
return elements
}

private let cacheManager: FileProviderCacheManager
private let cacheSizeCellViewModel = LoadingWithLabelCellViewModel(title: LocalizedString.getValue("settings.cacheSize"))
private let clearCacheButtonCellViewModel = ButtonCellViewModel<SettingsButtonAction>(action: .clearCache, title: LocalizedString.getValue("settings.clearCache"), isEnabled: false)

Expand All @@ -93,9 +92,8 @@ class SettingsViewModel: TableViewModel<SettingsSection> {
private var subscribers = Set<AnyCancellable>()
private lazy var showDebugModeWarningPublisher = PassthroughSubject<Void, Never>()

init(cacheManager: FileProviderCacheManager = FileProviderCacheManager(), cryptomatorSetttings: CryptomatorSettings = CryptomatorUserDefaults.shared, fileProviderConnector: FileProviderConnector = FileProviderXPCConnector.shared) {
self.cacheManager = cacheManager
self.cryptomatorSettings = cryptomatorSetttings
init(cryptomatorSettings: CryptomatorSettings = CryptomatorUserDefaults.shared, fileProviderConnector: FileProviderConnector = FileProviderXPCConnector.shared) {
self.cryptomatorSettings = cryptomatorSettings
self.fileProviderConnector = fileProviderConnector
}

Expand All @@ -107,7 +105,11 @@ class SettingsViewModel: TableViewModel<SettingsSection> {
self.clearCacheButtonCellViewModel.isEnabled.value = false
}
}
return cacheManager.getTotalLocalCacheSizeInBytes().then { totalCacheSizeInBytes -> Void in
let getXPCPromise: Promise<XPC<CacheManaging>> = fileProviderConnector.getXPC(serviceName: .cacheManaging, domain: nil)
return getXPCPromise.then { xpc in
xpc.proxy.getLocalCacheSizeInBytes()
}.then { receivedCacheSizeInBytes -> Void in
let totalCacheSizeInBytes = receivedCacheSizeInBytes?.intValue ?? 0
loading = false
self.cacheSizeCellViewModel.isLoading.value = false
self.clearCacheButtonCellViewModel.isEnabled.value = totalCacheSizeInBytes > 0
Expand All @@ -117,7 +119,10 @@ class SettingsViewModel: TableViewModel<SettingsSection> {
}

func clearCache() -> Promise<Void> {
return cacheManager.clearCache().then {
let getXPCPromise: Promise<XPC<CacheManaging>> = fileProviderConnector.getXPC(serviceName: .cacheManaging, domain: nil)
return getXPCPromise.then { xpc in
xpc.proxy.clearCache()
}.then {
self.refreshCacheSize()
}
}
Expand Down Expand Up @@ -152,7 +157,7 @@ class SettingsViewModel: TableViewModel<SettingsSection> {
}

private func notifyFileProviderAboutLogLevelUpdate() {
let getXPCPromise: Promise<XPC<LogLevelUpdating>> = fileProviderConnector.getXPC(serviceName: LogLevelUpdatingService.name, domain: nil)
let getXPCPromise: Promise<XPC<LogLevelUpdating>> = fileProviderConnector.getXPC(serviceName: .logLevelUpdating, domain: nil)
getXPCPromise.then { xpc in
xpc.proxy.logLevelUpdated()
}.always {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ class ChangePasswordViewModel: TableViewModel<ChangePasswordSection>, ChangePass

private func lockVault() -> Promise<Void> {
let domainIdentifier = NSFileProviderDomainIdentifier(vaultAccount.vaultUID)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: VaultLockingService.name, domainIdentifier: domainIdentifier)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domainIdentifier: domainIdentifier)
return getXPCPromise.then { xpc -> Void in
xpc.proxy.lockVault(domainIdentifier: domainIdentifier)
}.always {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class VaultKeepUnlockedViewModel: TableViewModel<VaultKeepUnlockedSection>, Vaul

func gracefulLockVault() -> Promise<Void> {
let domainIdentifier = NSFileProviderDomainIdentifier(vaultUID)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: VaultLockingService.name, domainIdentifier: domainIdentifier)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domainIdentifier: domainIdentifier)
return getXPCPromise.then { xpc in
xpc.proxy.gracefulLockVault(domainIdentifier: domainIdentifier)
}.then {
Expand Down Expand Up @@ -118,7 +118,7 @@ class VaultKeepUnlockedViewModel: TableViewModel<VaultKeepUnlockedSection>, Vaul

private func getVaultIsUnlocked() -> Promise<Bool> {
let domainIdentifier = NSFileProviderDomainIdentifier(vaultUID)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: VaultLockingService.name, domainIdentifier: domainIdentifier)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domainIdentifier: domainIdentifier)
return getXPCPromise.then { xpc in
return xpc.proxy.getIsUnlockedVault(domainIdentifier: domainIdentifier)
}.always {
Expand Down
2 changes: 1 addition & 1 deletion Cryptomator/VaultDetail/MoveVault/MoveVaultViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class MoveVaultViewModel: ChooseFolderViewModel, MoveVaultViewModelProtocol {

private func lockVault() -> Promise<Void> {
let domainIdentifier = NSFileProviderDomainIdentifier(vaultInfo.vaultUID)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: VaultLockingService.name, domainIdentifier: domainIdentifier)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domainIdentifier: domainIdentifier)
return getXPCPromise.then { xpc -> Void in
xpc.proxy.lockVault(domainIdentifier: domainIdentifier)
self.fileProviderConnector.invalidateXPC(xpc)
Expand Down
4 changes: 2 additions & 2 deletions Cryptomator/VaultDetail/VaultDetailViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ class VaultDetailViewModel: VaultDetailViewModelProtocol {

func lockVault() -> Promise<Void> {
let domainIdentifier = NSFileProviderDomainIdentifier(vaultUID)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: VaultLockingService.name, domainIdentifier: domainIdentifier)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domainIdentifier: domainIdentifier)

return getXPCPromise.then { xpc -> Void in
xpc.proxy.lockVault(domainIdentifier: domainIdentifier)
Expand All @@ -225,7 +225,7 @@ class VaultDetailViewModel: VaultDetailViewModelProtocol {

func refreshVaultStatus() -> Promise<Void> {
let domainIdentifier = NSFileProviderDomainIdentifier(vaultUID)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: VaultLockingService.name, domainIdentifier: domainIdentifier)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domainIdentifier: domainIdentifier)
switchCellViewModel?.isOn.value = biometricalUnlockEnabled
return getXPCPromise.then { xpc in
return wrap { handler in
Expand Down
2 changes: 1 addition & 1 deletion Cryptomator/VaultList/VaultCellViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class VaultCellViewModel: TableViewCellViewModel, VaultCellViewModelProtocol {

func lockVault() -> Promise<Void> {
let domainIdentifier = NSFileProviderDomainIdentifier(vault.vaultUID)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: VaultLockingService.name, domainIdentifier: domainIdentifier)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domainIdentifier: domainIdentifier)
return getXPCPromise.then { xpc in
xpc.proxy.lockVault(domainIdentifier: domainIdentifier)
}.then {
Expand Down
4 changes: 2 additions & 2 deletions Cryptomator/VaultList/VaultListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class VaultListViewModel: ViewModel, VaultListViewModelProtocol {

func lockVault(_ vaultInfo: VaultInfo) -> Promise<Void> {
let domainIdentifier = NSFileProviderDomainIdentifier(vaultInfo.vaultUID)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: VaultLockingService.name, domainIdentifier: domainIdentifier)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domainIdentifier: domainIdentifier)
return getXPCPromise.then { xpc in
xpc.proxy.lockVault(domainIdentifier: domainIdentifier)
}.then {
Expand All @@ -104,7 +104,7 @@ class VaultListViewModel: ViewModel, VaultListViewModelProtocol {
}

func refreshVaultLockStates() -> Promise<Void> {
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: VaultLockingService.name, domain: nil)
let getXPCPromise: Promise<XPC<VaultLocking>> = fileProviderConnector.getXPC(serviceName: .vaultLocking, domain: nil)

return getXPCPromise.then { xpc in
return wrap { handler in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//
// CacheManaging.swift
// CryptomatorCommonCore
//
// Created by Philipp Schmid on 10.05.22.
// Copyright © 2022 Skymatic GmbH. All rights reserved.
//

import FileProvider
import Foundation
import Promises

@objc public protocol CacheManaging: NSFileProviderServiceSource {
/**
Evicts the local file belonging to the given `identifier` from the cache.

A file gets evicted from the cache only if there is no pending (or failed) upload for that file.

`Reply` will be called with `nil` if the call was successful, otherwise the error will be passed as an `NSError`.
"Because communication over XPC is asynchronous, all methods in the protocol must have a return type of void. If you need to return data, you can define a reply block [...]" see: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html
*/
func evictFileFromCache(with identifier: NSFileProviderItemIdentifier, reply: @escaping (NSError?) -> Void)

/**
Clears the entire cache of all FileProviderDomains.

Only files that do not have a pending or failed upload are removed from the cache.

`Reply` will be called with `nil` if the call was successful, otherwise the error will be passed as an `NSError`.
"Because communication over XPC is asynchronous, all methods in the protocol must have a return type of void. If you need to return data, you can define a reply block [...]" see: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html
*/
func clearCache(reply: @escaping (NSError?) -> Void)

/**
Returns the total local cache size in bytes currently used by all FileProviderDomains.

- Note: Only files that do not have a pending or failed upload are counted towards the cache.

`Reply` will be called with the size of the local cache size in bytes if the call was successful, otherwise the error will be passed as an `NSError`.
"Because communication over XPC is asynchronous, all methods in the protocol must have a return type of void. If you need to return data, you can define a reply block [...]" see: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html
*/
func getLocalCacheSizeInBytes(reply: @escaping (NSNumber?, NSError?) -> Void)
}

public extension CacheManaging {
/**
Evicts the local file belonging to the given `identifier` from the cache.

A file gets evicted from the cache only if there is no pending (or failed) upload for that file.
*/
func evictFileFromCache(with itemIdentifier: NSFileProviderItemIdentifier) -> Promise<Void> {
return wrap {
self.evictFileFromCache(with: itemIdentifier, reply: $0)
}.then { error -> Void in
if let error = error {
throw error
}
}
}

func evictFilesFromCache(with itemIdentifiers: [NSFileProviderItemIdentifier]) -> Promise<Void> {
guard let itemIdentifier = itemIdentifiers.first else {
return Promise(())
}
return evictFileFromCache(with: itemIdentifier).then {
self.evictFilesFromCache(with: Array(itemIdentifiers.dropFirst()))
}
}

/**
Clears the entire cache of all FileProviderDomains.

Only files that do not have a pending or failed upload are removed from the cache.
*/
func clearCache() -> Promise<Void> {
return wrap {
self.clearCache(reply: $0)
}.then { error -> Void in
if let error = error {
throw error
}
}
}

/**
Returns the total local cache size in bytes currently used by all FileProviderDomains.

- Note: Only files that do not have a pending or failed upload are counted towards the cache.
*/
func getLocalCacheSizeInBytes() -> Promise<NSNumber?> {
return wrap {
self.getLocalCacheSizeInBytes(reply: $0)
}
}
}

public extension NSFileProviderServiceName {
static let cacheManaging = NSFileProviderServiceName("org.cryptomator.ios.cache-managing")
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,6 @@ public class FileProviderXPCConnector: FileProviderConnector {

public static let shared = FileProviderXPCConnector()

public func getProxy<T>(serviceName: NSFileProviderServiceName, domainIdentifier: NSFileProviderDomainIdentifier) -> Promise<T> {
return NSFileProviderManager.getDomains().then { domains in
guard let domain = domains.first(where: { $0.identifier == domainIdentifier }) else {
throw FileProviderXPCConnectorError.domainNotFound
}
return self.getProxy(serviceName: serviceName, domain: domain)
}
}

public func getXPC<T>(serviceName: NSFileProviderServiceName, domain: NSFileProviderDomain?) -> Promise<XPC<T>> {
var url = NSFileProviderManager.default.documentStorageURL
if let domain = domain {
Expand Down Expand Up @@ -93,38 +84,6 @@ public class FileProviderXPCConnector: FileProviderConnector {
})
}
}

public func getProxy<T>(serviceName: NSFileProviderServiceName, domain: NSFileProviderDomain?) -> Promise<T> {
var url = NSFileProviderManager.default.documentStorageURL
if let domain = domain {
url.appendPathComponent(domain.pathRelativeToDocumentStorage)
}
return wrap { handler in
FileManager.default.getFileProviderServicesForItem(at: url, completionHandler: handler)
}.then { services -> Promise<NSXPCConnection?> in
if let desiredService = services?[serviceName] {
return desiredService.getFileProviderConnection()
} else {
return Promise(FileProviderXPCConnectorError.serviceNotSupported)
}
}.then { connection -> T in
guard let connection = connection else {
throw FileProviderXPCConnectorError.connectionIsNil
}
guard let type = T.self as AnyObject as? Protocol else {
throw FileProviderXPCConnectorError.typeMismatch
}
connection.remoteObjectInterface = NSXPCInterface(with: type)
connection.resume()
let rawProxy = connection.remoteObjectProxyWithErrorHandler { errorAccessingRemoteObject in
DDLogError("remoteObjectProxy failed with error: \(errorAccessingRemoteObject)")
}
guard let proxy = rawProxy as? T else {
throw FileProviderXPCConnectorError.rawProxyCastingFailed
}
return proxy
}
}
}

extension NSFileProviderService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import Foundation
func logLevelUpdated()
}

public enum LogLevelUpdatingService {
public static var name: NSFileProviderServiceName {
return NSFileProviderServiceName("org.cryptomator.ios.log-level-updating")
}
public extension NSFileProviderServiceName {
static let logLevelUpdating = NSFileProviderServiceName("org.cryptomator.ios.log-level-updating")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// UploadRetrying.swift
// CryptomatorCommonCore
//
// Created by Philipp Schmid on 03.05.22.
// Copyright © 2021 Skymatic GmbH. All rights reserved.
//

import FileProvider
import Foundation

@objc public protocol UploadRetrying: NSFileProviderServiceSource {
/**
Retries the upload for the given item identifiers.
*/
func retryUpload(for itemIdentifiers: [NSFileProviderItemIdentifier], reply: @escaping (Error?) -> Void)

func getCurrentFractionalUploadProgress(for itemIdentifier: NSFileProviderItemIdentifier, reply: @escaping (NSNumber?) -> Void)
}

public extension NSFileProviderServiceName {
static let uploadRetryingService = NSFileProviderServiceName("org.cryptomator.ios.upload-retrying")
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ import Promises
func getUnlockedVaultDomainIdentifiers(reply: @escaping ([NSFileProviderDomainIdentifier]) -> Void)
}

public enum VaultLockingService {
public static var name: NSFileProviderServiceName {
return NSFileProviderServiceName("org.cryptomator.ios.vault-locking")
}
public extension NSFileProviderServiceName {
static let vaultLocking = NSFileProviderServiceName("org.cryptomator.ios.vault-locking")
}

// MARK: Convenience
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import Foundation
func endBiometricalUnlock()
}

public enum VaultUnlockingService {
public static var name: NSFileProviderServiceName {
return NSFileProviderServiceName("org.cryptomator.ios.vault-unlocking")
}
public extension NSFileProviderServiceName {
static let vaultUnlocking = NSFileProviderServiceName("org.cryptomator.ios.vault-unlocking")
}
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,17 @@ public extension NSFileProviderManager {

public extension NSFileProviderDomain {
convenience init(vaultUID: String, displayName: String) {
self.init(identifier: NSFileProviderDomainIdentifier(vaultUID), displayName: displayName, pathRelativeToDocumentStorage: vaultUID)
self.init(identifier: NSFileProviderDomainIdentifier(vaultUID), displayName: displayName)
}

convenience init(identifier: NSFileProviderDomainIdentifier, displayName: String) {
self.init(identifier: identifier, displayName: displayName, pathRelativeToDocumentStorage: identifier.rawValue)
}

/**
Creates a NSFileProviderDomain from a `NSFileProviderDomainIdentifier` where the `pathRelativeToDocumentStorage` equals to the raw value of the given `identifier` and an empty `displayName`.
*/
convenience init(identifier: NSFileProviderDomainIdentifier) {
self.init(identifier: identifier, displayName: "")
}
}
Loading

0 comments on commit 74d302b

Please sign in to comment.