diff --git a/LibSession-Util b/LibSession-Util index d6147ef38..23c943d44 160000 --- a/LibSession-Util +++ b/LibSession-Util @@ -1 +1 @@ -Subproject commit d6147ef385e39d3fae64611dcf16ac1e14bab333 +Subproject commit 23c943d44e0127dc243de3ed4f72c550e06638ee diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 0434bafb9..f8b99e4b0 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -8109,7 +8109,7 @@ CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 465; + CURRENT_PROJECT_VERSION = 466; ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -8187,7 +8187,7 @@ CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 465; + CURRENT_PROJECT_VERSION = 466; ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; diff --git a/Session/Onboarding/LoadingScreen.swift b/Session/Onboarding/LoadingScreen.swift index d85d0c10a..af3560ef3 100644 --- a/Session/Onboarding/LoadingScreen.swift +++ b/Session/Onboarding/LoadingScreen.swift @@ -112,6 +112,8 @@ struct LoadingScreen: View { self.percentage = 1 } DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.flow.completeRegistration() + let homeVC: HomeVC = HomeVC(flow: self.flow) self.host.controller?.navigationController?.setViewControllers([ homeVC ], animated: true) } diff --git a/Session/Onboarding/PNModeScreen.swift b/Session/Onboarding/PNModeScreen.swift index ee66f00f3..1e14a38e6 100644 --- a/Session/Onboarding/PNModeScreen.swift +++ b/Session/Onboarding/PNModeScreen.swift @@ -130,10 +130,7 @@ struct PNModeScreen: View { // If we are registering then we can just continue on guard flow != .register else { - self.flow.completeRegistration() - self.finishRegister() - - return + return finishRegister() } // Check if we already have a profile name (ie. profile retrieval completed while waiting on @@ -149,12 +146,7 @@ struct PNModeScreen: View { guard existingProfileName?.isEmpty != false else { // If we have one then we can go straight to the home screen - self.flow.completeRegistration() - - // Go to the home screen - let homeVC: HomeVC = HomeVC() - self.host.controller?.navigationController?.setViewControllers([ homeVC ], animated: true) - return + return finishRegister() } // If we don't have one then show a loading indicator and try to retrieve the existing name @@ -166,6 +158,8 @@ struct PNModeScreen: View { } private func finishRegister() { + self.flow.completeRegistration() + let homeVC: HomeVC = HomeVC(flow: self.flow) self.host.controller?.navigationController?.setViewControllers([ homeVC ], animated: true) return diff --git a/SessionMessagingKit/Jobs/Types/MessageSendJob.swift b/SessionMessagingKit/Jobs/Types/MessageSendJob.swift index e70b578c1..e10fb6919 100644 --- a/SessionMessagingKit/Jobs/Types/MessageSendJob.swift +++ b/SessionMessagingKit/Jobs/Types/MessageSendJob.swift @@ -28,9 +28,15 @@ public enum MessageSendJob: JobExecutor { return failure(job, JobRunnerError.missingRequiredDetails, true, dependencies) } - // We need to include 'fileIds' when sending messages with attachments to Open Groups - // so extract them from any associated attachments + /// We need to include `fileIds` when sending messages with attachments to Open Groups so extract them from any + /// associated attachments var messageFileIds: [String] = [] + let messageType: String = { + switch details.destination { + case .syncMessage: return "\(type(of: details.message)) (SyncMessage)" + default: return "\(type(of: details.message))" + } + }() /// Ensure any associated attachments have already been uploaded before sending the message /// @@ -188,11 +194,11 @@ public enum MessageSendJob: JobExecutor { receiveCompletion: { result in switch result { case .finished: - Log.info("[MessageSendJob] Completed sending \(type(of: details.message)) after \(.seconds(dependencies.dateNow.timeIntervalSince1970 - startTime), unit: .s).") + Log.info("[MessageSendJob] Completed sending \(messageType) after \(.seconds(dependencies.dateNow.timeIntervalSince1970 - startTime), unit: .s).") success(job, false, dependencies) case .failure(let error): - Log.info("[MessageSendJob] Failed to send \(type(of: details.message)) after \(.seconds(dependencies.dateNow.timeIntervalSince1970 - startTime), unit: .s) due to error: \(error).") + Log.info("[MessageSendJob] Failed to send \(messageType) after \(.seconds(dependencies.dateNow.timeIntervalSince1970 - startTime), unit: .s) due to error: \(error).") // Actual error handling switch (error, details.message) { @@ -269,36 +275,6 @@ extension MessageSendJob { let message: Message = try variant.decode(from: container, forKey: .message) var destination: Message.Destination = try container.decode(Message.Destination.self, forKey: .destination) - /// Handle the legacy 'isSyncMessage' flag - this flag was deprecated in `2.5.2` (April 2024) and can be removed in a - /// subsequent release after May 2024 - if ((try? container.decode(Bool.self, forKey: .isSyncMessage)) ?? false) { - switch (destination, message) { - case (.contact, let message as VisibleMessage): - guard let targetPublicKey: String = message.syncTarget else { - SNLog("Unable to decode messageSend job due to missing syncTarget") - throw StorageError.decodingFailed - } - - destination = .syncMessage(originalRecipientPublicKey: targetPublicKey) - - case (.contact, let message as ExpirationTimerUpdate): - guard let targetPublicKey: String = message.syncTarget else { - SNLog("Unable to decode messageSend job due to missing syncTarget") - throw StorageError.decodingFailed - } - - destination = .syncMessage(originalRecipientPublicKey: targetPublicKey) - - case (.contact(let publicKey), _): - SNLog("Sync message in messageSend job was missing explicit syncTarget (falling back to specified value)") - destination = .syncMessage(originalRecipientPublicKey: publicKey) - - default: - SNLog("Unable to decode messageSend job due to invalid sync message state") - throw StorageError.decodingFailed - } - } - self = Details( destination: destination, message: message diff --git a/SessionNotificationServiceExtension/NotificationError.swift b/SessionNotificationServiceExtension/NotificationError.swift index 8f16548fc..355c5563c 100644 --- a/SessionNotificationServiceExtension/NotificationError.swift +++ b/SessionNotificationServiceExtension/NotificationError.swift @@ -17,8 +17,8 @@ enum NotificationError: Error, CustomStringConvertible { case .processing(let result): return "Failed to process notification (\(result)) (NotificationError.processing)." case .messageProcessing: return "Failed to process message (NotificationError.messageProcessing)." case .ignorableMessage: return "Ignorable message (NotificationError.ignorableMessage)." - case .messageHandling(let error): return "Failed to handle message (\("\(error)".noPeriod)) (NotificationError.messageHandling)." - case .other(let error): return "Unknown error occurred: \("\(error)".noPeriod)) (NotificationError.other)." + case .messageHandling(let error): return "Failed to handle message (\(error)) (NotificationError.messageHandling)." + case .other(let error): return "Unknown error occurred: \(error) (NotificationError.other)." } } } diff --git a/SessionSnodeKit/LibSession/LibSession+Networking.swift b/SessionSnodeKit/LibSession/LibSession+Networking.swift index 125fd1ac0..53b6e30af 100644 --- a/SessionSnodeKit/LibSession/LibSession+Networking.swift +++ b/SessionSnodeKit/LibSession/LibSession+Networking.swift @@ -36,12 +36,17 @@ public extension LibSession { static var hasPaths: Bool { !lastPaths.wrappedValue.isEmpty } static var pathsDescription: String { lastPaths.wrappedValue.prettifiedDescription } - fileprivate class CallbackWrapper { + internal class CallbackWrapper { public let resultPublisher: CurrentValueSubject = CurrentValueSubject(nil) + private let callback: ((Output) -> Void)? private var pointersToDeallocate: [UnsafeRawPointer?] = [] // MARK: - Initialization + init(_ callback: ((Output) -> Void)? = nil) { + self.callback = callback + } + deinit { pointersToDeallocate.forEach { $0?.deallocate() } } @@ -75,9 +80,17 @@ public extension LibSession { return Log.error("[LibSession] CallbackWrapper called with null context.") } - // Dispatch async so we don't block libSession's internals with Swift logic (which can block other requests) let wrapper: CallbackWrapper = Unmanaged>.fromOpaque(ctx).takeRetainedValue() - DispatchQueue.global(qos: .default).async { [wrapper] in wrapper.resultPublisher.send(output) } + + switch wrapper.callback { + case .none: + // Dispatch async so we don't block libSession's internals with Swift logic (which can block other requests) + DispatchQueue.global(qos: .default).async { [wrapper] in wrapper.resultPublisher.send(output) } + + case .some(let callback): + // We generally shouldn't use the `callback` method but it's useful for tests + callback(output) + } } public func unsafePointer() -> UnsafeMutableRawPointer { Unmanaged.passRetained(self).toOpaque() } @@ -610,6 +623,19 @@ extension LibSession { ed25519PubkeyHex = String(libSessionVal: cSnode.ed25519_pubkey_hex) } + internal init?(nodeString: String) { + let parts: [String] = nodeString.components(separatedBy: "|") + + guard + parts.count == 4, + let port: UInt16 = UInt16(parts[1]) + else { return nil } + + ip = parts[0] + quicPort = port + ed25519PubkeyHex = parts[3] + } + public func hash(into hasher: inout Hasher) { ip.hash(into: &hasher) quicPort.hash(into: &hasher) @@ -646,7 +672,7 @@ public extension Network.Destination { } } -private extension LibSession.CallbackWrapper { +internal extension LibSession.CallbackWrapper { func cServerDestination(_ destination: Network.Destination) throws -> network_server_destination { guard case .server(let url, let method, let headers, let x25519PublicKey) = destination, diff --git a/SessionUtilitiesKit/General/String+Utilities.swift b/SessionUtilitiesKit/General/String+Utilities.swift index 895fe6df6..b6faf4b86 100644 --- a/SessionUtilitiesKit/General/String+Utilities.swift +++ b/SessionUtilitiesKit/General/String+Utilities.swift @@ -77,14 +77,6 @@ public extension String { // MARK: - Formatting -public extension String { - var noPeriod: String { - guard self.hasSuffix(".") && !self.hasSuffix("...") else { return self } - - return String(self.prefix(count - 1)) - } -} - public extension String.StringInterpolation { mutating func appendInterpolation(_ value: TimeUnit, unit: TimeUnit.Unit, resolution: Int = 2) { appendLiteral("\(TimeUnit(value, unit: unit, resolution: resolution))") // stringlint:disable diff --git a/SessionUtilitiesKit/JobRunner/JobRunner.swift b/SessionUtilitiesKit/JobRunner/JobRunner.swift index 811a8aa70..af0beaad7 100644 --- a/SessionUtilitiesKit/JobRunner/JobRunner.swift +++ b/SessionUtilitiesKit/JobRunner/JobRunner.swift @@ -742,7 +742,7 @@ public final class JobRunner: JobRunnerType { ) -> (Int64, Job)? { switch job?.behaviour { case .recurringOnActive, .recurringOnLaunch, .runOnceNextLaunch: - SNLog("[JobRunner] Attempted to insert \(job?.variant) job before the current one even though it's behaviour is \(job?.behaviour)") + SNLog("[JobRunner] Attempted to insert \(job) before the current one even though it's behaviour is \(job?.behaviour)") return nil default: break @@ -843,7 +843,7 @@ public final class JobRunner: JobRunnerType { case (.enqueueOnly, .some(let uniqueHashValue)): // Nothing currently running or sitting in a JobQueue guard !allJobInfo().contains(where: { _, info -> Bool in info.uniqueHashValue == uniqueHashValue }) else { - SNLog("[JobRunner] Unable to add \(job.variant) job due to unique constraint") + SNLog("[JobRunner] Unable to add \(job) due to unique constraint") return nil } @@ -856,7 +856,7 @@ public final class JobRunner: JobRunnerType { // Nothing in the database !Job.filter(Job.Columns.uniqueHashValue == uniqueHashValue).isNotEmpty(db) else { - SNLog("[JobRunner] Unable to add \(job.variant) job due to unique constraint") + SNLog("[JobRunner] Unable to add \(job) due to unique constraint") return nil } @@ -864,7 +864,7 @@ public final class JobRunner: JobRunnerType { case (.persist, .none): guard let updatedJob: Job = try? job.inserted(db), updatedJob.id != nil else { - SNLog("[JobRunner] Unable to add \(job.variant) job\(job.id == nil ? " due to missing id" : "")") + SNLog("[JobRunner] Unable to add \(job)\(job.id == nil ? " due to missing id" : "")") return nil } @@ -1062,7 +1062,7 @@ public final class JobQueue: Hashable { job.nextRunTimestamp <= dependencies.dateNow.timeIntervalSince1970 else { return } guard job.id != nil else { - SNLog("[JobRunner] Prevented attempt to add \(job.variant) job without id to queue") + SNLog("[JobRunner] Prevented attempt to add \(job) without id to queue") return } @@ -1090,7 +1090,7 @@ public final class JobQueue: Hashable { using dependencies: Dependencies ) { guard let jobId: Int64 = job.id else { - SNLog("[JobRunner] Prevented attempt to upsert \(job.variant) job without id to queue") + SNLog("[JobRunner] Prevented attempt to upsert \(job) without id to queue") return } @@ -1116,7 +1116,7 @@ public final class JobQueue: Hashable { fileprivate func insert(_ job: Job, before otherJob: Job) { guard job.id != nil else { - SNLog("[JobRunner] Prevented attempt to insert \(job.variant) job without id to queue") + SNLog("[JobRunner] Prevented attempt to insert \(job) without id to queue") return } @@ -1323,7 +1323,7 @@ public final class JobQueue: Hashable { // Run the first job in the pendingJobsQueue if !wasAlreadyRunning { - Log.info("[JobRunner] Starting \(queueContext) with (\(jobCount) job\(jobCount != 1 ? "s" : ""))") + Log.info("[JobRunner] Starting \(queueContext) with \(jobCount) jobs") } runNextJob(using: dependencies) } @@ -1360,7 +1360,7 @@ public final class JobQueue: Hashable { return } guard let jobExecutor: JobExecutor.Type = executorMap.wrappedValue[nextJob.variant] else { - SNLog("[JobRunner] \(queueContext) Unable to run \(nextJob.variant) job due to missing executor") + SNLog("[JobRunner] \(queueContext) Unable to run \(nextJob) due to missing executor") handleJobFailed( nextJob, error: JobRunnerError.executorMissing, @@ -1370,7 +1370,7 @@ public final class JobQueue: Hashable { return } guard !jobExecutor.requiresThreadId || nextJob.threadId != nil else { - SNLog("[JobRunner] \(queueContext) Unable to run \(nextJob.variant) job due to missing required threadId") + SNLog("[JobRunner] \(queueContext) Unable to run \(nextJob) due to missing required threadId") handleJobFailed( nextJob, error: JobRunnerError.requiredThreadIdMissing, @@ -1380,7 +1380,7 @@ public final class JobQueue: Hashable { return } guard !jobExecutor.requiresInteractionId || nextJob.interactionId != nil else { - SNLog("[JobRunner] \(queueContext) Unable to run \(nextJob.variant) job due to missing required interactionId") + SNLog("[JobRunner] \(queueContext) Unable to run \(nextJob) due to missing required interactionId") handleJobFailed( nextJob, error: JobRunnerError.requiredInteractionIdMissing, @@ -1390,7 +1390,7 @@ public final class JobQueue: Hashable { return } guard nextJob.id != nil else { - SNLog("[JobRunner] \(queueContext) Unable to run \(nextJob.variant) job due to missing id") + SNLog("[JobRunner] \(queueContext) Unable to run \(nextJob) due to missing id") handleJobFailed( nextJob, error: JobRunnerError.jobIdMissing, @@ -1420,7 +1420,7 @@ public final class JobQueue: Hashable { .defaulting(to: (0, [])) guard dependencyInfo.jobs.count == dependencyInfo.expectedCount else { - SNLog("[JobRunner] \(queueContext) Removing \(nextJob.variant) job due to missing dependencies") + SNLog("[JobRunner] \(queueContext) Removing \(nextJob) due to missing dependencies") handleJobFailed( nextJob, error: JobRunnerError.missingDependencies, @@ -1430,7 +1430,7 @@ public final class JobQueue: Hashable { return } guard dependencyInfo.jobs.isEmpty else { - SNLog("[JobRunner] \(queueContext) Deferring \(nextJob.variant) job until \(dependencyInfo.jobs.count) dependenc\(dependencyInfo.jobs.count == 1 ? "y" : "ies") are completed") + SNLog("[JobRunner] \(queueContext) Deferring \(nextJob) until \(dependencyInfo.jobs.count) dependencies are completed") // Enqueue the dependencies then defer the current job dependencies.jobRunner.enqueueDependenciesIfNeeded( @@ -1467,7 +1467,7 @@ public final class JobQueue: Hashable { ) ) } - SNLog("[JobRunner] \(queueContext) started \(nextJob.variant) job (\(executionType == .concurrent ? "\(numJobsRunning) currently running, " : "")\(numJobsRemaining) remaining)") + SNLog("[JobRunner] \(queueContext) started \(nextJob) (\(executionType == .concurrent ? "\(numJobsRunning) currently running, " : "")\(numJobsRemaining) remaining)") jobExecutor.run( nextJob, @@ -1642,7 +1642,7 @@ public final class JobQueue: Hashable { using dependencies: Dependencies ) { guard dependencies.storage.read(using: dependencies, { db in try Job.exists(db, id: job.id ?? -1) }) == true else { - SNLog("[JobRunner] \(queueContext) \(job.variant) job canceled") + SNLog("[JobRunner] \(queueContext) \(job) canceled") performCleanUp(for: job, result: .failed(error, permanentFailure), using: dependencies) internalQueue.async(using: dependencies) { [weak self] in @@ -1655,7 +1655,7 @@ public final class JobQueue: Hashable { // immediately (in this case we don't trigger any job callbacks because the // job isn't actually done, it's going to try again immediately) if self.type == .blocking && job.shouldBlock { - SNLog("[JobRunner] \(queueContext) \(job.variant) job failed due to error: \("\(error ?? JobRunnerError.unknown)".noPeriod); retrying immediately") + SNLog("[JobRunner] \(queueContext) \(job) failed due to error: \(error ?? JobRunnerError.unknown); retrying immediately") // If it was a possible deferral loop then we don't actually want to // retry the job (even if it's a blocking one, this gives a small chance @@ -1747,7 +1747,7 @@ public final class JobQueue: Hashable { } } - SNLog("[JobRunner] \(queueContext) \(job.variant) job \(failureText)") + SNLog("[JobRunner] \(queueContext) \(job) \(failureText)") performCleanUp(for: job, result: .failed(error, permanentFailure), using: dependencies) internalQueue.async(using: dependencies) { [weak self] in self?.runNextJob(using: dependencies) @@ -1840,6 +1840,19 @@ public final class JobQueue: Hashable { // MARK: - Formatting +private extension String.StringInterpolation { + mutating func appendInterpolation(_ job: Job) { + appendLiteral("\(job.variant) job (id: \(job.id ?? -1))") // stringlint:disable + } + + mutating func appendInterpolation(_ job: Job?) { + switch job { + case .some(let job): appendInterpolation(job) + case .none: appendLiteral("null job") // stringlint:disable + } + } +} + extension String.StringInterpolation { mutating func appendInterpolation(_ variant: Job.Variant?) { appendLiteral(variant.map { "\($0)" } ?? "unknown") // stringlint:disable