diff --git a/CHANGELOG.en.md b/CHANGELOG.en.md index 3a7c7c1a..133aa269 100644 --- a/CHANGELOG.en.md +++ b/CHANGELOG.en.md @@ -1,5 +1,13 @@ # Changelog +## [0.12.4 (621)] - 2023-01-19 + +- You can now change profile by swiping down the profile picture on the top left. +- Improves the reliability of the startup procedure after installing a new Olvid version. +- Fixes a bug where sharing with Olvid could fail from certain Apple's app (such as Music or the Developer app). +- Fixes a bug that could sometimes prevent the profile edition. +- Fixes a crash under iOS 15.7.x + ## [0.12.3 (611)] - 2023-01-11 - Introducing a long awaited feature! You can now create as many (independent) profiles as you want! For example, you can create one for family and friends and another for work. diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md index 084f45cc..5e26743d 100644 --- a/CHANGELOG.fr.md +++ b/CHANGELOG.fr.md @@ -1,5 +1,13 @@ # Changelog +## [0.12.4 (621)] - 2023-01-19 + +- Vous pouvez maintenant changer de profil en faisant glisser la photo de profil située en haut à gauche. +- Améliore la robustesse de l'app, notamment au démarrage après une mise à jour. +- Corrige un bug qui pouvait empêcher le partage vers Olvid depuis certaines Apps d'Apple (comme Music ou l'app Developer). +- Corrige un bug qui empêchait parfois d'éditer son profil. +- Corrige un crash sous iOS 15.7.x + ## [0.12.3 (611)] - 2023-01-11 - Vous l'attendiez tous... Vous pouvez maintenant créer autant de profils (indépendants) que vous voulez ! Par exemple, vous pouvez créer un profil pour la famille et les amis et un autre pour vos activités professionnelles. diff --git a/CoreDataStack/CoreDataStack/DataMigrationManager.swift b/CoreDataStack/CoreDataStack/DataMigrationManager.swift index d2b044cd..c517334c 100644 --- a/CoreDataStack/CoreDataStack/DataMigrationManager.swift +++ b/CoreDataStack/CoreDataStack/DataMigrationManager.swift @@ -114,7 +114,6 @@ open class DataMigrationManager try performMigration() } else { migrationRunningLog.addEvent(message: "No migration needed") - cleanOldTemporaryMigrationFiles() } } catch { migrationRunningLog.addEvent(message: "The migration failed: \(error.localizedDescription). Domain: \((error as NSError).domain)") @@ -124,6 +123,7 @@ open class DataMigrationManager migrationRunningLog.addEvent(message: "Creating the core data stack") self._coreDataStack = CoreDataStack(modelName: modelName, transactionAuthor: transactionAuthor) + cleanOldTemporaryMigrationFiles() } private var _coreDataStack: CoreDataStack! @@ -162,9 +162,12 @@ open class DataMigrationManager } private func getSourceStoreMetadata(storeURL: URL) throws -> [String: Any] { - let dict = try NSPersistentStoreCoordinator.metadataForPersistentStore(ofType: NSSQLiteStoreType, - at: storeURL, - options: nil) + let dict: [String: Any] + if #available(iOS 15, *) { + dict = try NSPersistentStoreCoordinator.metadataForPersistentStore(type: .sqlite, at: storeURL) + } else { + dict = try NSPersistentStoreCoordinator.metadataForPersistentStore(ofType: NSSQLiteStoreType, at: storeURL) + } return dict } @@ -209,17 +212,64 @@ open class DataMigrationManager private func getStoreManagedObjectModel(storeURL: URL) throws -> NSManagedObjectModel { let storeMetadata = try getSourceStoreMetadata(storeURL: storeURL) + let allModels = try getAllManagedObjectModels() for model in allModels { if model.isConfiguration(withName: nil, compatibleWithStoreMetadata: storeMetadata) { return model } } + + // If we reach this point, we could not find a model compatible with the store metadata + // We log a few things to debug this situation migrationRunningLog.addEvent(message: "Could not determine the store managed object model on disk") + do { + logStoreMetadataTo(migrationRunningLog: migrationRunningLog, storeMetadata: storeMetadata) + if let storeModelVersionIdentifiers = (storeMetadata[NSStoreModelVersionIdentifiersKey] as? NSArray)?.firstObject as? String { + migrationRunningLog.addEvent(message: "The store model version identifier from the store metadadata found on disk is \(storeModelVersionIdentifiers)") + if let model = allModels.first(where: { $0.versionIdentifier == storeModelVersionIdentifiers }) { + migrationRunningLog.addEvent(message: "We found a model having the version identifier \(storeModelVersionIdentifiers). Logging its details now.") + logNSManagedObjectModelTo(migrationRunningLog: migrationRunningLog, model: model) + } else { + migrationRunningLog.addEvent(message: "Among all the models, we could not find a model having an identifier equal to \(storeModelVersionIdentifiers)") + } + } else { + migrationRunningLog.addEvent(message: "Could not determine the store model version identifier from the store metadadata found on disk") + } + } + throw DataMigrationManager.makeError(message: "Could not determine the store managed object model on disk") } + private func logStoreMetadataTo(migrationRunningLog: RunningLogError, storeMetadata: [String: Any]) { + migrationRunningLog.addEvent(message: "Content of Store Metadata on disk:") + for (key, value) in storeMetadata { + if key == "NSStoreModelVersionHashes", let modelVersionHashes = value as? [String: Data] { + migrationRunningLog.addEvent(message: " \(key) :") + let sortedModelVersionHashes = modelVersionHashes.sorted { $0.key < $1.key } + for (key, value) in sortedModelVersionHashes { + migrationRunningLog.addEvent(message: " \(key) : \(value.hexString())") + } + } else { + migrationRunningLog.addEvent(message: " \(key) : \(String(describing: value))") + } + } + } + + + private func logNSManagedObjectModelTo(migrationRunningLog: RunningLogError, model: NSManagedObjectModel) { + let entities = model.entities.sorted { ($0.name ?? "") < ($1.name ?? "") } + for entity in entities { + if let entityName = entity.name { + migrationRunningLog.addEvent(message: " \(entityName) : \(entity.versionHash.hexString())") + } else { + migrationRunningLog.addEvent(message: " Entity without name : \(entity.versionHash.hexString())") + } + } + } + + // MARK: - Is migration needed private func isMigrationNeeded() throws -> Bool { @@ -235,6 +285,64 @@ open class DataMigrationManager migrationRunningLog.addEvent(message: "Failed to get source store metadata: \(error.localizedDescription)") os_log("Failed to get source store metadata: %{public}@", log: log, type: .fault, error.localizedDescription) logDebugInformation() + assert(SQLITE_CORRUPT == 11) + let nsError = error as NSError + if (nsError.domain == NSSQLiteErrorDomain && nsError.code == SQLITE_CORRUPT) || + (nsError.domain == NSCocoaErrorDomain && nsError.code == NSPersistentStoreInvalidTypeError) { + // If the database is corrupted, we know we won't be able to do anything with the file. + // Before giving up, we look for another (not corrupted) .sqlite file in the same directory. + // If non is found, we have no choice but to throw an error. + // If one or more are found, we keep the one with the latest version, use it to replace the corrupted .sqlite file, and try again. + migrationRunningLog.addEvent(message: "[RECOVERY] Since the database is corrupted, we try to recover") + if let urlOfLatestUsableSQLiteFile = getURLOfLatestUsableSQLiteFile(distinctFrom: storeURL) { + + migrationRunningLog.addEvent(message: "[RECOVERY] We found a candidate for the database replacement: \(urlOfLatestUsableSQLiteFile.lastPathComponent)") + + // Step 1: remove all files relating to the corrupted database (in that order shm file -> wal file -> sqlite file) + let shmFile = self.storeURL.deletingPathExtension().appendingPathExtension("sqlite-shm") + let walFile = self.storeURL.deletingPathExtension().appendingPathExtension("sqlite-wal") + do { + if FileManager.default.fileExists(atPath: shmFile.path) { + migrationRunningLog.addEvent(message: "[RECOVERY] Deleting \(shmFile.lastPathComponent)") + try FileManager.default.removeItem(at: shmFile) + } else { + migrationRunningLog.addEvent(message: "[RECOVERY] No \(shmFile.lastPathComponent) to delete") + } + if FileManager.default.fileExists(atPath: walFile.path) { + migrationRunningLog.addEvent(message: "[RECOVERY] Deleting \(walFile.lastPathComponent)") + try FileManager.default.removeItem(at: walFile) + } else { + migrationRunningLog.addEvent(message: "[RECOVERY] No \(walFile.lastPathComponent) to delete") + } + if FileManager.default.fileExists(atPath: storeURL.path) { + migrationRunningLog.addEvent(message: "[RECOVERY] Deleting \(storeURL.lastPathComponent)") + try FileManager.default.removeItem(at: storeURL) + } else { + migrationRunningLog.addEvent(message: "[RECOVERY] No \(storeURL.lastPathComponent) to delete") + } + } + + // Step 2: move the latest usable SQLite file (and its associated files, in that order: sqlite file -> wal file -> shm file) + let shmFileSource = urlOfLatestUsableSQLiteFile.deletingPathExtension().appendingPathExtension("sqlite-shm") + let walFileSource = urlOfLatestUsableSQLiteFile.deletingPathExtension().appendingPathExtension("sqlite-wal") + try FileManager.default.moveItem(at: urlOfLatestUsableSQLiteFile, to: storeURL) + migrationRunningLog.addEvent(message: "[RECOVERY] Did move \(urlOfLatestUsableSQLiteFile.lastPathComponent) to \(storeURL.lastPathComponent)") + if FileManager.default.fileExists(atPath: walFileSource.path) { + try FileManager.default.moveItem(at: walFileSource, to: walFile) + migrationRunningLog.addEvent(message: "[RECOVERY] Did move \(walFileSource.lastPathComponent) to \(walFile.lastPathComponent)") + } + if FileManager.default.fileExists(atPath: shmFileSource.path) { + try FileManager.default.moveItem(at: shmFileSource, to: shmFile) + migrationRunningLog.addEvent(message: "[RECOVERY] Did move \(shmFileSource.lastPathComponent) to \(shmFile.lastPathComponent)") + } + + // We have replaced the corrupted database found by the best possible candidate. Now we try again. + + migrationRunningLog.addEvent(message: "[RECOVERY] Done with recovery operations. We test again whether a migration is needed.") + return try isMigrationNeeded() + } + migrationRunningLog.addEvent(message: "[RECOVERY] We could not recover as no temporary SQLite file could be found") + } throw error } @@ -266,6 +374,48 @@ open class DataMigrationManager df.timeStyle = .short return df }() + + + private func getURLOfLatestUsableSQLiteFile(distinctFrom urlToSkip: URL) -> URL? { + migrationRunningLog.addEvent(message: "[RECOVERY] Looking for the latest temporary SQLite file") + var urlOfLatestSQLiteFile: URL? + var modelVersionOfLatestSQLiteFile: String? + var isDirectory: ObjCBool = false + if FileManager.default.fileExists(atPath: storeURL.path, isDirectory: &isDirectory), !isDirectory.boolValue { + let storeDirectory = storeURL.deletingLastPathComponent() + migrationRunningLog.addEvent(message: "[RECOVERY] Looking for the latest temporary SQLite file in \(storeDirectory.path)") + if let directoryContents = try? FileManager.default.contentsOfDirectory(at: storeDirectory, includingPropertiesForKeys: nil) { + for file in directoryContents { + guard file.pathExtension == "sqlite" && file != urlToSkip else { continue } + migrationRunningLog.addEvent(message: "[RECOVERY] Found an sqlite file: \(file.lastPathComponent)") + guard let sourceStoreMetadata = try? getSourceStoreMetadata(storeURL: file) else { + migrationRunningLog.addEvent(message: "[RECOVERY] Could not get metadata of file: \(file.lastPathComponent)") + continue + } + guard let sourceVersionIdentifier = (sourceStoreMetadata[NSStoreModelVersionIdentifiersKey] as? [Any])?.first as? String else { + assertionFailure() + migrationRunningLog.addEvent(message: "[RECOVERY] Could not get version identifier from source store metadata of file: \(file.lastPathComponent)") + continue + } + migrationRunningLog.addEvent(message: "[RECOVERY] The source version identifier of file \(file.lastPathComponent) is \(sourceVersionIdentifier)") + guard (try? modelVersion(sourceVersionIdentifier, isMoreRecentThan: modelVersionOfLatestSQLiteFile)) == true else { + migrationRunningLog.addEvent(message: "[RECOVERY] The file \(file.lastPathComponent) is not the latest") + continue + } + // We found a new latest candidate + migrationRunningLog.addEvent(message: "[RECOVERY] We found a new candidate for the latest temporary SQLite file: \(file.lastPathComponent)") + urlOfLatestSQLiteFile = file + modelVersionOfLatestSQLiteFile = sourceVersionIdentifier + } + } + } + if let urlOfLatestSQLiteFile { + migrationRunningLog.addEvent(message: "[RECOVERY] Returning \(urlOfLatestSQLiteFile.lastPathComponent) as the latest temporary sqlite file") + } else { + migrationRunningLog.addEvent(message: "[RECOVERY] We could not find any temporary sqlite file") + } + return urlOfLatestSQLiteFile + } /// As for now, this method is called when we fail to obtain (metada) information about the current version of the database and thus, fail to migrate to a new version. @@ -633,6 +783,10 @@ open class DataMigrationManager } + open func modelVersion(_ rawModelVersion: String, isMoreRecentThan otherRawModelVersion: String?) throws -> Bool { + fatalError("Must be overwritten by subclass") + } + } diff --git a/Engine/ObvDatabaseManager/ObvDatabaseManager/DataMigrationManagerForObvEngine.swift b/Engine/ObvDatabaseManager/ObvDatabaseManager/DataMigrationManagerForObvEngine.swift index d0c0a01a..8dc85672 100644 --- a/Engine/ObvDatabaseManager/ObvDatabaseManager/DataMigrationManagerForObvEngine.swift +++ b/Engine/ObvDatabaseManager/ObvDatabaseManager/DataMigrationManagerForObvEngine.swift @@ -89,6 +89,13 @@ final class DataMigrationManagerForObvEngine: DataMigrationManager Bool { + guard let otherRawModelVersion else { return true } + guard let otherModelVersion = ObvEngineModelVersion(rawValue: otherRawModelVersion) else { + assertionFailure() + throw Self.makeError(message: "Could not parse other raw model version") + } + guard let modelVersion = ObvEngineModelVersion(rawValue: rawModelVersion) else { + assertionFailure() + throw Self.makeError(message: "Could not parse raw model version") + } + guard let otherModelVersionAsInt = otherModelVersion.intValue else { + assertionFailure() + throw Self.makeError(message: "Could not determine int value from other model version") + } + guard let modelVersionAsInt = modelVersion.intValue else { + assertionFailure() + throw Self.makeError(message: "Could not determine int value from model version") + } + return modelVersionAsInt > otherModelVersionAsInt + } + } diff --git a/Engine/ObvNetworkFetchManager/ObvNetworkFetchManager/Coordinators/NetworkFetchFlowCoordinator.swift b/Engine/ObvNetworkFetchManager/ObvNetworkFetchManager/Coordinators/NetworkFetchFlowCoordinator.swift index c0abde57..0ce8e12f 100644 --- a/Engine/ObvNetworkFetchManager/ObvNetworkFetchManager/Coordinators/NetworkFetchFlowCoordinator.swift +++ b/Engine/ObvNetworkFetchManager/ObvNetworkFetchManager/Coordinators/NetworkFetchFlowCoordinator.swift @@ -35,6 +35,7 @@ final class NetworkFetchFlowCoordinator: NetworkFetchFlowDelegate, ObvErrorMaker private let queueForPostingNotifications = DispatchQueue(label: "NetworkFetchFlowCoordinator queue for notifications") private let internalQueue = OperationQueue.createSerialQueue(name: "NetworkFetchFlowCoordinator internal operation queue") + private let syncQueue = DispatchQueue(label: "NetworkFetchFlowCoordinator internal queue") weak var delegateManager: ObvNetworkFetchDelegateManager? { didSet { @@ -419,20 +420,14 @@ extension NetworkFetchFlowCoordinator { os_log("The Delegate Manager is not set", log: log, type: .fault) return } - - let log = OSLog(subsystem: delegateManager.logSubsystem, category: logCategory) - os_log("Processing unprocessed messages within flow %{public}@", log: log, type: .debug, flowId.debugDescription) - - if Thread.isMainThread { - os_log("processUnprocessedMessages is running on the main thread", log: log, type: .fault) - } + let log = OSLog(subsystem: delegateManager.logSubsystem, category: logCategory) guard let notificationDelegate = delegateManager.notificationDelegate else { os_log("The notification delegate is not set", log: log, type: .fault) return } - + guard let contextCreator = delegateManager.contextCreator else { os_log("The context creator is not set", log: log, type: .fault) return @@ -442,38 +437,46 @@ extension NetworkFetchFlowCoordinator { os_log("The processDownloadedMessageDelegate is not set", log: log, type: .fault) return } + + let queueForPostingNotifications = self.queueForPostingNotifications + let internalQueue = self.internalQueue - var moreUnprocessedMessagesRemain = true - var maxNumberOfOperations = 1_000 - - while moreUnprocessedMessagesRemain && maxNumberOfOperations > 0 { - - maxNumberOfOperations -= 1 - assert(maxNumberOfOperations > 0, "May happen if there were many unprocessed messages. But this is unlikely and should be investigated.") + syncQueue.async { + + os_log("Processing unprocessed messages within flow %{public}@", log: log, type: .debug, flowId.debugDescription) + + var moreUnprocessedMessagesRemain = true + var maxNumberOfOperations = 1_000 - os_log("Initializing a ProcessBatchOfUnprocessedMessagesOperation (maxNumberOfOperations is %d)", log: log, type: .info, maxNumberOfOperations) - let op1 = ProcessBatchOfUnprocessedMessagesOperation(ownedCryptoIdentity: ownedCryptoIdentity, - queueForPostingNotifications: queueForPostingNotifications, - notificationDelegate: notificationDelegate, - processDownloadedMessageDelegate: processDownloadedMessageDelegate, - log: log) - let composedOp = CompositionOfOneContextualOperation(op1: op1, contextCreator: contextCreator, log: log, flowId: flowId) - internalQueue.addOperations([composedOp], waitUntilFinished: true) - composedOp.logReasonIfCancelled(log: log) - if composedOp.isCancelled { - os_log("The ProcessBatchOfUnprocessedMessagesOperation cancelled: %{public}@", log: log, type: .fault, composedOp.reasonForCancel?.localizedDescription ?? "No reason given") - assertionFailure(composedOp.reasonForCancel.debugDescription) - moreUnprocessedMessagesRemain = false - } else { - os_log("The ProcessBatchOfUnprocessedMessagesOperation succeeded", log: log, type: .info) - moreUnprocessedMessagesRemain = op1.moreUnprocessedMessagesRemain ?? false - if moreUnprocessedMessagesRemain { - os_log("More unprocessed messages remain", log: log, type: .info) + while moreUnprocessedMessagesRemain && maxNumberOfOperations > 0 { + + maxNumberOfOperations -= 1 + assert(maxNumberOfOperations > 0, "May happen if there were many unprocessed messages. But this is unlikely and should be investigated.") + + os_log("Initializing a ProcessBatchOfUnprocessedMessagesOperation (maxNumberOfOperations is %d)", log: log, type: .info, maxNumberOfOperations) + let op1 = ProcessBatchOfUnprocessedMessagesOperation(ownedCryptoIdentity: ownedCryptoIdentity, + queueForPostingNotifications: queueForPostingNotifications, + notificationDelegate: notificationDelegate, + processDownloadedMessageDelegate: processDownloadedMessageDelegate, + log: log) + let composedOp = CompositionOfOneContextualOperation(op1: op1, contextCreator: contextCreator, log: log, flowId: flowId) + internalQueue.addOperations([composedOp], waitUntilFinished: true) + composedOp.logReasonIfCancelled(log: log) + if composedOp.isCancelled { + os_log("The ProcessBatchOfUnprocessedMessagesOperation cancelled: %{public}@", log: log, type: .fault, composedOp.reasonForCancel?.localizedDescription ?? "No reason given") + assertionFailure(composedOp.reasonForCancel.debugDescription) + moreUnprocessedMessagesRemain = false + } else { + os_log("The ProcessBatchOfUnprocessedMessagesOperation succeeded", log: log, type: .info) + moreUnprocessedMessagesRemain = op1.moreUnprocessedMessagesRemain ?? false + if moreUnprocessedMessagesRemain { + os_log("More unprocessed messages remain", log: log, type: .info) + } } + } } - } diff --git a/Engine/ObvProtocolManager/ObvProtocolManager/Coordinators/ProtocolStarterCoordinator.swift b/Engine/ObvProtocolManager/ObvProtocolManager/Coordinators/ProtocolStarterCoordinator.swift index 85294883..1f697143 100644 --- a/Engine/ObvProtocolManager/ObvProtocolManager/Coordinators/ProtocolStarterCoordinator.swift +++ b/Engine/ObvProtocolManager/ObvProtocolManager/Coordinators/ProtocolStarterCoordinator.swift @@ -121,13 +121,15 @@ extension ProtocolStarterCoordinator { let log = OSLog(subsystem: delegateManager.logSubsystem, category: ProtocolStarterCoordinator.logCategory) guard let contextCreator = delegateManager.contextCreator else { + assertionFailure() os_log("The context creator is not set", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "The context creator is not set") } guard let channelDelegate = delegateManager.channelDelegate else { + assertionFailure() os_log("The channel delegate is not set", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "The channel delegate is not set") } let protocolInstanceUid = UID.gen(with: prng) @@ -135,8 +137,9 @@ extension ProtocolStarterCoordinator { cryptoProtocolId: .DeviceDiscoveryForContactIdentity, protocolInstanceUid: protocolInstanceUid) guard let messageToSend = DeviceDiscoveryForContactIdentityProtocol.InitialMessage(coreProtocolMessage: coreMessage, contactIdentity: contactIdentity).generateObvChannelProtocolMessageToSend(with: prng) else { + assertionFailure() os_log("Could create generic protocol message to send", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "Could create generic protocol message to send") } let prng = self.prng @@ -171,8 +174,9 @@ extension ProtocolStarterCoordinator { contactIdentityFullDisplayName: contactFullDisplayName, ownIdentityCoreDetails: ownIdentityCoreDetails) guard let initialMessageToSend = initialMessage.generateObvChannelProtocolMessageToSend(with: prng) else { + assertionFailure() os_log("Could create generic protocol message to send", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") } return initialMessageToSend @@ -192,8 +196,9 @@ extension ProtocolStarterCoordinator { contactIdentityB: identity2, contactIdentityCoreDetailsB: details2) guard let initialMessageToSend = initialMessage.generateObvChannelProtocolMessageToSend(with: prng) else { + assertionFailure() os_log("Could create generic protocol message to send", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") } return initialMessageToSend @@ -207,18 +212,21 @@ extension ProtocolStarterCoordinator { os_log("Call to startChannelCreationWithContactDeviceProtocolBetweenTheCurrentDeviceOf", log: log, type: .debug) guard let contextCreator = delegateManager.contextCreator else { + assertionFailure() os_log("The context creator is not set", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "The context creator is not set") } guard let identityDelegate = delegateManager.identityDelegate else { + assertionFailure() os_log("The identity delegate is not set", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "The identity delegate is not set") } guard let channelDelegate = delegateManager.channelDelegate else { + assertionFailure() os_log("The channel delegate is not set", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "The channel delegate is not set") } var error: Error? = nil @@ -274,8 +282,9 @@ extension ProtocolStarterCoordinator { protocolInstanceUid: protocolInstanceUid) let initialMessage = ChannelCreationWithContactDeviceProtocol.InitialMessage(coreProtocolMessage: coreMessage, contactIdentity: contactIdentity, contactDeviceUid: contactDeviceUid) guard let initialMessageToSend = initialMessage.generateObvChannelProtocolMessageToSend(with: prng) else { + assertionFailure() os_log("Could create generic protocol message to send", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") } return initialMessageToSend } @@ -286,8 +295,9 @@ extension ProtocolStarterCoordinator { let log = OSLog(subsystem: delegateManager.logSubsystem, category: ProtocolStarterCoordinator.logCategory) guard let identityDelegate = delegateManager.identityDelegate else { + assertionFailure() os_log("The identity delegate is not set", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "The identity delegate is not set") } let groupInformationWithPhoto = try identityDelegate.getGroupOwnedInformationAndPublishedPhoto(ownedIdentity: ownedIdentity, @@ -301,8 +311,9 @@ extension ProtocolStarterCoordinator { let initialMessage = GroupManagementProtocol.GroupMembersChangedTriggerMessage(coreProtocolMessage: coreMessage, groupInformation: groupInformationWithPhoto.groupInformation) guard let initialMessageToSend = initialMessage.generateObvChannelProtocolMessageToSend(with: prng) else { + assertionFailure() os_log("Could create generic protocol message to send", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") } return initialMessageToSend @@ -355,16 +366,17 @@ extension ProtocolStarterCoordinator { let log = OSLog(subsystem: delegateManager.logSubsystem, category: ProtocolStarterCoordinator.logCategory) guard let identityDelegate = delegateManager.identityDelegate else { + assertionFailure() os_log("The identity delegate is not set", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "The identity delegate is not set") } guard let groupStructure = try identityDelegate.getGroupOwnedStructure(ownedIdentity: ownedIdentity, groupUid: groupUid, within: obvContext) else { - throw NSError() + throw Self.makeError(message: "Could not get group owned structure") } guard groupStructure.groupType == .owned else { - throw NSError() + throw Self.makeError(message: "The group type is not owned") } let groupInformationWithPhoto = try identityDelegate.getGroupOwnedInformationAndPublishedPhoto(ownedIdentity: ownedIdentity, groupUid: groupUid, within: obvContext) @@ -377,8 +389,9 @@ extension ProtocolStarterCoordinator { groupInformation: groupInformationWithPhoto.groupInformation, newGroupMembers: newGroupMembers) guard let initialMessageToSend = initialMessage.generateObvChannelProtocolMessageToSend(with: prng) else { + assertionFailure() os_log("Could create generic protocol message to send", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "Could create generic protocol message to send") } return initialMessageToSend @@ -562,8 +575,9 @@ extension ProtocolStarterCoordinator { let log = OSLog(subsystem: delegateManager.logSubsystem, category: ProtocolStarterCoordinator.logCategory) guard let identityDelegate = delegateManager.identityDelegate else { + assertionFailure() os_log("The identity delegate is not set", log: log, type: .fault) - throw NSError() + throw Self.makeError(message: "The identity delegate is not set") } let groupInformationWithPhoto = try identityDelegate.getGroupJoinedInformationAndPublishedPhoto(ownedIdentity: ownedIdentity, groupUid: groupUid, groupOwner: groupOwner, within: obvContext) diff --git a/Engine/ObvProtocolManager/ObvProtocolManager/CoreData/ProtocolInstanceWaitingForContactUpgradeToOneToOne.swift b/Engine/ObvProtocolManager/ObvProtocolManager/CoreData/ProtocolInstanceWaitingForContactUpgradeToOneToOne.swift index c452b073..8a918f9b 100644 --- a/Engine/ObvProtocolManager/ObvProtocolManager/CoreData/ProtocolInstanceWaitingForContactUpgradeToOneToOne.swift +++ b/Engine/ObvProtocolManager/ObvProtocolManager/CoreData/ProtocolInstanceWaitingForContactUpgradeToOneToOne.swift @@ -130,7 +130,9 @@ extension ProtocolInstanceWaitingForContactUpgradeToOneToOne { static func deleteAllRelatedToProtocolInstance(_ protocolInstance: ProtocolInstance, delegateManager: ObvProtocolDelegateManager) throws { - guard let obvContext = protocolInstance.obvContext else { throw NSError() } + guard let obvContext = protocolInstance.obvContext else { + throw Self.makeError(message: "The protocol instance has no obvContext in deleteAllRelatedToProtocolInstance(...)") + } let request: NSFetchRequest = ProtocolInstanceWaitingForContactUpgradeToOneToOne.fetchRequest() request.predicate = Predicate.withAssociatedProtocolInstance(protocolInstance) @@ -144,7 +146,9 @@ extension ProtocolInstanceWaitingForContactUpgradeToOneToOne { static func deleteRelatedToProtocolInstance(_ protocolInstance: ProtocolInstance, contactCryptoIdentity: ObvCryptoIdentity, delegateManager: ObvProtocolDelegateManager) throws { - guard let obvContext = protocolInstance.obvContext else { throw NSError() } + guard let obvContext = protocolInstance.obvContext else { + throw Self.makeError(message: "The protocol instance has no obvContext in deleteRelatedToProtocolInstance(...)") + } let request: NSFetchRequest = ProtocolInstanceWaitingForContactUpgradeToOneToOne.fetchRequest() request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [ diff --git a/Engine/ObvProtocolManager/ObvProtocolManager/ObvProtocolManager.swift b/Engine/ObvProtocolManager/ObvProtocolManager/ObvProtocolManager.swift index 2bdb83b0..c443af9d 100644 --- a/Engine/ObvProtocolManager/ObvProtocolManager/ObvProtocolManager.swift +++ b/Engine/ObvProtocolManager/ObvProtocolManager/ObvProtocolManager.swift @@ -83,19 +83,29 @@ extension ObvProtocolManager { public func fulfill(requiredDelegate delegate: AnyObject, forDelegateType delegateType: ObvEngineDelegateType) throws { switch delegateType { case .ObvCreateContextDelegate: - guard let delegate = delegate as? ObvCreateContextDelegate else { throw NSError() } + guard let delegate = delegate as? ObvCreateContextDelegate else { + throw Self.makeError(message: "The ObvCreateContextDelegate is not set") + } delegateManager.contextCreator = delegate case .ObvChannelDelegate: - guard let delegate = delegate as? ObvChannelDelegate else { throw NSError() } + guard let delegate = delegate as? ObvChannelDelegate else { + throw Self.makeError(message: "The ObvChannelDelegate is not set") + } delegateManager.channelDelegate = delegate case .ObvIdentityDelegate: - guard let delegate = delegate as? ObvIdentityDelegate else { throw NSError() } + guard let delegate = delegate as? ObvIdentityDelegate else { + throw Self.makeError(message: "The ObvIdentityDelegate is not set") + } delegateManager.identityDelegate = delegate case .ObvNotificationDelegate: - guard let delegate = delegate as? ObvNotificationDelegate else { throw NSError() } + guard let delegate = delegate as? ObvNotificationDelegate else { + throw Self.makeError(message: "The ObvNotificationDelegate is not set") + } delegateManager.notificationDelegate = delegate case .ObvSolveChallengeDelegate: - guard let delegate = delegate as? ObvSolveChallengeDelegate else { throw NSError() } + guard let delegate = delegate as? ObvSolveChallengeDelegate else { + throw Self.makeError(message: "The ObvSolveChallengeDelegate is not set") + } delegateManager.solveChallengeDelegate = delegate default: throw Self.makeError(message: "Unexpected delegate type") diff --git a/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/GroupProtocols/GroupV1Protocols/GroupManagementProtocol/GroupManagementProtocolSteps.swift b/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/GroupProtocols/GroupV1Protocols/GroupManagementProtocol/GroupManagementProtocolSteps.swift index 8f88def2..f0b2f83c 100644 --- a/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/GroupProtocols/GroupV1Protocols/GroupManagementProtocol/GroupManagementProtocolSteps.swift +++ b/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/GroupProtocols/GroupV1Protocols/GroupManagementProtocol/GroupManagementProtocolSteps.swift @@ -190,7 +190,7 @@ extension GroupManagementProtocol { let coreMessage = getCoreMessage(for: .AllConfirmedObliviousChannelsWithOtherDevicesOfOwnedIdentity(ownedIdentity: ownedIdentity)) let concreteProtocolMessage = PropagateGroupCreationMessage(coreProtocolMessage: coreMessage, groupInformation: updatedGroupInformationWithPhoto.groupInformation, pendingGroupMembers: pendingGroupMembers) guard let messageToSend = concreteProtocolMessage.generateObvChannelProtocolMessageToSend(with: prng) else { - throw NSError() + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -497,7 +497,9 @@ extension GroupManagementProtocol { cryptoProtocolId: .GroupManagement, protocolInstanceUid: childProtocolInstanceUid) let childProtocolInitialMessage = GroupManagementProtocol.GroupMembersChangedTriggerMessage(coreProtocolMessage: coreMessage, groupInformation: groupInformationWithPhoto.groupInformation) - guard let messageToSend = childProtocolInitialMessage.generateObvChannelProtocolMessageToSend(with: localPrng) else { throw NSError() } + guard let messageToSend = childProtocolInitialMessage.generateObvChannelProtocolMessageToSend(with: localPrng) else { + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: localPrng, within: obvContext) } @@ -517,7 +519,9 @@ extension GroupManagementProtocol { let groupStructure: GroupStructure do { - guard let _groupStructure = try identityDelegate.getGroupOwnedStructure(ownedIdentity: ownedIdentity, groupUid: groupInformation.groupUid, within: obvContext) else { throw NSError() } + guard let _groupStructure = try identityDelegate.getGroupOwnedStructure(ownedIdentity: ownedIdentity, groupUid: groupInformation.groupUid, within: obvContext) else { + throw Self.makeError(message: "Could not get group owned structure") + } groupStructure = _groupStructure } catch { os_log("Could not access the group in database", log: log, type: .error) @@ -629,7 +633,9 @@ extension GroupManagementProtocol { cryptoProtocolId: .GroupManagement, protocolInstanceUid: childProtocolInstanceUid) let childProtocolInitialMessage = GroupManagementProtocol.GroupMembersChangedTriggerMessage(coreProtocolMessage: coreMessage, groupInformation: groupInformationWithPhoto.groupInformation) - guard let messageToSend = childProtocolInitialMessage.generateObvChannelProtocolMessageToSend(with: localPrng) else { throw NSError() } + guard let messageToSend = childProtocolInitialMessage.generateObvChannelProtocolMessageToSend(with: localPrng) else { + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: localPrng, within: obvContext) } @@ -897,7 +903,9 @@ extension GroupManagementProtocol { cryptoProtocolId: .GroupManagement, protocolInstanceUid: childProtocolInstanceUid) let childProtocolInitialMessage = GroupManagementProtocol.GroupMembersChangedTriggerMessage(coreProtocolMessage: coreMessage, groupInformation: groupInformationWithPhoto.groupInformation) - guard let messageToSend = childProtocolInitialMessage.generateObvChannelProtocolMessageToSend(with: localPrng) else { throw NSError() } + guard let messageToSend = childProtocolInitialMessage.generateObvChannelProtocolMessageToSend(with: localPrng) else { + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: localPrng, within: obvContext) } @@ -1369,7 +1377,7 @@ extension GroupManagementProtocol { pendingMembers: pendingGroupMembers, groupMembersVersion: groupStructure.groupMembersVersion) guard let messageToSend = concreteProtocolMessage.generateObvChannelProtocolMessageToSend(with: prng) else { - throw NSError() + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } catch { @@ -1418,7 +1426,9 @@ extension ProtocolStep { let groupStructure: GroupStructure do { - guard let _groupStructure = try identityDelegate.getGroupOwnedStructure(ownedIdentity: step.ownedIdentity, groupUid: groupInformation.groupUid, within: obvContext) else { throw NSError() } + guard let _groupStructure = try identityDelegate.getGroupOwnedStructure(ownedIdentity: step.ownedIdentity, groupUid: groupInformation.groupUid, within: obvContext) else { + throw Self.makeError(message: "Could not get group owned structure") + } groupStructure = _groupStructure } catch { os_log("Could not access the group in database", log: log, type: .error) @@ -1478,7 +1488,7 @@ extension ProtocolStep { let coreMessage = getCoreMessage(for: .AllConfirmedObliviousChannelsWithContactIdentities(contactIdentities: groupStructure.groupMembers, fromOwnedIdentity: step.ownedIdentity)) let concreteProtocolMessage = GroupManagementProtocol.NewMembersMessage(coreProtocolMessage: coreMessage, groupInformation: groupInformation, groupMembers: groupMembersWithCoreDetails, pendingMembers: groupStructure.pendingGroupMembers, groupMembersVersion: groupStructure.groupMembersVersion) guard let messageToSend = concreteProtocolMessage.generateObvChannelProtocolMessageToSend(with: step.prng) else { - throw NSError() + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") } _ = try channelDelegate.post(messageToSend, randomizedWith: step.prng, within: obvContext) } catch { diff --git a/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/KeycloakContactAdditionProtocol/KeycloakContactAdditionProtocolSteps.swift b/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/KeycloakContactAdditionProtocol/KeycloakContactAdditionProtocolSteps.swift index 0db7a80d..957510d1 100644 --- a/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/KeycloakContactAdditionProtocol/KeycloakContactAdditionProtocolSteps.swift +++ b/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/KeycloakContactAdditionProtocol/KeycloakContactAdditionProtocolSteps.swift @@ -303,7 +303,9 @@ extension KeycloakContactAdditionProtocol { else { let coreMessage = self.getCoreMessage(for: .AsymmetricChannel(to: contactIdentity, remoteDeviceUids: contactDeviceUids, fromOwnedIdentity: self.ownedIdentity)) let concreteProtocolMessage = ConfirmationMessage(coreProtocolMessage: coreMessage, accepted: false) - guard let messageToSend = concreteProtocolMessage.generateObvChannelProtocolMessageToSend(with: self.prng) else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelProtocolMessageToSend(with: self.prng) else { + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: self.prng, within: obvContext) return FinishedState() @@ -311,7 +313,9 @@ extension KeycloakContactAdditionProtocol { let coreMessage = self.getCoreMessage(for: .ServerQuery(ownedIdentity: ownedIdentity)) let concreteProtocolMessage = CheckForRevocationServerQueryMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelServerQueryMessageToSend(serverQueryType: .checkKeycloakRevocation(keycloakServerUrl: keycloakServerURL, signedContactDetails: signedContactDetails)) else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelServerQueryMessageToSend(serverQueryType: .checkKeycloakRevocation(keycloakServerUrl: keycloakServerURL, signedContactDetails: signedContactDetails)) else { + throw Self.makeError(message: "Could not generate ObvChannelServerQueryMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: self.prng, within: obvContext) return CheckingForRevocationState(contactIdentity: contactIdentity, identityCoreDetails: userCoreDetails, contactDeviceUids: contactDeviceUids, keycloakServerURL: keycloakServerURL) @@ -346,7 +350,9 @@ extension KeycloakContactAdditionProtocol { // User is revoked let coreMessage = self.getCoreMessage(for: .AsymmetricChannel(to: contactIdentity, remoteDeviceUids: contactDeviceUids, fromOwnedIdentity: self.ownedIdentity)) let concreteProtocolMessage = ConfirmationMessage(coreProtocolMessage: coreMessage, accepted: false) - guard let messageToSend = concreteProtocolMessage.generateObvChannelProtocolMessageToSend(with: self.prng) else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelProtocolMessageToSend(with: self.prng) else { + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: self.prng, within: obvContext) return FinishedState() @@ -369,7 +375,9 @@ extension KeycloakContactAdditionProtocol { let coreMessage = self.getCoreMessage(for: .AsymmetricChannel(to: contactIdentity, remoteDeviceUids: contactDeviceUids, fromOwnedIdentity: self.ownedIdentity)) let concreteProtocolMessage = ConfirmationMessage(coreProtocolMessage: coreMessage, accepted: true) - guard let messageToSend = concreteProtocolMessage.generateObvChannelProtocolMessageToSend(with: self.prng) else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelProtocolMessageToSend(with: self.prng) else { + throw Self.makeError(message: "Could not generate ObvChannelProtocolMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: self.prng, within: obvContext) return FinishedState() diff --git a/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/OneToOneContactInvitationProtocol/OneToOneContactInvitationProtocolStates.swift b/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/OneToOneContactInvitationProtocol/OneToOneContactInvitationProtocolStates.swift index d518642c..741e2c59 100644 --- a/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/OneToOneContactInvitationProtocol/OneToOneContactInvitationProtocolStates.swift +++ b/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/OneToOneContactInvitationProtocol/OneToOneContactInvitationProtocolStates.swift @@ -55,7 +55,9 @@ extension OneToOneContactInvitationProtocol { func obvEncode() -> ObvEncoded { [contactIdentity, dialogUuid].obvEncode() } init(_ encoded: ObvEncoded) throws { - guard let encodedElements = [ObvEncoded](encoded, expectedCount: 2) else { throw NSError() } + guard let encodedElements = [ObvEncoded](encoded, expectedCount: 2) else { + throw Self.makeError(message: "Could not get list of encoded elements for InvitationSentState") + } self.contactIdentity = try encodedElements[0].obvDecode() self.dialogUuid = try encodedElements[1].obvDecode() } @@ -78,7 +80,9 @@ extension OneToOneContactInvitationProtocol { func obvEncode() -> ObvEncoded { [contactIdentity, dialogUuid].obvEncode() } init(_ encoded: ObvEncoded) throws { - guard let encodedElements = [ObvEncoded](encoded, expectedCount: 2) else { throw NSError() } + guard let encodedElements = [ObvEncoded](encoded, expectedCount: 2) else { + throw Self.makeError(message: "Could not get list of encoded elements for InvitationReceivedState") + } self.contactIdentity = try encodedElements[0].obvDecode() self.dialogUuid = try encodedElements[1].obvDecode() } diff --git a/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/TrustEstablishmentProtocol/TrustEstablishmentProtocolSteps.swift b/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/TrustEstablishmentProtocol/TrustEstablishmentProtocolSteps.swift index 699b4046..53ced578 100644 --- a/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/TrustEstablishmentProtocol/TrustEstablishmentProtocolSteps.swift +++ b/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/TrustEstablishmentProtocol/TrustEstablishmentProtocolSteps.swift @@ -183,7 +183,9 @@ extension TrustEstablishmentProtocol { let contact = CryptoIdentityWithFullDisplayName(cryptoIdentity: contactIdentity, fullDisplayName: contactIdentityFullDisplayName) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: .inviteSent(contact: contact))) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -226,7 +228,9 @@ extension TrustEstablishmentProtocol { let contact = CryptoIdentityWithFullDisplayName(cryptoIdentity: contactIdentity, fullDisplayName: contactIdentityFullDisplayName) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: .inviteSent(contact: contact))) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -292,7 +296,9 @@ extension TrustEstablishmentProtocol { let dialogType = ObvChannelDialogToSendType.sasExchange(contact: contact, sasToDisplay: sasToDisplay, numberOfBadEnteredSas: 0) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogSasExchangeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -340,7 +346,9 @@ extension TrustEstablishmentProtocol { let contact = CryptoIdentityWithCoreDetails(cryptoIdentity: contactIdentity, coreDetails: contactIdentityCoreDetails) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: .acceptInvite(contact: contact))) let concreteProtocolMessage = BobDialogInvitationConfirmationMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -404,7 +412,9 @@ extension TrustEstablishmentProtocol { let contact = CryptoIdentityWithCoreDetails(cryptoIdentity: contactIdentity, coreDetails: contactIdentityCoreDetails) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: .acceptInvite(contact: contact))) let concreteProtocolMessage = BobDialogInvitationConfirmationMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -488,7 +498,9 @@ extension TrustEstablishmentProtocol { let dialogType = ObvChannelDialogToSendType.delete let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -504,7 +516,9 @@ extension TrustEstablishmentProtocol { let dialogType = ObvChannelDialogToSendType.invitationAccepted(contact: contact) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -512,7 +526,7 @@ extension TrustEstablishmentProtocol { let seedForSas: Seed do { - guard !commitment.isEmpty else { throw NSError() } + guard !commitment.isEmpty else { throw Self.makeError(message: "The commitment is empty") } seedForSas = try identityDelegate.getDeterministicSeedForOwnedIdentity(ownedIdentity, diversifiedUsing: commitment, within: obvContext) } catch { os_log("Could not compute (deterministic but diversified) seed for sas", log: log, type: .error) @@ -585,7 +599,9 @@ extension TrustEstablishmentProtocol { let dialogType = ObvChannelDialogToSendType.delete let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -601,7 +617,10 @@ extension TrustEstablishmentProtocol { let dialogType = ObvChannelDialogToSendType.invitationAccepted(contact: contact) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -609,7 +628,7 @@ extension TrustEstablishmentProtocol { let seedForSas: Seed do { - guard !commitment.isEmpty else { throw NSError() } + guard !commitment.isEmpty else { throw Self.makeError(message: "The commitment is empty") } seedForSas = try identityDelegate.getDeterministicSeedForOwnedIdentity(ownedIdentity, diversifiedUsing: commitment, within: obvContext) } catch { os_log("Could not compute (deterministic but diversified) seed for sas", log: log, type: .error) @@ -684,7 +703,9 @@ extension TrustEstablishmentProtocol { let dialogType = ObvChannelDialogToSendType.sasExchange(contact: contact, sasToDisplay: sasToDisplay, numberOfBadEnteredSas: 0) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogSasExchangeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -757,7 +778,9 @@ extension TrustEstablishmentProtocol { let dialogType = ObvChannelDialogToSendType.sasExchange(contact: contact, sasToDisplay: sasToDisplay, numberOfBadEnteredSas: newNumberOfBadEnteredSas) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogSasExchangeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -891,7 +914,9 @@ extension TrustEstablishmentProtocol { let dialogType = ObvChannelDialogToSendType.delete let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } return CancelledState() diff --git a/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/TrustEstablishmentWithSAS/TrustEstablishmentWithSASProtocolSteps.swift b/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/TrustEstablishmentWithSAS/TrustEstablishmentWithSASProtocolSteps.swift index 3f054c72..d1f79637 100644 --- a/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/TrustEstablishmentWithSAS/TrustEstablishmentWithSASProtocolSteps.swift +++ b/Engine/ObvProtocolManager/ObvProtocolManager/Protocols/TrustEstablishmentWithSAS/TrustEstablishmentWithSASProtocolSteps.swift @@ -186,7 +186,10 @@ extension TrustEstablishmentWithSASProtocol { let contact = CryptoIdentityWithFullDisplayName(cryptoIdentity: contactIdentity, fullDisplayName: contactIdentityFullDisplayName) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: .inviteSent(contact: contact))) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -229,7 +232,10 @@ extension TrustEstablishmentWithSASProtocol { let contact = CryptoIdentityWithFullDisplayName(cryptoIdentity: contactIdentity, fullDisplayName: contactIdentityFullDisplayName) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: .inviteSent(contact: contact))) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -299,7 +305,10 @@ extension TrustEstablishmentWithSASProtocol { let dialogType = ObvChannelDialogToSendType.sasExchange(contact: contact, sasToDisplay: sasToDisplay, numberOfBadEnteredSas: 0) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogSasExchangeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -369,7 +378,10 @@ extension TrustEstablishmentWithSASProtocol { let contact = CryptoIdentityWithCoreDetails(cryptoIdentity: contactIdentity, coreDetails: contactIdentityCoreDetails) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: .acceptInvite(contact: contact))) let concreteProtocolMessage = BobDialogInvitationConfirmationMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -433,7 +445,10 @@ extension TrustEstablishmentWithSASProtocol { let contact = CryptoIdentityWithCoreDetails(cryptoIdentity: contactIdentity, coreDetails: contactIdentityCoreDetails) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: .acceptInvite(contact: contact))) let concreteProtocolMessage = BobDialogInvitationConfirmationMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -517,7 +532,10 @@ extension TrustEstablishmentWithSASProtocol { let dialogType = ObvChannelDialogToSendType.delete let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -533,7 +551,10 @@ extension TrustEstablishmentWithSASProtocol { let dialogType = ObvChannelDialogToSendType.invitationAccepted(contact: contact) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -541,7 +562,7 @@ extension TrustEstablishmentWithSASProtocol { let seedBobForSas: Seed do { - guard !commitment.isEmpty else { throw NSError() } + guard !commitment.isEmpty else { throw Self.makeError(message: "The commitment is empty") } seedBobForSas = try identityDelegate.getDeterministicSeedForOwnedIdentity(ownedIdentity, diversifiedUsing: commitment, within: obvContext) } catch { os_log("Could not compute (deterministic but diversified) seed for sas", log: log, type: .error) @@ -614,7 +635,10 @@ extension TrustEstablishmentWithSASProtocol { let dialogType = ObvChannelDialogToSendType.delete let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -630,7 +654,10 @@ extension TrustEstablishmentWithSASProtocol { let dialogType = ObvChannelDialogToSendType.invitationAccepted(contact: contact) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -638,7 +665,7 @@ extension TrustEstablishmentWithSASProtocol { let seedBobForSas: Seed do { - guard !commitment.isEmpty else { throw NSError() } + guard !commitment.isEmpty else { throw Self.makeError(message: "The commitment is empty") } seedBobForSas = try identityDelegate.getDeterministicSeedForOwnedIdentity(ownedIdentity, diversifiedUsing: commitment, within: obvContext) } catch { os_log("Could not compute (deterministic but diversified) seed for sas", log: log, type: .error) @@ -717,7 +744,10 @@ extension TrustEstablishmentWithSASProtocol { let dialogType = ObvChannelDialogToSendType.sasExchange(contact: contact, sasToDisplay: sasToDisplay, numberOfBadEnteredSas: 0) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogSasExchangeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -790,7 +820,10 @@ extension TrustEstablishmentWithSASProtocol { let dialogType = ObvChannelDialogToSendType.sasExchange(contact: contact, sasToDisplay: sasToDisplay, numberOfBadEnteredSas: newNumberOfBadEnteredSas) let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogSasExchangeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } @@ -910,7 +943,10 @@ extension TrustEstablishmentWithSASProtocol { let dialogType = ObvChannelDialogToSendType.delete let coreMessage = getCoreMessage(for: .UserInterface(uuid: dialogUuid, ownedIdentity: ownedIdentity, dialogType: dialogType)) let concreteProtocolMessage = DialogInformativeMessage(coreProtocolMessage: coreMessage) - guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { throw NSError() } + guard let messageToSend = concreteProtocolMessage.generateObvChannelDialogMessageToSend() else { + assertionFailure() + throw Self.makeError(message: "Could not generate ObvChannelDialogMessageToSend") + } _ = try channelDelegate.post(messageToSend, randomizedWith: prng, within: obvContext) } return CancelledState() diff --git a/Engine/ObvServerInterface/ObvServerInterface/ObvS3Method/ObvS3UploadMethod.swift b/Engine/ObvServerInterface/ObvServerInterface/ObvS3Method/ObvS3UploadMethod.swift index cdecb9c4..c6c520a2 100644 --- a/Engine/ObvServerInterface/ObvServerInterface/ObvS3Method/ObvS3UploadMethod.swift +++ b/Engine/ObvServerInterface/ObvServerInterface/ObvS3Method/ObvS3UploadMethod.swift @@ -29,11 +29,15 @@ public protocol ObvS3UploadMethod: ObvS3Method { public extension ObvS3UploadMethod { + static func makeError(message: String) -> Error { + NSError(domain: String(describing: self), code: 0, userInfo: [NSLocalizedFailureReasonErrorKey: message]) + } + func uploadTask(within session: URLSession) throws -> URLSessionUploadTask { let request = try getURLRequest(httpMethod: "PUT", dataToSend: nil) guard FileManager.default.fileExists(atPath: fileURL.path) else { assertionFailure() - throw NSError() + throw Self.makeError(message: "Cannot perform upload task as the file could not be found at the indicated URL") } let task = session.uploadTask(with: request, fromFile: fileURL) task.countOfBytesClientExpectsToReceive = Int64(countOfBytesClientExpectsToReceive) diff --git a/Engine/ObvServerInterface/ObvServerInterface/ObvServerMethod/Methods/Send/ObvServerUploadPrivateURLsForAttachmentChunksMethod.swift b/Engine/ObvServerInterface/ObvServerInterface/ObvServerMethod/Methods/Send/ObvServerUploadPrivateURLsForAttachmentChunksMethod.swift index e054d26d..91a1453d 100644 --- a/Engine/ObvServerInterface/ObvServerInterface/ObvServerMethod/Methods/Send/ObvServerUploadPrivateURLsForAttachmentChunksMethod.swift +++ b/Engine/ObvServerInterface/ObvServerInterface/ObvServerMethod/Methods/Send/ObvServerUploadPrivateURLsForAttachmentChunksMethod.swift @@ -28,7 +28,10 @@ import OlvidUtils public final class ObvServerUploadPrivateURLsForAttachmentChunksMethod: ObvServerDataMethod { static let log = OSLog(subsystem: "io.olvid.server.interface.ObvServerUploadPrivateURLsForAttachmentChunksMethod", category: "ObvServerInterface") - + private static func makeError(message: String) -> Error { + NSError(domain: "ObvServerUploadPrivateURLsForAttachmentChunksMethod", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey: message]) + } + public let pathComponent = "/uploadAttachment" public let ownedIdentity: ObvCryptoIdentity public let serverURL: URL @@ -86,8 +89,14 @@ public final class ObvServerUploadPrivateURLsForAttachmentChunksMethod: ObvServe let chunkUploadPrivateUrls: [URL] do { chunkUploadPrivateUrls = try encodedURLs.compactMap { - guard let urlAsString = String($0) else { throw NSError() } - guard !urlAsString.isEmpty else { throw NSError() } + guard let urlAsString = String($0) else { + assertionFailure() + throw Self.makeError(message: "Could not turn encoded URL into a string") + } + guard !urlAsString.isEmpty else { + assertionFailure() + throw Self.makeError(message: "The string obtained from the URL is empty") + } return URL(string: urlAsString) } } catch { diff --git a/iOSClient/ObvMessenger/ObvMessenger.xcodeproj/project.pbxproj b/iOSClient/ObvMessenger/ObvMessenger.xcodeproj/project.pbxproj index 238be731..b17c1f99 100644 --- a/iOSClient/ObvMessenger/ObvMessenger.xcodeproj/project.pbxproj +++ b/iOSClient/ObvMessenger/ObvMessenger.xcodeproj/project.pbxproj @@ -2748,6 +2748,8 @@ C41143EA20AC7F43005DFB7A /* AppTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTheme.swift; sourceTree = ""; }; C41143ED20AC815C005DFB7A /* ObvButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObvButton.swift; sourceTree = ""; }; C4122B3820AD853100FEF4B6 /* InvitationsCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitationsCollectionViewController.swift; sourceTree = ""; }; + C412591A2970CAE900B4C3E4 /* ObvMessenger 61.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "ObvMessenger 61.xcdatamodel"; sourceTree = ""; }; + C412591C2970CB3B00B4C3E4 /* MigrationAppDatabase_v60_to_v61.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = MigrationAppDatabase_v60_to_v61.md; sourceTree = ""; }; C412614C21B6D71A00339A06 /* ObvMessengerMappingModel_v4_to_v5.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = ObvMessengerMappingModel_v4_to_v5.xcmappingmodel; sourceTree = ""; }; C412D7622531F89400311B6A /* AutorisationRequesterHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutorisationRequesterHostingController.swift; sourceTree = ""; }; C412F0772794DE2C00EEF71E /* MigrationAppDatabase_v39_to_v40.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = MigrationAppDatabase_v39_to_v40.md; sourceTree = ""; }; @@ -4556,6 +4558,14 @@ path = InvitationsCollection; sourceTree = ""; }; + C412591B2970CB1800B4C3E4 /* v60_to_v61 */ = { + isa = PBXGroup; + children = ( + C412591C2970CB3B00B4C3E4 /* MigrationAppDatabase_v60_to_v61.md */, + ); + path = v60_to_v61; + sourceTree = ""; + }; C412F0752794DE0D00EEF71E /* v39_to_v40 */ = { isa = PBXGroup; children = ( @@ -6251,6 +6261,7 @@ C4A27B142191ADA700E04F1E /* Migration */ = { isa = PBXGroup; children = ( + C412591B2970CB1800B4C3E4 /* v60_to_v61 */, C4D41B6A29507CD2009FCAF7 /* v59_to_v60 */, C4406164294FE5BA006FBFDD /* v58_to_v59 */, C42353672926967600C5D0E1 /* v57_to_v58 */, @@ -9726,7 +9737,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 611; + CURRENT_PROJECT_VERSION = 621; DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -9753,7 +9764,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 611; + CURRENT_PROJECT_VERSION = 621; DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -9779,7 +9790,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 611; + CURRENT_PROJECT_VERSION = 621; DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = ObvMessengerIntentsExtension/Info.plist; @@ -9791,7 +9802,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.3; + MARKETING_VERSION = 0.12.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "io.olvid.messenger-debug.ObvMessengerIntentsExtension"; @@ -9811,7 +9822,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 611; + CURRENT_PROJECT_VERSION = 621; DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = ObvMessengerIntentsExtension/Info.plist; @@ -9823,7 +9834,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.3; + MARKETING_VERSION = 0.12.4; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = io.olvid.messenger.ObvMessengerIntentsExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -9841,7 +9852,7 @@ CODE_SIGN_ENTITLEMENTS = ObvMessengerNotificationServiceExtension/ObvMessengerNotificationServiceExtensionDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 611; + CURRENT_PROJECT_VERSION = 621; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; @@ -9852,7 +9863,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.3; + MARKETING_VERSION = 0.12.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "$(OBV_PRODUCT_BUNDLE_IDENTIFIER_FOR_NOTIFICATION_SERVICE_EXTENSION)"; @@ -9873,7 +9884,7 @@ CODE_SIGN_ENTITLEMENTS = ObvMessengerNotificationServiceExtension/ObvMessengerNotificationServiceExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 611; + CURRENT_PROJECT_VERSION = 621; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; @@ -9884,7 +9895,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.3; + MARKETING_VERSION = 0.12.4; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "$(OBV_PRODUCT_BUNDLE_IDENTIFIER_FOR_NOTIFICATION_SERVICE_EXTENSION)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -10036,7 +10047,7 @@ CODE_SIGN_ENTITLEMENTS = ObvMessenger/ObvMessengerDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 611; + CURRENT_PROJECT_VERSION = 621; DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -10051,7 +10062,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.12.3; + MARKETING_VERSION = 0.12.4; OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=500 -Xfrontend -warn-long-expression-type-checking=1500"; PRODUCT_BUNDLE_IDENTIFIER = "$(OBV_PRODUCT_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -10074,7 +10085,7 @@ CODE_SIGN_ENTITLEMENTS = ObvMessenger/ObvMessenger.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 611; + CURRENT_PROJECT_VERSION = 621; DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; EXCLUDED_SOURCE_FILE_NAMES = ""; @@ -10090,7 +10101,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.12.3; + MARKETING_VERSION = 0.12.4; PRODUCT_BUNDLE_IDENTIFIER = "$(OBV_PRODUCT_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -10107,7 +10118,7 @@ CODE_SIGN_ENTITLEMENTS = ObvMessengerShareExtension/ObvMessengerShareExtensionDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 611; + CURRENT_PROJECT_VERSION = 621; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; @@ -10118,7 +10129,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.3; + MARKETING_VERSION = 0.12.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "$(OBV_PRODUCT_BUNDLE_IDENTIFIER_FOR_SHARE_EXTENSION)"; @@ -10139,7 +10150,7 @@ CODE_SIGN_ENTITLEMENTS = ObvMessengerShareExtension/ObvMessengerShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 611; + CURRENT_PROJECT_VERSION = 621; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; @@ -10150,7 +10161,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.12.3; + MARKETING_VERSION = 0.12.4; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "$(OBV_PRODUCT_BUNDLE_IDENTIFIER_FOR_SHARE_EXTENSION)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -10258,6 +10269,7 @@ C4F533A0209C4AB500F5D2BB /* ObvMessenger.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + C412591A2970CAE900B4C3E4 /* ObvMessenger 61.xcdatamodel */, C4BD99B32936B37A002EC253 /* ObvMessenger 60.xcdatamodel */, C4AE3F2F292E156000715714 /* ObvMessenger 59.xcdatamodel */, C423536229266AB500C5D0E1 /* ObvMessenger 58.xcdatamodel */, @@ -10319,7 +10331,7 @@ C4A27B1121919F6100E04F1E /* ObvMessenger 2.xcdatamodel */, C4F533A1209C4AB500F5D2BB /* ObvMessenger.xcdatamodel */, ); - currentVersion = C4BD99B32936B37A002EC253 /* ObvMessenger 60.xcdatamodel */; + currentVersion = C412591A2970CAE900B4C3E4 /* ObvMessenger 61.xcdatamodel */; path = ObvMessenger.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/iOSClient/ObvMessenger/ObvMessenger/Coordinators/PersistedDiscussionsUpdatesCoordinator/PersistedDiscussionsUpdatesCoordinator.swift b/iOSClient/ObvMessenger/ObvMessenger/Coordinators/PersistedDiscussionsUpdatesCoordinator/PersistedDiscussionsUpdatesCoordinator.swift index d34d930b..d5d44b07 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/Coordinators/PersistedDiscussionsUpdatesCoordinator/PersistedDiscussionsUpdatesCoordinator.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/Coordinators/PersistedDiscussionsUpdatesCoordinator/PersistedDiscussionsUpdatesCoordinator.swift @@ -2416,12 +2416,10 @@ extension PersistedDiscussionsUpdatesCoordinator: ScreenCaptureDetectorDelegate func screenCaptureOfSensitiveMessagesWasDetected(discussionPermanentID: ObvManagedObjectPermanentID) async { - debugPrint("🔋 Screen capture of a discussion was detected") processDectection(discussionPermanentID: discussionPermanentID) } func screenshotOfSensitiveMessagesWasDetected(discussionPermanentID: ObvManagedObjectPermanentID) async { - debugPrint("🔋 Screenshot of a discussion was detected") processDectection(discussionPermanentID: discussionPermanentID) } diff --git a/iOSClient/ObvMessenger/ObvMessenger/CoreData/DataMigrationManagerForObvMessenger.swift b/iOSClient/ObvMessenger/ObvMessenger/CoreData/DataMigrationManagerForObvMessenger.swift index 94dd742f..379e8cd0 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/CoreData/DataMigrationManagerForObvMessenger.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/CoreData/DataMigrationManagerForObvMessenger.swift @@ -90,14 +90,22 @@ final class DataMigrationManagerForObvMessenger: DataMigrationManager Bool { + guard let otherRawModelVersion else { return true } + guard let otherModelVersion = ObvMessengerModelVersion(rawValue: otherRawModelVersion) else { + assertionFailure() + throw Self.makeError(message: "Could not parse other raw model version") + } + guard let modelVersion = ObvMessengerModelVersion(rawValue: rawModelVersion) else { + assertionFailure() + throw Self.makeError(message: "Could not parse raw model version") + } + guard let otherModelVersionAsInt = otherModelVersion.intValue else { + assertionFailure() + throw Self.makeError(message: "Could not determine int value from other model version") + } + guard let modelVersionAsInt = modelVersion.intValue else { + assertionFailure() + throw Self.makeError(message: "Could not determine int value from model version") + } + return modelVersionAsInt > otherModelVersionAsInt + } override func getNextManagedObjectModelVersion(from sourceModel: NSManagedObjectModel) throws -> (destinationModel: NSManagedObjectModel, migrationType: DataMigrationManager.MigrationType) { @@ -201,7 +231,8 @@ final class DataMigrationManagerForObvMessenger: DataMigrationManager [PersistedObvOwnedIdentity] { let request: NSFetchRequest = PersistedObvOwnedIdentity.fetchRequest() request.predicate = Predicate.isHidden(false) + request.sortDescriptors = [ + NSSortDescriptor(key: Predicate.Key.customDisplayName.rawValue, ascending: true), + NSSortDescriptor(key: Predicate.Key.fullDisplayName.rawValue, ascending: true), + ] return try context.fetch(request) } diff --git a/iOSClient/ObvMessenger/ObvMessenger/CoreData/Migration/v60_to_v61/MigrationAppDatabase_v60_to_v61.md b/iOSClient/ObvMessenger/ObvMessenger/CoreData/Migration/v60_to_v61/MigrationAppDatabase_v60_to_v61.md new file mode 100644 index 00000000..5b96c9fc --- /dev/null +++ b/iOSClient/ObvMessenger/ObvMessenger/CoreData/Migration/v60_to_v61/MigrationAppDatabase_v60_to_v61.md @@ -0,0 +1,12 @@ +# App database migration from v60 to v61 + +## PersistedMessageReaction - Modified entity + +- ++ + +Makes the rawEmoji attribute optional. Does not prevent a lightweight migration. + +## Conclusion + +A lightweight migration is sufficient. diff --git a/iOSClient/ObvMessenger/ObvMessenger/CoreData/ObvMessenger.xcdatamodeld/.xccurrentversion b/iOSClient/ObvMessenger/ObvMessenger/CoreData/ObvMessenger.xcdatamodeld/.xccurrentversion index 6b062205..9695ac77 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/CoreData/ObvMessenger.xcdatamodeld/.xccurrentversion +++ b/iOSClient/ObvMessenger/ObvMessenger/CoreData/ObvMessenger.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - ObvMessenger 60.xcdatamodel + ObvMessenger 61.xcdatamodel diff --git a/iOSClient/ObvMessenger/ObvMessenger/CoreData/ObvMessenger.xcdatamodeld/ObvMessenger 61.xcdatamodel/contents b/iOSClient/ObvMessenger/ObvMessenger/CoreData/ObvMessenger.xcdatamodeld/ObvMessenger 61.xcdatamodel/contents new file mode 100644 index 00000000..411813be --- /dev/null +++ b/iOSClient/ObvMessenger/ObvMessenger/CoreData/ObvMessenger.xcdatamodeld/ObvMessenger 61.xcdatamodel/contents @@ -0,0 +1,472 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/iOSClient/ObvMessenger/ObvMessenger/CoreData/PersistedMessage/PersistedMessage.swift b/iOSClient/ObvMessenger/ObvMessenger/CoreData/PersistedMessage/PersistedMessage.swift index 37d083de..1704baad 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/CoreData/PersistedMessage/PersistedMessage.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/CoreData/PersistedMessage/PersistedMessage.swift @@ -616,10 +616,8 @@ extension PersistedMessage { if let contactReaction = reactionFromContact(with: contact.cryptoId) { try contactReaction.updateEmoji(with: emoji, at: reactionTimestamp) - } else if let emoji = emoji { - _ = try PersistedMessageReactionReceived(emoji: emoji, timestamp: reactionTimestamp, message: self, contact: contact) } else { - // The new emoji is nil (meaning we should remove a previous reaction) and no previous reaction can be found. There is nothing to do. + _ = try PersistedMessageReactionReceived(emoji: emoji, timestamp: reactionTimestamp, message: self, contact: contact) } } diff --git a/iOSClient/ObvMessenger/ObvMessenger/CoreData/PersistedMessage/PersistedMessageReaction.swift b/iOSClient/ObvMessenger/ObvMessenger/CoreData/PersistedMessage/PersistedMessageReaction.swift index 1e2c8c6b..e5ac8a2f 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/CoreData/PersistedMessage/PersistedMessageReaction.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/CoreData/PersistedMessage/PersistedMessageReaction.swift @@ -32,7 +32,7 @@ class PersistedMessageReaction: NSManagedObject { // MARK: Attributes - @NSManaged private var rawEmoji: String + @NSManaged private var rawEmoji: String? @NSManaged private(set) var timestamp: Date // MARK: Relationships @@ -41,13 +41,13 @@ class PersistedMessageReaction: NSManagedObject { // MARK: Other variables - var emoji: String { + var emoji: String? { return self.rawEmoji } // MARK: - Initializer - fileprivate convenience init(emoji: String, timestamp: Date, message: PersistedMessage, forEntityName entityName: String) throws { + fileprivate convenience init(emoji: String?, timestamp: Date, message: PersistedMessage, forEntityName entityName: String) throws { guard let context = message.managedObjectContext else { throw PersistedMessageReaction.makeError(message: "Could not find context in message") } let entityDescription = NSEntityDescription.entity(forEntityName: entityName, in: context)! @@ -60,21 +60,18 @@ class PersistedMessageReaction: NSManagedObject { func updateEmoji(with newEmoji: String?, at newTimestamp: Date) throws { guard self.timestamp < newTimestamp else { return } - if let newEmoji = newEmoji { - try self.setEmoji(with: newEmoji, at: newTimestamp) - } else { - try self.delete() - } + try self.setEmoji(with: newEmoji, at: newTimestamp) } - private func setEmoji(with newEmoji: String, at reactionTimestamp: Date) throws { - guard newEmoji.count == 1 else { throw PersistedMessageReaction.makeError(message: "Invalid emoji: \(newEmoji)") } + private func setEmoji(with newEmoji: String?, at reactionTimestamp: Date) throws { + if let newEmoji { + guard newEmoji.count == 1 else { throw PersistedMessageReaction.makeError(message: "Invalid emoji: \(newEmoji)") } + } self.rawEmoji = newEmoji self.timestamp = reactionTimestamp } - func delete() throws { guard let context = self.managedObjectContext else { throw PersistedMessageReaction.makeError(message: "Cannot find context") } context.delete(self) @@ -137,7 +134,7 @@ final class PersistedMessageReactionReceived: PersistedMessageReaction { // MARK: - Initializer - convenience init(emoji: String, timestamp: Date, message: PersistedMessage, contact: PersistedObvContactIdentity) throws { + convenience init(emoji: String?, timestamp: Date, message: PersistedMessage, contact: PersistedObvContactIdentity) throws { guard message.managedObjectContext == contact.managedObjectContext else { throw PersistedMessageReactionReceived.makeError(message: "Incoherent contexts") } try self.init(emoji: emoji, timestamp: timestamp, message: message, forEntityName: Self.entityName) self.contact = contact diff --git a/iOSClient/ObvMessenger/ObvMessenger/Info.plist b/iOSClient/ObvMessenger/ObvMessenger/Info.plist index d6b18b24..7634974c 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/Info.plist +++ b/iOSClient/ObvMessenger/ObvMessenger/Info.plist @@ -73,7 +73,7 @@ CFBundleVersion - 611 + 621 HARDCODED_API_KEY $(HARDCODED_API_KEY) ITSAppUsesNonExemptEncryption diff --git a/iOSClient/ObvMessenger/ObvMessenger/Invitation Flow/SubViews/IdentityCardContentView.swift b/iOSClient/ObvMessenger/ObvMessenger/Invitation Flow/SubViews/IdentityCardContentView.swift index 423364ba..1cc62dd2 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/Invitation Flow/SubViews/IdentityCardContentView.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/Invitation Flow/SubViews/IdentityCardContentView.swift @@ -255,6 +255,7 @@ class SingleIdentity: Identifiable, Hashable, ObservableObject { guard Thread.isMainThread else { return } guard let context = notification.object as? NSManagedObjectContext else { assertionFailure(); return } guard context == ObvStack.shared.viewContext else { return } + guard self?.ownedIdentity?.managedObjectContext == context else { return } guard let ownedIdentity = self?.ownedIdentity else { assertionFailure(); return } self?.setPublishedVariables(with: ownedIdentity) }) diff --git a/iOSClient/ObvMessenger/ObvMessenger/Main/Contacts/SingleOwnedIdentity/SingleOwnedIdentityFlowViewController.swift b/iOSClient/ObvMessenger/ObvMessenger/Main/Contacts/SingleOwnedIdentity/SingleOwnedIdentityFlowViewController.swift index c8da3241..88f406bd 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/Main/Contacts/SingleOwnedIdentity/SingleOwnedIdentityFlowViewController.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/Main/Contacts/SingleOwnedIdentity/SingleOwnedIdentityFlowViewController.swift @@ -23,6 +23,7 @@ import ObvTypes import ObvEngine import StoreKit import os.log +import CoreData protocol SingleOwnedIdentityFlowViewControllerDelegate: AnyObject { @@ -452,6 +453,13 @@ final class SingleOwnedIdentityFlowViewController: UIHostingController [ReactionAndCount] { guard let reactions = reactions else { return [] } - var reactionsCount = [String: Int]() - for reaction in reactions { - let count = reactionsCount[reaction.emoji] ?? 0 - reactionsCount[reaction.emoji] = count + 1 + let emojis = reactions.compactMap({ $0.emoji }) + let emojisCount: [String: Int] = emojis.reduce(into: [:]) { counts, emoji in + counts[emoji, default: 0] += 1 } - var result = [ReactionAndCount]() - for (emoji, count) in reactionsCount { - result += [ReactionAndCount(emoji: emoji, count: count)] - } - result.sort() - return result + let reactionAndCount = emojisCount.map({ ReactionAndCount(emoji: $0.key, count: $0.value) }).sorted() + return reactionAndCount } } diff --git a/iOSClient/ObvMessenger/ObvMessenger/Managers/UserNotificationManager/UserNotificationsBadgesManager.swift b/iOSClient/ObvMessenger/ObvMessenger/Managers/UserNotificationManager/UserNotificationsBadgesManager.swift index c296e981..6c2481a3 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/Managers/UserNotificationManager/UserNotificationsBadgesManager.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/Managers/UserNotificationManager/UserNotificationsBadgesManager.swift @@ -70,7 +70,7 @@ actor UserNotificationsBadgesManager { } - private func recomputeAllBadges(completion: (Bool) -> Void) { + private func recomputeAllBadges(completion: @escaping (Bool) -> Void) { guard let userDefaults else { completion(false); return } if let currentOwnedCryptoId = self.currentOwnedCryptoId { let refreshBadgeForNewMessagesOperation = RefreshBadgeForNewMessagesOperation(ownedCryptoId: currentOwnedCryptoId, userDefaults: userDefaults, log: Self.log) @@ -80,8 +80,9 @@ actor UserNotificationsBadgesManager { } let refreshAppBadgeOperation = RefreshAppBadgeOperation(userDefaults: userDefaults, log: Self.log) queueForBadgesOperations.addOperation(refreshAppBadgeOperation) - queueForBadgesOperations.waitUntilAllOperationsAreFinished() - completion(true) + refreshAppBadgeOperation.completionBlock = { + completion(true) + } } } diff --git a/iOSClient/ObvMessenger/ObvMessenger/Managers/UserNotificationManager/UserNotificationsManager.swift b/iOSClient/ObvMessenger/ObvMessenger/Managers/UserNotificationManager/UserNotificationsManager.swift index e6ffa7e9..8b5f593f 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/Managers/UserNotificationManager/UserNotificationsManager.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/Managers/UserNotificationManager/UserNotificationsManager.swift @@ -327,20 +327,26 @@ extension UserNotificationsManager { /// When a received reaction message is deleted (for whatever reason), we remove any existing notification related to this reaction. private func observePersistedMessageReactionReceivedWasDeletedNotifications() { - observationTokens.append(ObvMessengerCoreDataNotification.observePersistedMessageReactionReceivedWasDeletedOnSentMessage { (sentMessagePermanentID, contactPermanentID) in - let notificationCenter = UNUserNotificationCenter.current() - let notificationId = ObvUserNotificationIdentifier.newReaction(messagePermanentID: sentMessagePermanentID, contactPermanentId: contactPermanentID) + observationTokens.append(ObvMessengerCoreDataNotification.observePersistedMessageReactionReceivedWasDeletedOnSentMessage { [weak self] (sentMessagePermanentID, contactPermanentID) in + self?.deleteNotificationsReaction(sentMessagePermanentID: sentMessagePermanentID, + contactPermanentID: contactPermanentID) + }) + } - // Remove the notification if it was added by the app - ObvDisplayableLogs.shared.log("📣 Removing a user notification (added by the app) as its corresponding PersistedMessageReaction was deleted") - UserNotificationsScheduler.removeAllNotificationWithIdentifier(notificationId, notificationCenter: notificationCenter) + private func deleteNotificationsReaction(sentMessagePermanentID: ObvManagedObjectPermanentID, contactPermanentID: ObvManagedObjectPermanentID) { + let notificationCenter = UNUserNotificationCenter.current() + let notificationId = ObvUserNotificationIdentifier.newReaction(messagePermanentID: sentMessagePermanentID, contactPermanentId: contactPermanentID) + + // Remove the notification if it was added by the app + ObvDisplayableLogs.shared.log("📣 Removing a user notification (added by the app) as its corresponding PersistedMessageReaction was deleted") + UserNotificationsScheduler.removeAllNotificationWithIdentifier(notificationId, notificationCenter: notificationCenter) + + // Remove the notification if it was added by the extension + Task { + ObvDisplayableLogs.shared.log("📣 Removing a user notification (added by the extension) as its corresponding PersistedMessageReaction was deleted") + await UserNotificationsScheduler.removeReactionNotificationsAddedByExtension(with: notificationId, notificationCenter: notificationCenter) + } - // Remove the notification if it was added by the extension - Task { - ObvDisplayableLogs.shared.log("📣 Removing a user notification (added by the extension) as its corresponding PersistedMessageReaction was deleted") - await UserNotificationsScheduler.removeReactionNotificationsAddedByExtension(with: notificationId, notificationCenter: notificationCenter) - } - }) } @@ -358,41 +364,44 @@ extension UserNotificationsManager { guard let message = reactionReceived.message as? PersistedMessageSent else { return } guard let contact = reactionReceived.contact else { return } - do { - let infos = UserNotificationCreator.ReactionNotificationInfos( - messageSent: try message.toStruct(), - contact: try contact.toStruct(), - urlForStoringPNGThumbnail: nil) - let (notificationId, notificationContent) = UserNotificationCreator.createReactionNotification(infos: infos, emoji: reactionReceived.emoji, reactionTimestamp: reactionReceived.timestamp) - - let notificationCenter = UNUserNotificationCenter.current() - let reactionsTimestamps = UserNotificationsScheduler.getAllReactionsTimestampAddedByExtension(with: notificationId, notificationCenter: notificationCenter) - let discussion = message.discussion - - if reactionsTimestamps.count == 1, - let timestamp = reactionsTimestamps.first, - timestamp >= reactionReceived.timestamp { + if let emoji = reactionReceived.emoji { + do { + let infos = UserNotificationCreator.ReactionNotificationInfos( + messageSent: try message.toStruct(), + contact: try contact.toStruct(), + urlForStoringPNGThumbnail: nil) + let (notificationId, notificationContent) = UserNotificationCreator.createReactionNotification(infos: infos, emoji: emoji, reactionTimestamp: reactionReceived.timestamp) - // If there is only one notifications in the center that is more recent that the given one, we let it. - return - } else { - // We remove all the notification that comes from the extension. - Task { - ObvDisplayableLogs.shared.log("📣 Removing a user notification (added by the extension) as its corresponding PersistedMessageReaction was inserted or deleted") - await UserNotificationsScheduler.removeReactionNotificationsAddedByExtension(with: notificationId, notificationCenter: notificationCenter) + let notificationCenter = UNUserNotificationCenter.current() + let reactionsTimestamps = UserNotificationsScheduler.getAllReactionsTimestampAddedByExtension(with: notificationId, notificationCenter: notificationCenter) + let discussion = message.discussion + + if reactionsTimestamps.count == 1, + let timestamp = reactionsTimestamps.first, + timestamp >= reactionReceived.timestamp { + // There is only one notification in the notification center and it is more recent than the received one. We leave the existing notification as is. + return + } else { + // We remove all the notifications that come from the extension. + Task { + ObvDisplayableLogs.shared.log("📣 Removing a user notification (added by the extension) as its corresponding PersistedMessageReaction was inserted or deleted") + await UserNotificationsScheduler.removeReactionNotificationsAddedByExtension(with: notificationId, notificationCenter: notificationCenter) + } + // And replace them with a notification that is not necessary the most recent (in the case where multiple reaction update messages have been received) and replace by a single notification with notificationID as request identifier. + UserNotificationsScheduler.filteredScheduleNotification( + discussionKind: try discussion.toStruct(), + notificationId: notificationId, + notificationContent: notificationContent, + notificationCenter: notificationCenter) } - // And replace them with a notification that is not nececarry the more recent (in the case that multiple reaction update messages have been received) and replace by a single notification with notificationID as request identifier. - UserNotificationsScheduler.filteredScheduleNotification( - discussionKind: try discussion.toStruct(), - notificationId: notificationId, - notificationContent: notificationContent, - notificationCenter: notificationCenter) + } catch { + os_log("Could not notifiy: %{public}@", log: log, type: .fault, error.localizedDescription) + return } - } catch { - os_log("Could not notifiy: %{public}@", log: log, type: .fault, error.localizedDescription) - return + } else { + _self.deleteNotificationsReaction(sentMessagePermanentID: message.objectPermanentID, + contactPermanentID: contact.objectPermanentID) } - } }) } diff --git a/iOSClient/ObvMessenger/ObvMessenger/UIElements/NewCircledInitialsView.swift b/iOSClient/ObvMessenger/ObvMessenger/UIElements/NewCircledInitialsView.swift index 5f1f3e90..521e4250 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/UIElements/NewCircledInitialsView.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/UIElements/NewCircledInitialsView.swift @@ -33,7 +33,7 @@ final class NewCircledInitialsView: UIView { private let redShieldView = UIImageView() private let greenShieldView = UIImageView() - private var currentConfiguration: CircledInitialsConfiguration? + private(set) var currentConfiguration: CircledInitialsConfiguration? func configureWith(_ configuration: CircledInitialsConfiguration) { guard self.currentConfiguration != configuration else { return } diff --git a/iOSClient/ObvMessenger/ObvMessenger/UIElements/StandardViewControllerSubclasses/ShowOwnedIdentityButtonUIViewController.swift b/iOSClient/ObvMessenger/ObvMessenger/UIElements/StandardViewControllerSubclasses/ShowOwnedIdentityButtonUIViewController.swift index 2d8e0583..ffaf82f7 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/UIElements/StandardViewControllerSubclasses/ShowOwnedIdentityButtonUIViewController.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/UIElements/StandardViewControllerSubclasses/ShowOwnedIdentityButtonUIViewController.swift @@ -30,28 +30,32 @@ class ShowOwnedIdentityButtonUIViewController: UIViewController, OwnedIdentityCh private(set) var currentOwnedCryptoId: ObvCryptoId let log: OSLog private let titleLabel = UILabel() - private var profilePictureView: NewCircledInitialsView? private var observationTokens = [NSObjectProtocol]() private static func makeError(message: String) -> Error { NSError(domain: "ShowOwnedIdentityButtonUIViewController", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey: message]) } private var profilePictureBarButtonItem: ProfilePictureBarButtonItem? private var viewDidLoadWasCalled = false private var barButtonItemToShowInsteadOfProfilePicture: UIBarButtonItem? + init(ownedCryptoId: ObvCryptoId, logCategory: String, barButtonItemToShowInsteadOfProfilePicture: UIBarButtonItem? = nil) { self.currentOwnedCryptoId = ownedCryptoId self.log = OSLog(subsystem: ObvMessengerConstants.logSubsystem, category: logCategory) self.barButtonItemToShowInsteadOfProfilePicture = barButtonItemToShowInsteadOfProfilePicture super.init(nibName: nil, bundle: nil) } + + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + func setTitle(_ title: String?) { self.titleLabel.text = title self.navigationItem.title = title } + override func viewDidLoad() { super.viewDidLoad() viewDidLoadWasCalled = true @@ -72,6 +76,7 @@ class ShowOwnedIdentityButtonUIViewController: UIViewController, OwnedIdentityCh let profilePictureBarButtonItem = ProfilePictureBarButtonItem.makeWithInitialConfiguration(.icon(.person)) profilePictureBarButtonItem.addTarget(self, action: #selector(ownedCircledInitialsBarButtonItemWasTapped), for: .touchUpInside) profilePictureBarButtonItem.setUILongPressGestureRecognizer(target: self, action: #selector(ownedCircledInitialsBarButtonItemWasLongPressed)) + profilePictureBarButtonItem.setUISwipeGestureRecognizer(target: self, action: #selector(ownedCircledInitialsBarButtonItemWasSwiped)) self.navigationItem.leftBarButtonItem = profilePictureBarButtonItem observeChangesOfOwnedCircledInitialsConfiguration() if let ownedIdentity = try? PersistedObvOwnedIdentity.get(cryptoId: currentOwnedCryptoId, within: ObvStack.shared.viewContext) { @@ -83,7 +88,10 @@ class ShowOwnedIdentityButtonUIViewController: UIViewController, OwnedIdentityCh } continuouslyUpdateTheRedDotOnTheProfilePictureView() } + + // MARK: - Switching current owned identity + @MainActor func switchCurrentOwnedCryptoId(to newOwnedCryptoId: ObvCryptoId) async { self.currentOwnedCryptoId = newOwnedCryptoId @@ -100,7 +108,10 @@ class ShowOwnedIdentityButtonUIViewController: UIViewController, OwnedIdentityCh profilePictureBarButtonItem.configureRedDot(isHidden: true) } } + + // MARK: - Updating the profile picture view + private func observeChangesOfOwnedCircledInitialsConfiguration() { assert(Thread.isMainThread) observationTokens.append(ObvMessengerCoreDataNotification.observeOwnedCircledInitialsConfigurationDidChange { [weak self] _, ownedCryptoId, newOwnedCircledInitialsConfiguration in @@ -111,6 +122,8 @@ class ShowOwnedIdentityButtonUIViewController: UIViewController, OwnedIdentityCh } }) } + + private func continuouslyUpdateTheRedDotOnTheProfilePictureView() { observationTokens.append(contentsOf: [ ObvMessengerCoreDataNotification.observeNumberOfNewMessagesChangedForOwnedIdentity { [weak self] concernedOwnedIdentity, _ in @@ -125,6 +138,8 @@ class ShowOwnedIdentityButtonUIViewController: UIViewController, OwnedIdentityCh // Do it once now updateTheRedDotOnTheProfilePictureView() } + + private func updateTheRedDotOnTheProfilePictureView() { let currentOwnedCryptoId = self.currentOwnedCryptoId ObvStack.shared.performBackgroundTask { [weak self] context in @@ -140,7 +155,10 @@ class ShowOwnedIdentityButtonUIViewController: UIViewController, OwnedIdentityCh } } } + + // MARK: - Handling interaction with profile picture + @objc func ownedCircledInitialsBarButtonItemWasTapped() { assert(Thread.isMainThread) let ownedIdentities: [PersistedObvOwnedIdentity] @@ -176,11 +194,45 @@ class ShowOwnedIdentityButtonUIViewController: UIViewController, OwnedIdentityCh .postOnDispatchQueue() present(ownedIdentityChooserVC, animated: true) } + + @objc func ownedCircledInitialsBarButtonItemWasLongPressed() { assert(Thread.isMainThread) showAlertForUnlockingHiddenOwnedIdentity() } + + + @objc func ownedCircledInitialsBarButtonItemWasSwiped(gestureRecognizer: UIPanGestureRecognizer) { + assert(Thread.isMainThread) + // Determine the appropriate owned identity to show + let nextOwnedCryptoId: ObvCryptoId + guard let currentOwnedIdentity = try? PersistedObvOwnedIdentity.get(cryptoId: currentOwnedCryptoId, within: ObvStack.shared.viewContext) else { assertionFailure(); return } + guard let nonHiddenOwnedIdentities = try? PersistedObvOwnedIdentity.getAllNonHiddenOwnedIdentities(within: ObvStack.shared.viewContext) else { assertionFailure(); return } + if currentOwnedIdentity.isHidden { + guard let nextOwnedIdentity = nonHiddenOwnedIdentities.first else { assertionFailure(); return } + nextOwnedCryptoId = nextOwnedIdentity.cryptoId + } else { + guard nonHiddenOwnedIdentities.contains(currentOwnedIdentity) else { assertionFailure(); return } + let list = nonHiddenOwnedIdentities + nonHiddenOwnedIdentities + guard let index = list.firstIndex(of: currentOwnedIdentity) else { assertionFailure(); return } + guard index-1 < list.count else { assertionFailure(); return } + let nextOwnedIdentity = list[index+1] + nextOwnedCryptoId = nextOwnedIdentity.cryptoId + } + + guard nextOwnedCryptoId != currentOwnedCryptoId else { return } + + profilePictureBarButtonItem?.doAnimateNextCircledInitialsConfigurationChange() + + ObvMessengerInternalNotification.userWantsToSwitchToOtherOwnedIdentity(ownedCryptoId: nextOwnedCryptoId) + .postOnDispatchQueue() + + + } + + // MARK: - Unlocking hidden owned identity + private func showAlertForUnlockingHiddenOwnedIdentity() { let alert = UIAlertController(title: Strings.OpenHiddenProfileAlert.title, message: Strings.OpenHiddenProfileAlert.message, @@ -194,6 +246,8 @@ class ShowOwnedIdentityButtonUIViewController: UIViewController, OwnedIdentityCh alert.addAction(UIAlertAction(title: CommonString.Word.Cancel, style: .cancel)) present(alert, animated: true) } + + @objc final private func textFieldForUnlockingHiddenProfileDidChange(textField: UITextField) { guard let presentedAlert = presentedViewController as? UIAlertController else { return } guard let presentedTextField = presentedAlert.textFields?.first else { return } @@ -275,36 +329,60 @@ extension ShowOwnedIdentityButtonUIViewController { +// MARK: - ProfilePictureBarButtonItem + + fileprivate class ProfilePictureBarButtonItem: UIBarButtonItem { + private var profilePictureView: NewCircledInitialsView? + private var profilePictureViewsContainer: UIView? private var buttonView: UIButton? private var longPressGestureRecognizer: UILongPressGestureRecognizer? + private var swipeGestureRecognizer: UISwipeGestureRecognizer? private var redDotView: DotView? + private var animateNextCircledInitialsConfigurationChange = false + private let generator = UINotificationFeedbackGenerator() + static func makeWithInitialConfiguration(_ initialConfiguration: CircledInitialsConfiguration) -> ProfilePictureBarButtonItem { + let buttonView = UIButton() buttonView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ buttonView.widthAnchor.constraint(equalTo: buttonView.heightAnchor), ]) + + let profilePictureViewsContainer = UIView() + profilePictureViewsContainer.translatesAutoresizingMaskIntoConstraints = false + profilePictureViewsContainer.isUserInteractionEnabled = false + profilePictureViewsContainer.clipsToBounds = true + buttonView.addSubview(profilePictureViewsContainer) + let profilePictureView = NewCircledInitialsView() profilePictureView.translatesAutoresizingMaskIntoConstraints = false profilePictureView.isUserInteractionEnabled = false - buttonView.addSubview(profilePictureView) + profilePictureViewsContainer.addSubview(profilePictureView) + let redDotView = DotView() redDotView.translatesAutoresizingMaskIntoConstraints = false redDotView.isHidden = true redDotView.isUserInteractionEnabled = false buttonView.addSubview(redDotView) + NSLayoutConstraint.activate([ - profilePictureView.centerXAnchor.constraint(equalTo: buttonView.centerXAnchor), - profilePictureView.centerYAnchor.constraint(equalTo: buttonView.centerYAnchor), - profilePictureView.heightAnchor.constraint(equalTo: buttonView.heightAnchor, multiplier: 0.8), + profilePictureViewsContainer.centerXAnchor.constraint(equalTo: buttonView.centerXAnchor), + profilePictureViewsContainer.centerYAnchor.constraint(equalTo: buttonView.centerYAnchor), + profilePictureViewsContainer.heightAnchor.constraint(equalTo: profilePictureViewsContainer.widthAnchor), + profilePictureViewsContainer.heightAnchor.constraint(equalTo: buttonView.heightAnchor, multiplier: 0.8), + + profilePictureView.centerXAnchor.constraint(equalTo: profilePictureViewsContainer.centerXAnchor), + profilePictureView.centerYAnchor.constraint(equalTo: profilePictureViewsContainer.centerYAnchor), + profilePictureView.heightAnchor.constraint(equalTo: profilePictureViewsContainer.heightAnchor), + redDotView.widthAnchor.constraint(equalTo: redDotView.heightAnchor), redDotView.widthAnchor.constraint(equalTo: buttonView.widthAnchor, multiplier: 0.25), redDotView.trailingAnchor.constraint(equalTo: buttonView.trailingAnchor, constant: -4), redDotView.topAnchor.constraint(equalTo: buttonView.topAnchor, constant: 4), - ]) profilePictureView.configureWith(initialConfiguration) @@ -314,18 +392,60 @@ fileprivate class ProfilePictureBarButtonItem: UIBarButtonItem { buttonItem.redDotView = redDotView return buttonItem } + + func configureWith(_ configuration: CircledInitialsConfiguration) { guard let profilePictureView else { assertionFailure(); return } - profilePictureView.configureWith(configuration) + defer { animateNextCircledInitialsConfigurationChange = false } + + if animateNextCircledInitialsConfigurationChange, let currentConfiguration = profilePictureView.currentConfiguration, let superview = profilePictureView.superview { + // We create a temporary NewCircledInitialsView for the animation (we will slide it down) + let tempProfilePictureView = NewCircledInitialsView() + tempProfilePictureView.isUserInteractionEnabled = false + tempProfilePictureView.configureWith(currentConfiguration) + superview.addSubview(tempProfilePictureView) + tempProfilePictureView.frame = profilePictureView.frame + + // Now that the temporary NewCircledInitialsView hides the profilePictureView, we can scale it down and configure it with the new received configuration + profilePictureView.transform = .init(scaleX: 0, y: 0) + profilePictureView.configureWith(configuration) + + // We launch two animations in parallel: + // - the first slides the temporary NewCircledInitialsView and removes it at the end + // - the second scales back the profilePictureView (after a short delay) + let typicalAnimationTime: TimeInterval = 0.2 + UIView.animate(withDuration: typicalAnimationTime, delay: 0, animations: { + tempProfilePictureView.center = CGPoint(x: tempProfilePictureView.center.x, y: tempProfilePictureView.center.y + tempProfilePictureView.frame.height * 1.1) + }) { _ in + tempProfilePictureView.removeFromSuperview() + } + UIView.animate(withDuration: typicalAnimationTime, delay: typicalAnimationTime/2, animations: { [weak self] in + profilePictureView.transform = .init(scaleX: 1, y: 1) + self?.generator.notificationOccurred(.success) + }) + } else { + profilePictureView.configureWith(configuration) + } } + + + func doAnimateNextCircledInitialsConfigurationChange() { + animateNextCircledInitialsConfigurationChange = true + } + + func configureRedDot(isHidden: Bool) { assert(redDotView != nil) redDotView?.isHidden = isHidden } + + func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControl.Event) { guard let buttonView else { assertionFailure(); return } buttonView.addTarget(target, action: action, for: controlEvents) } + + func setUILongPressGestureRecognizer(target: Any?, action: Selector?) { guard let buttonView else { assertionFailure(); return } assert(Thread.isMainThread) @@ -337,6 +457,19 @@ fileprivate class ProfilePictureBarButtonItem: UIBarButtonItem { buttonView.addGestureRecognizer(longPressGestureRecognizer!) } + + func setUISwipeGestureRecognizer(target: Any?, action: Selector?) { + guard let buttonView else { assertionFailure(); return } + assert(Thread.isMainThread) + if let swipeGestureRecognizer { + buttonView.removeGestureRecognizer(swipeGestureRecognizer) + } + swipeGestureRecognizer = nil + swipeGestureRecognizer = UISwipeGestureRecognizer(target: target, action: action) + swipeGestureRecognizer?.direction = .down + buttonView.addGestureRecognizer(swipeGestureRecognizer!) + } + } diff --git a/iOSClient/ObvMessenger/ObvMessenger/Utils/Loading Item Providers/LoadItemProviderOperation.swift b/iOSClient/ObvMessenger/ObvMessenger/Utils/Loading Item Providers/LoadItemProviderOperation.swift index bb179bb6..28567e3f 100644 --- a/iOSClient/ObvMessenger/ObvMessenger/Utils/Loading Item Providers/LoadItemProviderOperation.swift +++ b/iOSClient/ObvMessenger/ObvMessenger/Utils/Loading Item Providers/LoadItemProviderOperation.swift @@ -34,7 +34,7 @@ import OlvidUtils final class LoadItemProviderOperation: OperationWithSpecificReasonForCancel { private let preferredUTIs = [kUTTypeFileURL, kUTTypeJPEG, kUTTypePNG, kUTTypeMPEG4, kUTTypeMP3, kUTTypeQuickTimeMovie].map({ $0 as String }) - private let ignoredUTIs = [UTI.Bitmoji.avatarID, UTI.Bitmoji.comicID, UTI.Bitmoji.packID] + private let ignoredUTIs = [UTI.Bitmoji.avatarID, UTI.Bitmoji.comicID, UTI.Bitmoji.packID, UTI.Apple.groupActivitiesActivity] private let itemProviderOrItemURL: ItemProviderOrItemURL @@ -132,6 +132,8 @@ final class LoadItemProviderOperation: OperationWithSpecificReasonForCancelCFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 611 + $(CURRENT_PROJECT_VERSION) NSExtension NSExtensionPointIdentifier diff --git a/iOSClient/ObvMessenger/ObvMessengerShareExtension/Info.plist b/iOSClient/ObvMessenger/ObvMessengerShareExtension/Info.plist index bfe149c8..cef91a55 100644 --- a/iOSClient/ObvMessenger/ObvMessengerShareExtension/Info.plist +++ b/iOSClient/ObvMessenger/ObvMessengerShareExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 611 + $(CURRENT_PROJECT_VERSION) NSExtension NSExtensionAttributes diff --git a/iOSClient/ObvMessenger/ObvMessengerShareExtension/Operations/LoadFileRepresentationsOperation.swift b/iOSClient/ObvMessenger/ObvMessengerShareExtension/Operations/LoadFileRepresentationsOperation.swift index a23a1de4..19c8334f 100644 --- a/iOSClient/ObvMessenger/ObvMessengerShareExtension/Operations/LoadFileRepresentationsOperation.swift +++ b/iOSClient/ObvMessenger/ObvMessengerShareExtension/Operations/LoadFileRepresentationsOperation.swift @@ -39,13 +39,12 @@ final class LoadFileRepresentationsOperation: Operation, LoadedItemProviderProvi private let itemProviders: [NSItemProvider] - private let log: OSLog + private let log = OSLog(subsystem: ObvMessengerConstants.logSubsystem, category: "LoadFileRepresentationsOperation") private(set) var loadedItemProviders: [LoadedItemProvider]? - init(itemProviders: [NSItemProvider], log: OSLog) { + init(itemProviders: [NSItemProvider]) { self.itemProviders = itemProviders - self.log = log super.init() } @@ -57,12 +56,16 @@ final class LoadFileRepresentationsOperation: Operation, LoadedItemProviderProvi op.waitUntilFinished() assert(op.isFinished) guard !op.isCancelled else { + os_log("The operation cancelled for item provider %{public}@", log: log, type: .error, itemProvider.debugDescription) op.logReasonIfCancelled(log: log) - return + continue } + os_log("The operation did not cancel for item provider %{public}@", log: log, type: .info, itemProvider.debugDescription) guard let loadedItemProvider = op.loadedItemProvider else { - return + os_log("The operation does not provide a loaded item provider for item provider %{public}@", log: log, type: .error, itemProvider.debugDescription) + continue } + os_log("Adding a loaded item provider to the list for item provider %{public}@", log: log, type: .info, itemProvider.debugDescription) loadedItemProviders += [loadedItemProvider] } self.loadedItemProviders = loadedItemProviders diff --git a/iOSClient/ObvMessenger/ObvMessengerShareExtension/ShareViewController.swift b/iOSClient/ObvMessenger/ObvMessengerShareExtension/ShareViewController.swift index 6c6bc38e..8a9b18e7 100644 --- a/iOSClient/ObvMessenger/ObvMessengerShareExtension/ShareViewController.swift +++ b/iOSClient/ObvMessenger/ObvMessengerShareExtension/ShareViewController.swift @@ -215,6 +215,7 @@ final class ShareViewController: UIViewController, ShareExtensionErrorViewContro } override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) self.earlyAbortWipe() } @@ -413,7 +414,7 @@ final class ShareViewHostingController: UIHostingController, ShareVie } // Compute [LoadedItemProvider] from [NSItemProvider] - let op1 = LoadFileRepresentationsOperation(itemProviders: itemProviders, log: Self.log) + let op1 = LoadFileRepresentationsOperation(itemProviders: itemProviders) op1.completionBlock = { os_log("📤 Load File Representations Operation done.", log: Self.log, type: .info) }