diff --git a/ElementX/Sources/Application/TargetConfiguration.swift b/ElementX/Sources/Application/TargetConfiguration.swift index a82a497a4..d0a0406e2 100644 --- a/ElementX/Sources/Application/TargetConfiguration.swift +++ b/ElementX/Sources/Application/TargetConfiguration.swift @@ -7,6 +7,7 @@ import MatrixRustSDK +@MainActor enum Target: String { case mainApp = "elementx" case nse diff --git a/ElementX/Sources/Other/ExpiringTaskRunner.swift b/ElementX/Sources/Other/ExpiringTaskRunner.swift index 5bc8f1acc..835226621 100644 --- a/ElementX/Sources/Other/ExpiringTaskRunner.swift +++ b/ElementX/Sources/Other/ExpiringTaskRunner.swift @@ -11,7 +11,7 @@ enum ExpiringTaskRunnerError: Error { case timeout } -actor ExpiringTaskRunner { +actor ExpiringTaskRunner { private var continuation: CheckedContinuation? private var task: () async throws -> T diff --git a/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift b/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift index 490062c90..a4943c96c 100644 --- a/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift +++ b/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift @@ -9,34 +9,34 @@ import Foundation enum BlockquoteAttribute: AttributedStringKey { typealias Value = Bool - static var name = "MXBlockquoteAttribute" + static let name = "MXBlockquoteAttribute" } enum UserIDAttribute: AttributedStringKey { typealias Value = String - static var name = "MXUserIDAttribute" + static let name = "MXUserIDAttribute" } /// This attribute is used to help the composer convert a mention into to a markdown link before sending /// the message. It doesn't interact mention pills, as these fetch display names live from the room. enum UserDisplayNameAttribute: AttributedStringKey { typealias Value = String - static var name = "MXUserDisplayNameAttribute" + static let name = "MXUserDisplayNameAttribute" } enum RoomDisplayNameAttribute: AttributedStringKey { typealias Value = String - static var name = "MXRoomDisplayNameAttribute" + static let name = "MXRoomDisplayNameAttribute" } enum RoomIDAttribute: AttributedStringKey { typealias Value = String - static var name = "MXRoomIDAttribute" + static let name = "MXRoomIDAttribute" } enum RoomAliasAttribute: AttributedStringKey { typealias Value = String - static var name = "MXRoomAliasAttribute" + static let name = "MXRoomAliasAttribute" } enum EventOnRoomIDAttribute: AttributedStringKey { @@ -45,7 +45,7 @@ enum EventOnRoomIDAttribute: AttributedStringKey { let eventID: String } - static var name = "MXEventOnRoomIDAttribute" + static let name = "MXEventOnRoomIDAttribute" } enum EventOnRoomAliasAttribute: AttributedStringKey { @@ -54,12 +54,12 @@ enum EventOnRoomAliasAttribute: AttributedStringKey { let eventID: String } - static var name = "MXEventOnRoomAliasAttribute" + static let name = "MXEventOnRoomAliasAttribute" } enum AllUsersMentionAttribute: AttributedStringKey { typealias Value = Bool - static var name = "MXAllUsersMentionAttribute" + static let name = "MXAllUsersMentionAttribute" } // periphery: ignore - required to make NSAttributedString to AttributedString conversion even if not used directly diff --git a/ElementX/Sources/Other/Logging/MXLog.swift b/ElementX/Sources/Other/Logging/MXLog.swift index 36d797f30..ebe05906e 100644 --- a/ElementX/Sources/Other/Logging/MXLog.swift +++ b/ElementX/Sources/Other/Logging/MXLog.swift @@ -9,13 +9,11 @@ import Foundation import MatrixRustSDK -/** - Logging utility that provies multiple logging levels as well as file output and rolling. - Its purpose is to provide a common entry for customizing logging and should be used throughout the code. - */ +/// Logging utility that provies multiple logging levels as well as file output and rolling. +/// Its purpose is to provide a common entry for customizing logging and should be used throughout the code. enum MXLog { - private static var rootSpan: Span! - private static var currentTarget: String! + private nonisolated(unsafe) static var rootSpan: Span! + private nonisolated(unsafe) static var currentTarget: String! static func configure(currentTarget: String) { self.currentTarget = currentTarget diff --git a/ElementX/Sources/Other/MatrixEntityRegex.swift b/ElementX/Sources/Other/MatrixEntityRegex.swift index 4b52d9ad6..8b6b43672 100644 --- a/ElementX/Sources/Other/MatrixEntityRegex.swift +++ b/ElementX/Sources/Other/MatrixEntityRegex.swift @@ -32,12 +32,12 @@ enum MatrixEntityRegex: String { } // swiftlint:disable force_try - static var homeserverRegex = try! NSRegularExpression(pattern: MatrixEntityRegex.homeserver.rawValue, options: .caseInsensitive) - static var userIdentifierRegex = try! NSRegularExpression(pattern: MatrixEntityRegex.userID.rawValue, options: .caseInsensitive) - static var roomAliasRegex = try! NSRegularExpression(pattern: MatrixEntityRegex.roomAlias.rawValue, options: .caseInsensitive) - static var uriRegex = try! NSRegularExpression(pattern: MatrixEntityRegex.uri.rawValue, options: .caseInsensitive) - static var allUsersRegex = try! NSRegularExpression(pattern: MatrixEntityRegex.allUsers.rawValue) - static var linkRegex = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) + static let homeserverRegex = try! NSRegularExpression(pattern: MatrixEntityRegex.homeserver.rawValue, options: .caseInsensitive) + static let userIdentifierRegex = try! NSRegularExpression(pattern: MatrixEntityRegex.userID.rawValue, options: .caseInsensitive) + static let roomAliasRegex = try! NSRegularExpression(pattern: MatrixEntityRegex.roomAlias.rawValue, options: .caseInsensitive) + static let uriRegex = try! NSRegularExpression(pattern: MatrixEntityRegex.uri.rawValue, options: .caseInsensitive) + static let allUsersRegex = try! NSRegularExpression(pattern: MatrixEntityRegex.allUsers.rawValue) + static let linkRegex = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) // swiftlint:enable force_try static func isMatrixHomeserver(_ homeserver: String) -> Bool { diff --git a/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenViewModel.swift b/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenViewModel.swift index ee14884ba..378b6b352 100644 --- a/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenViewModel.swift +++ b/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenViewModel.swift @@ -55,7 +55,6 @@ class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScr private func convert(emojiCategories: [EmojiCategory]) -> [EmojiPickerEmojiCategoryViewData] { emojiCategories.compactMap { emojiCategory in - let emojisViewData: [EmojiPickerEmojiViewData] = emojiCategory.emojis.compactMap { emojiItem in EmojiPickerEmojiViewData(id: emojiItem.id, value: emojiItem.unicode) } diff --git a/ElementX/Sources/Services/Media/Provider/MediaFileHandleProxy.swift b/ElementX/Sources/Services/Media/Provider/MediaFileHandleProxy.swift index 179814f9c..af40e0909 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaFileHandleProxy.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaFileHandleProxy.swift @@ -6,11 +6,12 @@ // import Foundation -import MatrixRustSDK + +@preconcurrency import MatrixRustSDK /// A wrapper around Rust's `MediaFileHandle` type that provides us with a /// media file that is stored unencrypted in a temporary location for previewing. -class MediaFileHandleProxy { +final class MediaFileHandleProxy: Sendable { /// The underlying handle for the file. private let handle: MediaFileHandleProtocol diff --git a/ElementX/Sources/Services/Media/Provider/MediaLoader.swift b/ElementX/Sources/Services/Media/Provider/MediaLoader.swift index 19ebac8e1..89478fc6c 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaLoader.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaLoader.swift @@ -7,9 +7,10 @@ import Combine import Foundation -import MatrixRustSDK import UIKit +@preconcurrency import MatrixRustSDK + private final class MediaRequest { var continuations: [CheckedContinuation] = [] } diff --git a/ElementX/Sources/Services/Media/Provider/MediaSourceProxy.swift b/ElementX/Sources/Services/Media/Provider/MediaSourceProxy.swift index f86aa4e08..a4c7216b6 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaSourceProxy.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaSourceProxy.swift @@ -6,9 +6,9 @@ // import Foundation -import MatrixRustSDK +@preconcurrency import MatrixRustSDK -struct MediaSourceProxy: Hashable { +struct MediaSourceProxy: Hashable, Sendable { /// The media source provided by Rust. let underlyingSource: MediaSource /// The media's mime type, used when loading the media's file. diff --git a/NSE/Sources/NotificationServiceExtension.swift b/NSE/Sources/NotificationServiceExtension.swift index 338bb6553..fe052e8e6 100644 --- a/NSE/Sources/NotificationServiceExtension.swift +++ b/NSE/Sources/NotificationServiceExtension.swift @@ -27,16 +27,18 @@ import UserNotifications // called on the same instance of `NotificationService` as a previous // notification. -private let settings: CommonSettingsProtocol = AppSettings() - -private let keychainController = KeychainController(service: .sessions, - accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier) - class NotificationServiceExtension: UNNotificationServiceExtension { private var notificationHandler: NotificationHandler? private let appHooks = AppHooks() + private let settings: CommonSettingsProtocol = AppSettings() + + private let keychainController = KeychainController(service: .sessions, + accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier) + + // We can make the whole NSE a MainActor after https://github.com/swiftlang/swift-evolution/blob/main/proposals/0371-isolated-synchronous-deinit.md + // otherwise we wouldn't be able to log the tag in the deinit. deinit { ExtensionLogger.logMemory(with: tag) MXLog.info("\(tag) deinit") @@ -61,15 +63,15 @@ class NotificationServiceExtension: UNNotificationServiceExtension { return contentHandler(request.content) } - Target.nse.configure(logLevel: settings.logLevel, traceLogPacks: settings.traceLogPacks) - - MXLog.info("\(tag) #########################################") - - ExtensionLogger.logMemory(with: tag) - - MXLog.info("\(tag) Received payload: \(request.content.userInfo)") - Task { + await Target.nse.configure(logLevel: settings.logLevel, traceLogPacks: settings.traceLogPacks) + + MXLog.info("\(tag) #########################################") + + ExtensionLogger.logMemory(with: tag) + + MXLog.info("\(tag) Received payload: \(request.content.userInfo)") + do { let userSession = try await NSEUserSession(credentials: credentials, roomID: roomID, diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 3de5ba64f..64236171c 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -22,7 +22,7 @@ class LoggingTests: XCTestCase { let target = "tests" XCTAssertTrue(Tracing.logFiles.isEmpty) - Target.tests.configure(logLevel: .info, traceLogPacks: []) + await Target.tests.configure(logLevel: .info, traceLogPacks: []) // There is something weird with Rust logging where the file writing handle doesn't // notice that the file it is writing to was deleted, so we can't run these checks @@ -34,7 +34,7 @@ class LoggingTests: XCTestCase { try validateTargetName(target) try validateRoomSummaryContentIsRedacted() - try validateTimelineContentIsRedacted() + try await validateTimelineContentIsRedacted() try validateRustMessageContentIsRedacted() } @@ -114,7 +114,7 @@ class LoggingTests: XCTestCase { XCTAssertFalse(content.contains(heroName)) } - func validateTimelineContentIsRedacted() throws { + func validateTimelineContentIsRedacted() async throws { // Given timeline items that contain text let textAttributedString = "TextAttributed" let textMessage = TextRoomTimelineItem(id: .randomEvent, @@ -174,7 +174,7 @@ class LoggingTests: XCTestCase { contentType: nil)) // When logging that value - Target.tests.configure(logLevel: .info, traceLogPacks: []) + await Target.tests.configure(logLevel: .info, traceLogPacks: []) MXLog.info(textMessage) MXLog.info(noticeMessage)