diff --git a/AGENTS.md b/AGENTS.md index 09806fe0b..32dc9a756 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -279,7 +279,7 @@ Services = where product-level opinions live (Rust SDK stays spec-faithful). ## Concurrency & Actors -- All targets **Swift 6.2**, approachable concurrency. +- All targets **Swift 6** language mode (`SWIFT_VERSION: 6`), approachable concurrency. Compiler version come from Xcode toolchain. - **ElementX** app, **UnitTests** + **PreviewTests**: `SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor` — no redundant `@MainActor`. Other targets (incl. app extensions) nonisolated. - Services + data-layer types: `nonisolated` + `Sendable` when background work need. - Never `@unchecked Sendable` / `nonisolated(unsafe)`. Dev add these. diff --git a/AccessibilityTests/SupportingFiles/target.yml b/AccessibilityTests/SupportingFiles/target.yml index 3a5c43e3c..f35041018 100644 --- a/AccessibilityTests/SupportingFiles/target.yml +++ b/AccessibilityTests/SupportingFiles/target.yml @@ -36,7 +36,7 @@ targets: base: PRODUCT_NAME: AccessibilityTests PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.accessibility.tests - SWIFT_VERSION: 6.2 + SWIFT_VERSION: 6 SWIFT_APPROACHABLE_CONCURRENCY: YES configs: # This is required to remove the $(inherited) flags diff --git a/Components/MapLibre/MapLibreInterface/SupportingFiles/target.yml b/Components/MapLibre/MapLibreInterface/SupportingFiles/target.yml index c38ea268c..896381bcf 100644 --- a/Components/MapLibre/MapLibreInterface/SupportingFiles/target.yml +++ b/Components/MapLibre/MapLibreInterface/SupportingFiles/target.yml @@ -11,7 +11,7 @@ targets: base: PRODUCT_BUNDLE_IDENTIFIER: $(BASE_BUNDLE_IDENTIFIER).maplibre-interface GENERATE_INFOPLIST_FILE: YES - SWIFT_VERSION: 6.2 + SWIFT_VERSION: 6 SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor diff --git a/Components/MapLibre/MapLibreShim/SupportingFiles/target.yml b/Components/MapLibre/MapLibreShim/SupportingFiles/target.yml index 4e670d7d3..f5422822d 100644 --- a/Components/MapLibre/MapLibreShim/SupportingFiles/target.yml +++ b/Components/MapLibre/MapLibreShim/SupportingFiles/target.yml @@ -12,7 +12,7 @@ targets: base: PRODUCT_BUNDLE_IDENTIFIER: $(BASE_BUNDLE_IDENTIFIER).maplibre-shim GENERATE_INFOPLIST_FILE: YES - SWIFT_VERSION: 6.2 + SWIFT_VERSION: 6 SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 72d63b98e..f652e33e6 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -10047,7 +10047,7 @@ SKIP_INSTALL = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; @@ -10084,7 +10084,7 @@ PRODUCT_NAME = AccessibilityTests; SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = ElementX; }; @@ -10107,7 +10107,7 @@ PRODUCT_NAME = AccessibilityTests; SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = ElementX; }; @@ -10135,7 +10135,7 @@ SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h"; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -10156,7 +10156,7 @@ SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX"; }; @@ -10212,7 +10212,7 @@ SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_OBJC_BRIDGING_HEADER = "ElementX/SupportingFiles/ElementX-Bridging-Header.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h"; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -10247,7 +10247,7 @@ SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_OBJC_BRIDGING_HEADER = "ElementX/SupportingFiles/ElementX-Bridging-Header.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h"; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -10272,7 +10272,7 @@ SKIP_INSTALL = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; @@ -10294,7 +10294,7 @@ SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX"; }; @@ -10318,7 +10318,7 @@ PRODUCT_NAME = ShareExtension; SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -10498,7 +10498,7 @@ SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX"; }; @@ -10521,7 +10521,7 @@ PRODUCT_NAME = UITests; SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = ElementX; }; @@ -10547,7 +10547,7 @@ SKIP_INSTALL = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; @@ -10570,7 +10570,7 @@ PRODUCT_NAME = UITests; SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = ElementX; }; @@ -10598,7 +10598,7 @@ SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h"; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -10623,7 +10623,7 @@ SKIP_INSTALL = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; @@ -10643,7 +10643,7 @@ PRODUCT_NAME = IntegrationTests; SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = ElementX; }; @@ -10673,7 +10673,7 @@ PRODUCT_NAME = ShareExtension; SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -10692,7 +10692,7 @@ PRODUCT_NAME = IntegrationTests; SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = ElementX; }; @@ -10714,7 +10714,7 @@ SDKROOT = iphoneos; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_VERSION = 6.2; + SWIFT_VERSION = 6; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX"; }; diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 111a016b4..6b0d7d7be 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -723,7 +723,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg fatalError("User session not setup") } - Task { + Task { [self] in let credentials = SoftLogoutScreenCredentials(userID: userSession.clientProxy.userID, homeserverName: userSession.clientProxy.homeserver, userDisplayName: userSession.clientProxy.userProfilePublisher.value.displayName ?? "", @@ -744,7 +744,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg appHooks: appHooks, userIndicatorController: userIndicatorController) let coordinator = SoftLogoutScreenCoordinator(parameters: parameters) - self.softLogoutCoordinator = coordinator + softLogoutCoordinator = coordinator coordinator.actions .sink { [weak self] action in guard let self else { return } diff --git a/ElementX/Sources/Application/Windowing/WindowManager.swift b/ElementX/Sources/Application/Windowing/WindowManager.swift index 64cd9ee49..17a26113b 100644 --- a/ElementX/Sources/Application/Windowing/WindowManager.swift +++ b/ElementX/Sources/Application/Windowing/WindowManager.swift @@ -247,7 +247,7 @@ private extension UIWindowScene { self.sizeRestrictions?.maximumSize = size Task { - try await Task.sleep(for: .milliseconds(100)) + try? await Task.sleep(for: .milliseconds(100)) self.sizeRestrictions?.minimumSize = sizeRestrictions.minimumSize self.sizeRestrictions?.maximumSize = sizeRestrictions.maximumSize } diff --git a/ElementX/Sources/FlowCoordinators/RoomMembersFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomMembersFlowCoordinator.swift index aeb467e87..6f223d7c1 100644 --- a/ElementX/Sources/FlowCoordinators/RoomMembersFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomMembersFlowCoordinator.swift @@ -308,9 +308,9 @@ final class RoomMembersFlowCoordinator: FlowCoordinatorProtocol { // Replace the RoomMemberDetailsScreen without any animation. // If this pop and push happens before the previous navigation is completed it might break screen presentation logic - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) { - self.navigationStackCoordinator.pop(animated: false) - self.navigationStackCoordinator.push(coordinator, animated: false) { [weak self] in + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) { [self] in + navigationStackCoordinator.pop(animated: false) + navigationStackCoordinator.push(coordinator, animated: false) { [weak self] in self?.stateMachine.tryEvent(.dismissedUserProfile) } } diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index 5e3950b77..a9091c43f 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -371,7 +371,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { self.navigationTabCoordinator.setSheetCoordinator(nil) // The sheet needs to be dismissed before the alert can be shown - try await Task.sleep(for: .milliseconds(100)) + try? await Task.sleep(for: .milliseconds(100)) await self.runLogoutFlow() } case .forceLogout: diff --git a/ElementX/Sources/Mocks/SessionVerificationControllerProxyMock.swift b/ElementX/Sources/Mocks/SessionVerificationControllerProxyMock.swift index d69328ec1..aa10e298e 100644 --- a/ElementX/Sources/Mocks/SessionVerificationControllerProxyMock.swift +++ b/ElementX/Sources/Mocks/SessionVerificationControllerProxyMock.swift @@ -30,14 +30,14 @@ extension SessionVerificationControllerProxyMock { Task { guard let mock else { return } - try await Task.sleep(for: requestDelay) + try? await Task.sleep(for: requestDelay) mock.actions.send(.acceptedVerificationRequest) if otherDeviceStartsSasVerification { - try await Task.sleep(for: requestDelay) + try? await Task.sleep(for: requestDelay) mock.actions.send(.startedSasVerification) - try await Task.sleep(for: requestDelay) + try? await Task.sleep(for: requestDelay) mock.actions.send(.receivedVerificationData(emojis)) } } @@ -49,11 +49,11 @@ extension SessionVerificationControllerProxyMock { Task { guard let mock else { return } - try await Task.sleep(for: requestDelay) + try? await Task.sleep(for: requestDelay) mock.actions.send(.startedSasVerification) Task { - try await Task.sleep(for: requestDelay) + try? await Task.sleep(for: requestDelay) mock.actions.send(.receivedVerificationData(emojis)) } } @@ -65,7 +65,7 @@ extension SessionVerificationControllerProxyMock { Task { guard let mock else { return } - try await Task.sleep(for: requestDelay) + try? await Task.sleep(for: requestDelay) mock.actions.send(.finished) } @@ -76,7 +76,7 @@ extension SessionVerificationControllerProxyMock { Task { guard let mock else { return } - try await Task.sleep(for: requestDelay) + try? await Task.sleep(for: requestDelay) mock.actions.send(.cancelled) } @@ -87,7 +87,7 @@ extension SessionVerificationControllerProxyMock { Task { guard let mock else { return } - try await Task.sleep(for: requestDelay) + try? await Task.sleep(for: requestDelay) mock.actions.send(.cancelled) } diff --git a/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift b/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift index 391637bdb..032a98761 100644 --- a/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift +++ b/ElementX/Sources/Other/HTMLParsing/ElementXAttributeScope.swift @@ -6,7 +6,7 @@ // Please see LICENSE files in the repository root for full details. // -import Foundation +import SwiftUI enum UserIDAttribute: AttributedStringKey { typealias Value = String diff --git a/ElementX/Sources/Other/UserIndicator/UserIndicatorController.swift b/ElementX/Sources/Other/UserIndicator/UserIndicatorController.swift index 93df1249a..2fa5dfe35 100644 --- a/ElementX/Sources/Other/UserIndicator/UserIndicatorController.swift +++ b/ElementX/Sources/Other/UserIndicator/UserIndicatorController.swift @@ -49,7 +49,7 @@ class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol delayedIndicators.insert(indicator.id) Task { - try await Task.sleep(for: .seconds(delay.seconds)) + try? await Task.sleep(for: .seconds(delay.seconds)) guard delayedIndicators.contains(indicator.id) else { return diff --git a/ElementX/Sources/Screens/CreatePollScreen/PollFormScreenViewModel.swift b/ElementX/Sources/Screens/CreatePollScreen/PollFormScreenViewModel.swift index acd569abe..8e84b48df 100644 --- a/ElementX/Sources/Screens/CreatePollScreen/PollFormScreenViewModel.swift +++ b/ElementX/Sources/Screens/CreatePollScreen/PollFormScreenViewModel.swift @@ -78,7 +78,7 @@ class PollFormScreenViewModel: PollFormScreenViewModelType, PollFormScreenViewMo case .deleteOption(let index): // fixes a crash that caused an index out of range when an option with the keyboard focus was deleted Task { - try await Task.sleep(for: .milliseconds(100)) + try? await Task.sleep(for: .milliseconds(100)) guard state.bindings.options.indices.contains(index) else { return } diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index f5738dd40..cefd04afb 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -283,13 +283,13 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol // perphery: ignore - used in release mode func presentCrashedLastRunAlert() { // Delay setting the alert otherwise it automatically gets dismissed. Same as the force logout one. - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.state.bindings.alertInfo = AlertInfo(id: UUID(), - title: L10n.crashDetectionDialogContent(InfoPlistReader.main.bundleDisplayName), - primaryButton: .init(title: L10n.actionNo, action: nil), - secondaryButton: .init(title: L10n.actionYes) { [weak self] in - self?.actionsSubject.send(.presentFeedbackScreen) - }) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in + state.bindings.alertInfo = AlertInfo(id: UUID(), + title: L10n.crashDetectionDialogContent(InfoPlistReader.main.bundleDisplayName), + primaryButton: .init(title: L10n.actionNo, action: nil), + secondaryButton: .init(title: L10n.actionYes) { [weak self] in + self?.actionsSubject.send(.presentFeedbackScreen) + }) } } @@ -401,7 +401,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol private static let leaveRoomLoadingID = "LeaveRoomLoading" private func startLeaveRoomProcess(roomID: String) { - Task { + Task { [self] in defer { userIndicatorController.retractIndicatorWithId(Self.leaveRoomLoadingID) } diff --git a/ElementX/Sources/Screens/KnockRequestsListScreen/KnockRequestsListScreenViewModel.swift b/ElementX/Sources/Screens/KnockRequestsListScreen/KnockRequestsListScreenViewModel.swift index b6fc728d1..7dbdd0108 100644 --- a/ElementX/Sources/Screens/KnockRequestsListScreen/KnockRequestsListScreenViewModel.swift +++ b/ElementX/Sources/Screens/KnockRequestsListScreen/KnockRequestsListScreenViewModel.swift @@ -88,7 +88,7 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn let eventID = request.eventID state.handledEventIDs.insert(eventID) - Task { + Task { [self] in switch await request.accept() { case .success: hideLoadingIndicator() @@ -110,7 +110,7 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn let eventID = request.eventID state.handledEventIDs.insert(eventID) - Task { + Task { [self] in switch await request.decline() { case .success: hideLoadingIndicator() @@ -132,7 +132,7 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn let eventID = request.eventID state.handledEventIDs.insert(eventID) - Task { + Task { [self] in switch await request.ban() { case .success: hideLoadingIndicator() @@ -155,7 +155,7 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn showLoadingIndicator(title: L10n.screenKnockRequestsListAcceptAllLoadingTitle) state.handledEventIDs.formUnion(Set(requests.map(\.eventID))) - Task { + Task { [self] in let failedIDs = await withTaskGroup(of: (String, Result).self) { group in for request in requests { group.addTask { diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift index bbee74233..765867bc1 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift @@ -217,7 +217,7 @@ private class ElementTextView: UITextView, PillAttachmentViewProviderDelegate { super.init(frame: .zero, textContainer: nil) // Avoid `Publishing changes from within view update` warnings - DispatchQueue.main.async { + DispatchQueue.main.async { [self] in presendCallback.wrappedValue = { [weak self] in self?.acceptCurrentSuggestion() } diff --git a/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/NotificationSettingsEditScreenViewModel.swift b/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/NotificationSettingsEditScreenViewModel.swift index 0e533eb99..c57a0f6bc 100644 --- a/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/NotificationSettingsEditScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/NotificationSettingsEditScreenViewModel.swift @@ -166,7 +166,7 @@ class NotificationSettingsEditScreenViewModel: NotificationSettingsEditScreenVie roomNotificationModeProxy = .mentionsAndKeywordsOnly } state.pendingMode = mode - Task { + Task { [self] in do { // On modern clients, we don't have different settings for encrypted and non-encrypted rooms. let isOneToOne = chatType == .oneToOneChat diff --git a/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenViewModel.swift b/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenViewModel.swift index ecd97644c..4701896b0 100644 --- a/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenViewModel.swift +++ b/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenViewModel.swift @@ -72,7 +72,7 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc .weakAssign(to: \.state.selectedSpaceRoomID, on: self) .store(in: &cancellables) - Task { + Task { [self] in if case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(spaceRoomListProxy.id) { // Required to listen for membership updates in the members flow await roomProxy.subscribeForUpdates() diff --git a/ElementX/SupportingFiles/target.yml b/ElementX/SupportingFiles/target.yml index 8fccc124d..f926a4d0d 100644 --- a/ElementX/SupportingFiles/target.yml +++ b/ElementX/SupportingFiles/target.yml @@ -149,7 +149,7 @@ targets: SWIFT_OBJC_INTERFACE_HEADER_NAME: GeneratedInterface-Swift.h PILLS_UT_TYPE_IDENTIFIER: $(BASE_BUNDLE_IDENTIFIER).pills DEVELOPMENT_ASSET_PATHS: Components/DevelopmentAssets/Media - SWIFT_VERSION: 6.2 + SWIFT_VERSION: 6 SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor OTHER_SWIFT_FLAGS: diff --git a/IntegrationTests/SupportingFiles/target.yml b/IntegrationTests/SupportingFiles/target.yml index cd5ea02da..574407c3f 100644 --- a/IntegrationTests/SupportingFiles/target.yml +++ b/IntegrationTests/SupportingFiles/target.yml @@ -47,7 +47,7 @@ targets: base: PRODUCT_NAME: IntegrationTests PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.integration.tests - SWIFT_VERSION: 6.2 + SWIFT_VERSION: 6 SWIFT_APPROACHABLE_CONCURRENCY: YES debug: release: diff --git a/NSE/SupportingFiles/target.yml b/NSE/SupportingFiles/target.yml index 1077b7194..d6b0b781d 100644 --- a/NSE/SupportingFiles/target.yml +++ b/NSE/SupportingFiles/target.yml @@ -77,7 +77,7 @@ targets: CURRENT_PROJECT_VERSION: $(CURRENT_PROJECT_VERSION) DEVELOPMENT_TEAM: $(DEVELOPMENT_TEAM) SWIFT_OBJC_INTERFACE_HEADER_NAME: GeneratedInterface-Swift.h - SWIFT_VERSION: 6.2 + SWIFT_VERSION: 6 SWIFT_APPROACHABLE_CONCURRENCY: YES OTHER_SWIFT_FLAGS: - "-DIS_NSE" diff --git a/PreviewTests/SupportingFiles/target.yml b/PreviewTests/SupportingFiles/target.yml index ae6173051..a1af51a06 100644 --- a/PreviewTests/SupportingFiles/target.yml +++ b/PreviewTests/SupportingFiles/target.yml @@ -43,7 +43,7 @@ targets: PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.preview.tests # Requires iOS 26 as the generated tests reference previews gated to the Observations-based observe(_:). IPHONEOS_DEPLOYMENT_TARGET: '26.0' - SWIFT_VERSION: 6.2 + SWIFT_VERSION: 6 SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor debug: diff --git a/ShareExtension/SupportingFiles/target.yml b/ShareExtension/SupportingFiles/target.yml index 9d8ec029c..e035a4ccf 100644 --- a/ShareExtension/SupportingFiles/target.yml +++ b/ShareExtension/SupportingFiles/target.yml @@ -78,7 +78,7 @@ targets: MARKETING_VERSION: $(MARKETING_VERSION) CURRENT_PROJECT_VERSION: $(CURRENT_PROJECT_VERSION) DEVELOPMENT_TEAM: $(DEVELOPMENT_TEAM) - SWIFT_VERSION: 6.2 + SWIFT_VERSION: 6 SWIFT_APPROACHABLE_CONCURRENCY: YES sources: diff --git a/Tools/Scripts/Templates/SimpleScreenExample/ElementX/TemplateScreenViewModel.swift b/Tools/Scripts/Templates/SimpleScreenExample/ElementX/TemplateScreenViewModel.swift index c33f8efc5..c1e99a51f 100644 --- a/Tools/Scripts/Templates/SimpleScreenExample/ElementX/TemplateScreenViewModel.swift +++ b/Tools/Scripts/Templates/SimpleScreenExample/ElementX/TemplateScreenViewModel.swift @@ -34,12 +34,12 @@ class TemplateScreenViewModel: TemplateScreenViewModelType, TemplateScreenViewMo MXLog.info("View model: composer text changed to: \(state.bindings.composerText)") case .incrementCounter: Task { - try await Task.sleep(for: .seconds(.random(in: 1.0...2.0))) + try? await Task.sleep(for: .seconds(.random(in: 1.0...2.0))) state.counter += 1 } case .decrementCounter: Task { - try await Task.sleep(for: .seconds(.random(in: 1.0...2.0))) + try? await Task.sleep(for: .seconds(.random(in: 1.0...2.0))) state.counter -= 1 } } diff --git a/UITests/SupportingFiles/target.yml b/UITests/SupportingFiles/target.yml index 9186c864c..e5f46f4b5 100644 --- a/UITests/SupportingFiles/target.yml +++ b/UITests/SupportingFiles/target.yml @@ -48,7 +48,7 @@ targets: base: PRODUCT_NAME: UITests PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.ui.tests - SWIFT_VERSION: 6.2 + SWIFT_VERSION: 6 SWIFT_APPROACHABLE_CONCURRENCY: YES configs: # This is required to remove the $(inherited) flags diff --git a/UnitTests/Sources/DeferredFulfillmentTests.swift b/UnitTests/Sources/DeferredFulfillmentTests.swift index b641190d3..64cd0d043 100644 --- a/UnitTests/Sources/DeferredFulfillmentTests.swift +++ b/UnitTests/Sources/DeferredFulfillmentTests.swift @@ -46,11 +46,12 @@ struct DeferredFulfillmentTests { let deferred = deferFulfillment(observable.observe(\.counter)) { $0 == newValue } // When that value is changed asynchronously. - Task { try await observable.setCounter(newValue, delay: .seconds(1)) } + let task = Task { try await observable.setCounter(newValue, delay: .seconds(1)) } #expect(observable.counter == 0) // Then the test should be fulfilled once the update has taken place. try await deferred.fulfill() + try await task.value #expect(observable.counter == newValue) } @@ -61,7 +62,7 @@ struct DeferredFulfillmentTests { let deferred = deferFulfillment(observable.observe(\.counter)) { $0 == finalValue } // When that value is changed asynchronously with some intermediate values before it is reached. - Task { + let task = Task { try await observable.setCounter(100, delay: .seconds(.random(in: 1.0...2.0))) try await observable.setCounter(250, delay: .seconds(.random(in: 1.0...2.0))) try await observable.setCounter(finalValue, delay: .seconds(.random(in: 1.0...2.0))) @@ -70,6 +71,7 @@ struct DeferredFulfillmentTests { // Then the test should be fulfilled once the expected update has taken place. try await deferred.fulfill() + try await task.value #expect(observable.counter == finalValue) } } diff --git a/UnitTests/Sources/UserProfileScreenViewModelTests.swift b/UnitTests/Sources/UserProfileScreenViewModelTests.swift index 6c17d0966..c41528c36 100644 --- a/UnitTests/Sources/UserProfileScreenViewModelTests.swift +++ b/UnitTests/Sources/UserProfileScreenViewModelTests.swift @@ -81,7 +81,7 @@ struct UserProfileScreenViewModelTests { let waitForMemberToLoad = deferFulfillment(context.observe(\.viewState.userProfile)) { $0 != nil } try await waitForMemberToLoad.fulfill() - let deferred = deferFulfillment(context.observe(\.viewState.bindings).compactMap(\.inviteConfirmationUser), timeout: .seconds(5)) { $0.isUnknown } + let deferred = deferFulfillment(context.observe(\.viewState.bindings.inviteConfirmationUser), timeout: .seconds(5)) { $0?.isUnknown == true } context.send(viewAction: .openDirectChat) try await deferred.fulfill() diff --git a/UnitTests/SupportingFiles/target.yml b/UnitTests/SupportingFiles/target.yml index 9c5e14d04..02cba0b06 100644 --- a/UnitTests/SupportingFiles/target.yml +++ b/UnitTests/SupportingFiles/target.yml @@ -47,7 +47,7 @@ targets: PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.unit.tests # Requires iOS 26 so tests exercise the Observations-based observe(_:) overload. IPHONEOS_DEPLOYMENT_TARGET: '26.0' - SWIFT_VERSION: 6.2 + SWIFT_VERSION: 6 SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor debug: