Fix Swift 6.4 warnings (#6240)

This commit is contained in:
Mauro
2026-10-01 10:55:19 +02:00
committed by GitHub
parent 99c0fa5729
commit 831234865b
28 changed files with 71 additions and 69 deletions
+1 -1
View File
@@ -279,7 +279,7 @@ Services = where product-level opinions live (Rust SDK stays spec-faithful).
## Concurrency & Actors ## 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. - **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. - Services + data-layer types: `nonisolated` + `Sendable` when background work need.
- Never `@unchecked Sendable` / `nonisolated(unsafe)`. Dev add these. - Never `@unchecked Sendable` / `nonisolated(unsafe)`. Dev add these.
@@ -36,7 +36,7 @@ targets:
base: base:
PRODUCT_NAME: AccessibilityTests PRODUCT_NAME: AccessibilityTests
PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.accessibility.tests PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.accessibility.tests
SWIFT_VERSION: 6.2 SWIFT_VERSION: 6
SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_APPROACHABLE_CONCURRENCY: YES
configs: configs:
# This is required to remove the $(inherited) flags # This is required to remove the $(inherited) flags
@@ -11,7 +11,7 @@ targets:
base: base:
PRODUCT_BUNDLE_IDENTIFIER: $(BASE_BUNDLE_IDENTIFIER).maplibre-interface PRODUCT_BUNDLE_IDENTIFIER: $(BASE_BUNDLE_IDENTIFIER).maplibre-interface
GENERATE_INFOPLIST_FILE: YES GENERATE_INFOPLIST_FILE: YES
SWIFT_VERSION: 6.2 SWIFT_VERSION: 6
SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_APPROACHABLE_CONCURRENCY: YES
SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor
@@ -12,7 +12,7 @@ targets:
base: base:
PRODUCT_BUNDLE_IDENTIFIER: $(BASE_BUNDLE_IDENTIFIER).maplibre-shim PRODUCT_BUNDLE_IDENTIFIER: $(BASE_BUNDLE_IDENTIFIER).maplibre-shim
GENERATE_INFOPLIST_FILE: YES GENERATE_INFOPLIST_FILE: YES
SWIFT_VERSION: 6.2 SWIFT_VERSION: 6
SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_APPROACHABLE_CONCURRENCY: YES
SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor
+20 -20
View File
@@ -10047,7 +10047,7 @@
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
@@ -10084,7 +10084,7 @@
PRODUCT_NAME = AccessibilityTests; PRODUCT_NAME = AccessibilityTests;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = ElementX; TEST_TARGET_NAME = ElementX;
}; };
@@ -10107,7 +10107,7 @@
PRODUCT_NAME = AccessibilityTests; PRODUCT_NAME = AccessibilityTests;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = ElementX; TEST_TARGET_NAME = ElementX;
}; };
@@ -10135,7 +10135,7 @@
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h";
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
@@ -10156,7 +10156,7 @@
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX";
}; };
@@ -10212,7 +10212,7 @@
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_OBJC_BRIDGING_HEADER = "ElementX/SupportingFiles/ElementX-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "ElementX/SupportingFiles/ElementX-Bridging-Header.h";
SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h";
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
@@ -10247,7 +10247,7 @@
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_OBJC_BRIDGING_HEADER = "ElementX/SupportingFiles/ElementX-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "ElementX/SupportingFiles/ElementX-Bridging-Header.h";
SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h";
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
@@ -10272,7 +10272,7 @@
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
@@ -10294,7 +10294,7 @@
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX";
}; };
@@ -10318,7 +10318,7 @@
PRODUCT_NAME = ShareExtension; PRODUCT_NAME = ShareExtension;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
@@ -10498,7 +10498,7 @@
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX";
}; };
@@ -10521,7 +10521,7 @@
PRODUCT_NAME = UITests; PRODUCT_NAME = UITests;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = ElementX; TEST_TARGET_NAME = ElementX;
}; };
@@ -10547,7 +10547,7 @@
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
@@ -10570,7 +10570,7 @@
PRODUCT_NAME = UITests; PRODUCT_NAME = UITests;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = ElementX; TEST_TARGET_NAME = ElementX;
}; };
@@ -10598,7 +10598,7 @@
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "GeneratedInterface-Swift.h";
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
@@ -10623,7 +10623,7 @@
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
@@ -10643,7 +10643,7 @@
PRODUCT_NAME = IntegrationTests; PRODUCT_NAME = IntegrationTests;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = ElementX; TEST_TARGET_NAME = ElementX;
}; };
@@ -10673,7 +10673,7 @@
PRODUCT_NAME = ShareExtension; PRODUCT_NAME = ShareExtension;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
@@ -10692,7 +10692,7 @@
PRODUCT_NAME = IntegrationTests; PRODUCT_NAME = IntegrationTests;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = ElementX; TEST_TARGET_NAME = ElementX;
}; };
@@ -10714,7 +10714,7 @@
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_VERSION = 6.2; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ElementX.app/ElementX";
}; };
@@ -723,7 +723,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
fatalError("User session not setup") fatalError("User session not setup")
} }
Task { Task { [self] in
let credentials = SoftLogoutScreenCredentials(userID: userSession.clientProxy.userID, let credentials = SoftLogoutScreenCredentials(userID: userSession.clientProxy.userID,
homeserverName: userSession.clientProxy.homeserver, homeserverName: userSession.clientProxy.homeserver,
userDisplayName: userSession.clientProxy.userProfilePublisher.value.displayName ?? "", userDisplayName: userSession.clientProxy.userProfilePublisher.value.displayName ?? "",
@@ -744,7 +744,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
appHooks: appHooks, appHooks: appHooks,
userIndicatorController: userIndicatorController) userIndicatorController: userIndicatorController)
let coordinator = SoftLogoutScreenCoordinator(parameters: parameters) let coordinator = SoftLogoutScreenCoordinator(parameters: parameters)
self.softLogoutCoordinator = coordinator softLogoutCoordinator = coordinator
coordinator.actions coordinator.actions
.sink { [weak self] action in .sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -247,7 +247,7 @@ private extension UIWindowScene {
self.sizeRestrictions?.maximumSize = size self.sizeRestrictions?.maximumSize = size
Task { Task {
try await Task.sleep(for: .milliseconds(100)) try? await Task.sleep(for: .milliseconds(100))
self.sizeRestrictions?.minimumSize = sizeRestrictions.minimumSize self.sizeRestrictions?.minimumSize = sizeRestrictions.minimumSize
self.sizeRestrictions?.maximumSize = sizeRestrictions.maximumSize self.sizeRestrictions?.maximumSize = sizeRestrictions.maximumSize
} }
@@ -308,9 +308,9 @@ final class RoomMembersFlowCoordinator: FlowCoordinatorProtocol {
// Replace the RoomMemberDetailsScreen without any animation. // Replace the RoomMemberDetailsScreen without any animation.
// If this pop and push happens before the previous navigation is completed it might break screen presentation logic // 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)) { DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) { [self] in
self.navigationStackCoordinator.pop(animated: false) navigationStackCoordinator.pop(animated: false)
self.navigationStackCoordinator.push(coordinator, animated: false) { [weak self] in navigationStackCoordinator.push(coordinator, animated: false) { [weak self] in
self?.stateMachine.tryEvent(.dismissedUserProfile) self?.stateMachine.tryEvent(.dismissedUserProfile)
} }
} }
@@ -371,7 +371,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
self.navigationTabCoordinator.setSheetCoordinator(nil) self.navigationTabCoordinator.setSheetCoordinator(nil)
// The sheet needs to be dismissed before the alert can be shown // 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() await self.runLogoutFlow()
} }
case .forceLogout: case .forceLogout:
@@ -30,14 +30,14 @@ extension SessionVerificationControllerProxyMock {
Task { Task {
guard let mock else { return } guard let mock else { return }
try await Task.sleep(for: requestDelay) try? await Task.sleep(for: requestDelay)
mock.actions.send(.acceptedVerificationRequest) mock.actions.send(.acceptedVerificationRequest)
if otherDeviceStartsSasVerification { if otherDeviceStartsSasVerification {
try await Task.sleep(for: requestDelay) try? await Task.sleep(for: requestDelay)
mock.actions.send(.startedSasVerification) mock.actions.send(.startedSasVerification)
try await Task.sleep(for: requestDelay) try? await Task.sleep(for: requestDelay)
mock.actions.send(.receivedVerificationData(emojis)) mock.actions.send(.receivedVerificationData(emojis))
} }
} }
@@ -49,11 +49,11 @@ extension SessionVerificationControllerProxyMock {
Task { Task {
guard let mock else { return } guard let mock else { return }
try await Task.sleep(for: requestDelay) try? await Task.sleep(for: requestDelay)
mock.actions.send(.startedSasVerification) mock.actions.send(.startedSasVerification)
Task { Task {
try await Task.sleep(for: requestDelay) try? await Task.sleep(for: requestDelay)
mock.actions.send(.receivedVerificationData(emojis)) mock.actions.send(.receivedVerificationData(emojis))
} }
} }
@@ -65,7 +65,7 @@ extension SessionVerificationControllerProxyMock {
Task { Task {
guard let mock else { return } guard let mock else { return }
try await Task.sleep(for: requestDelay) try? await Task.sleep(for: requestDelay)
mock.actions.send(.finished) mock.actions.send(.finished)
} }
@@ -76,7 +76,7 @@ extension SessionVerificationControllerProxyMock {
Task { Task {
guard let mock else { return } guard let mock else { return }
try await Task.sleep(for: requestDelay) try? await Task.sleep(for: requestDelay)
mock.actions.send(.cancelled) mock.actions.send(.cancelled)
} }
@@ -87,7 +87,7 @@ extension SessionVerificationControllerProxyMock {
Task { Task {
guard let mock else { return } guard let mock else { return }
try await Task.sleep(for: requestDelay) try? await Task.sleep(for: requestDelay)
mock.actions.send(.cancelled) mock.actions.send(.cancelled)
} }
@@ -6,7 +6,7 @@
// Please see LICENSE files in the repository root for full details. // Please see LICENSE files in the repository root for full details.
// //
import Foundation import SwiftUI
enum UserIDAttribute: AttributedStringKey { enum UserIDAttribute: AttributedStringKey {
typealias Value = String typealias Value = String
@@ -49,7 +49,7 @@ class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol
delayedIndicators.insert(indicator.id) delayedIndicators.insert(indicator.id)
Task { Task {
try await Task.sleep(for: .seconds(delay.seconds)) try? await Task.sleep(for: .seconds(delay.seconds))
guard delayedIndicators.contains(indicator.id) else { guard delayedIndicators.contains(indicator.id) else {
return return
@@ -78,7 +78,7 @@ class PollFormScreenViewModel: PollFormScreenViewModelType, PollFormScreenViewMo
case .deleteOption(let index): case .deleteOption(let index):
// fixes a crash that caused an index out of range when an option with the keyboard focus was deleted // fixes a crash that caused an index out of range when an option with the keyboard focus was deleted
Task { Task {
try await Task.sleep(for: .milliseconds(100)) try? await Task.sleep(for: .milliseconds(100))
guard state.bindings.options.indices.contains(index) else { guard state.bindings.options.indices.contains(index) else {
return return
} }
@@ -283,13 +283,13 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
// perphery: ignore - used in release mode // perphery: ignore - used in release mode
func presentCrashedLastRunAlert() { func presentCrashedLastRunAlert() {
// Delay setting the alert otherwise it automatically gets dismissed. Same as the force logout one. // Delay setting the alert otherwise it automatically gets dismissed. Same as the force logout one.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
self.state.bindings.alertInfo = AlertInfo(id: UUID(), state.bindings.alertInfo = AlertInfo(id: UUID(),
title: L10n.crashDetectionDialogContent(InfoPlistReader.main.bundleDisplayName), title: L10n.crashDetectionDialogContent(InfoPlistReader.main.bundleDisplayName),
primaryButton: .init(title: L10n.actionNo, action: nil), primaryButton: .init(title: L10n.actionNo, action: nil),
secondaryButton: .init(title: L10n.actionYes) { [weak self] in secondaryButton: .init(title: L10n.actionYes) { [weak self] in
self?.actionsSubject.send(.presentFeedbackScreen) self?.actionsSubject.send(.presentFeedbackScreen)
}) })
} }
} }
@@ -401,7 +401,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
private static let leaveRoomLoadingID = "LeaveRoomLoading" private static let leaveRoomLoadingID = "LeaveRoomLoading"
private func startLeaveRoomProcess(roomID: String) { private func startLeaveRoomProcess(roomID: String) {
Task { Task { [self] in
defer { defer {
userIndicatorController.retractIndicatorWithId(Self.leaveRoomLoadingID) userIndicatorController.retractIndicatorWithId(Self.leaveRoomLoadingID)
} }
@@ -88,7 +88,7 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
let eventID = request.eventID let eventID = request.eventID
state.handledEventIDs.insert(eventID) state.handledEventIDs.insert(eventID)
Task { Task { [self] in
switch await request.accept() { switch await request.accept() {
case .success: case .success:
hideLoadingIndicator() hideLoadingIndicator()
@@ -110,7 +110,7 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
let eventID = request.eventID let eventID = request.eventID
state.handledEventIDs.insert(eventID) state.handledEventIDs.insert(eventID)
Task { Task { [self] in
switch await request.decline() { switch await request.decline() {
case .success: case .success:
hideLoadingIndicator() hideLoadingIndicator()
@@ -132,7 +132,7 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
let eventID = request.eventID let eventID = request.eventID
state.handledEventIDs.insert(eventID) state.handledEventIDs.insert(eventID)
Task { Task { [self] in
switch await request.ban() { switch await request.ban() {
case .success: case .success:
hideLoadingIndicator() hideLoadingIndicator()
@@ -155,7 +155,7 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
showLoadingIndicator(title: L10n.screenKnockRequestsListAcceptAllLoadingTitle) showLoadingIndicator(title: L10n.screenKnockRequestsListAcceptAllLoadingTitle)
state.handledEventIDs.formUnion(Set(requests.map(\.eventID))) state.handledEventIDs.formUnion(Set(requests.map(\.eventID)))
Task { Task { [self] in
let failedIDs = await withTaskGroup(of: (String, Result<Void, KnockRequestProxyError>).self) { group in let failedIDs = await withTaskGroup(of: (String, Result<Void, KnockRequestProxyError>).self) { group in
for request in requests { for request in requests {
group.addTask { group.addTask {
@@ -217,7 +217,7 @@ private class ElementTextView: UITextView, PillAttachmentViewProviderDelegate {
super.init(frame: .zero, textContainer: nil) super.init(frame: .zero, textContainer: nil)
// Avoid `Publishing changes from within view update` warnings // Avoid `Publishing changes from within view update` warnings
DispatchQueue.main.async { DispatchQueue.main.async { [self] in
presendCallback.wrappedValue = { [weak self] in presendCallback.wrappedValue = { [weak self] in
self?.acceptCurrentSuggestion() self?.acceptCurrentSuggestion()
} }
@@ -166,7 +166,7 @@ class NotificationSettingsEditScreenViewModel: NotificationSettingsEditScreenVie
roomNotificationModeProxy = .mentionsAndKeywordsOnly roomNotificationModeProxy = .mentionsAndKeywordsOnly
} }
state.pendingMode = mode state.pendingMode = mode
Task { Task { [self] in
do { do {
// On modern clients, we don't have different settings for encrypted and non-encrypted rooms. // On modern clients, we don't have different settings for encrypted and non-encrypted rooms.
let isOneToOne = chatType == .oneToOneChat let isOneToOne = chatType == .oneToOneChat
@@ -72,7 +72,7 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
.weakAssign(to: \.state.selectedSpaceRoomID, on: self) .weakAssign(to: \.state.selectedSpaceRoomID, on: self)
.store(in: &cancellables) .store(in: &cancellables)
Task { Task { [self] in
if case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(spaceRoomListProxy.id) { if case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(spaceRoomListProxy.id) {
// Required to listen for membership updates in the members flow // Required to listen for membership updates in the members flow
await roomProxy.subscribeForUpdates() await roomProxy.subscribeForUpdates()
+1 -1
View File
@@ -149,7 +149,7 @@ targets:
SWIFT_OBJC_INTERFACE_HEADER_NAME: GeneratedInterface-Swift.h SWIFT_OBJC_INTERFACE_HEADER_NAME: GeneratedInterface-Swift.h
PILLS_UT_TYPE_IDENTIFIER: $(BASE_BUNDLE_IDENTIFIER).pills PILLS_UT_TYPE_IDENTIFIER: $(BASE_BUNDLE_IDENTIFIER).pills
DEVELOPMENT_ASSET_PATHS: Components/DevelopmentAssets/Media DEVELOPMENT_ASSET_PATHS: Components/DevelopmentAssets/Media
SWIFT_VERSION: 6.2 SWIFT_VERSION: 6
SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_APPROACHABLE_CONCURRENCY: YES
SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor
OTHER_SWIFT_FLAGS: OTHER_SWIFT_FLAGS:
+1 -1
View File
@@ -47,7 +47,7 @@ targets:
base: base:
PRODUCT_NAME: IntegrationTests PRODUCT_NAME: IntegrationTests
PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.integration.tests PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.integration.tests
SWIFT_VERSION: 6.2 SWIFT_VERSION: 6
SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_APPROACHABLE_CONCURRENCY: YES
debug: debug:
release: release:
+1 -1
View File
@@ -77,7 +77,7 @@ targets:
CURRENT_PROJECT_VERSION: $(CURRENT_PROJECT_VERSION) CURRENT_PROJECT_VERSION: $(CURRENT_PROJECT_VERSION)
DEVELOPMENT_TEAM: $(DEVELOPMENT_TEAM) DEVELOPMENT_TEAM: $(DEVELOPMENT_TEAM)
SWIFT_OBJC_INTERFACE_HEADER_NAME: GeneratedInterface-Swift.h SWIFT_OBJC_INTERFACE_HEADER_NAME: GeneratedInterface-Swift.h
SWIFT_VERSION: 6.2 SWIFT_VERSION: 6
SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_APPROACHABLE_CONCURRENCY: YES
OTHER_SWIFT_FLAGS: OTHER_SWIFT_FLAGS:
- "-DIS_NSE" - "-DIS_NSE"
+1 -1
View File
@@ -43,7 +43,7 @@ targets:
PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.preview.tests PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.preview.tests
# Requires iOS 26 as the generated tests reference previews gated to the Observations-based observe(_:). # Requires iOS 26 as the generated tests reference previews gated to the Observations-based observe(_:).
IPHONEOS_DEPLOYMENT_TARGET: '26.0' IPHONEOS_DEPLOYMENT_TARGET: '26.0'
SWIFT_VERSION: 6.2 SWIFT_VERSION: 6
SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_APPROACHABLE_CONCURRENCY: YES
SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor
debug: debug:
+1 -1
View File
@@ -78,7 +78,7 @@ targets:
MARKETING_VERSION: $(MARKETING_VERSION) MARKETING_VERSION: $(MARKETING_VERSION)
CURRENT_PROJECT_VERSION: $(CURRENT_PROJECT_VERSION) CURRENT_PROJECT_VERSION: $(CURRENT_PROJECT_VERSION)
DEVELOPMENT_TEAM: $(DEVELOPMENT_TEAM) DEVELOPMENT_TEAM: $(DEVELOPMENT_TEAM)
SWIFT_VERSION: 6.2 SWIFT_VERSION: 6
SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_APPROACHABLE_CONCURRENCY: YES
sources: sources:
@@ -34,12 +34,12 @@ class TemplateScreenViewModel: TemplateScreenViewModelType, TemplateScreenViewMo
MXLog.info("View model: composer text changed to: \(state.bindings.composerText)") MXLog.info("View model: composer text changed to: \(state.bindings.composerText)")
case .incrementCounter: case .incrementCounter:
Task { 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 state.counter += 1
} }
case .decrementCounter: case .decrementCounter:
Task { 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 state.counter -= 1
} }
} }
+1 -1
View File
@@ -48,7 +48,7 @@ targets:
base: base:
PRODUCT_NAME: UITests PRODUCT_NAME: UITests
PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.ui.tests PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.ui.tests
SWIFT_VERSION: 6.2 SWIFT_VERSION: 6
SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_APPROACHABLE_CONCURRENCY: YES
configs: configs:
# This is required to remove the $(inherited) flags # This is required to remove the $(inherited) flags
@@ -46,11 +46,12 @@ struct DeferredFulfillmentTests {
let deferred = deferFulfillment(observable.observe(\.counter)) { $0 == newValue } let deferred = deferFulfillment(observable.observe(\.counter)) { $0 == newValue }
// When that value is changed asynchronously. // 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) #expect(observable.counter == 0)
// Then the test should be fulfilled once the update has taken place. // Then the test should be fulfilled once the update has taken place.
try await deferred.fulfill() try await deferred.fulfill()
try await task.value
#expect(observable.counter == newValue) #expect(observable.counter == newValue)
} }
@@ -61,7 +62,7 @@ struct DeferredFulfillmentTests {
let deferred = deferFulfillment(observable.observe(\.counter)) { $0 == finalValue } let deferred = deferFulfillment(observable.observe(\.counter)) { $0 == finalValue }
// When that value is changed asynchronously with some intermediate values before it is reached. // 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(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(250, delay: .seconds(.random(in: 1.0...2.0)))
try await observable.setCounter(finalValue, 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. // Then the test should be fulfilled once the expected update has taken place.
try await deferred.fulfill() try await deferred.fulfill()
try await task.value
#expect(observable.counter == finalValue) #expect(observable.counter == finalValue)
} }
} }
@@ -81,7 +81,7 @@ struct UserProfileScreenViewModelTests {
let waitForMemberToLoad = deferFulfillment(context.observe(\.viewState.userProfile)) { $0 != nil } let waitForMemberToLoad = deferFulfillment(context.observe(\.viewState.userProfile)) { $0 != nil }
try await waitForMemberToLoad.fulfill() 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) context.send(viewAction: .openDirectChat)
try await deferred.fulfill() try await deferred.fulfill()
+1 -1
View File
@@ -47,7 +47,7 @@ targets:
PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.unit.tests PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.unit.tests
# Requires iOS 26 so tests exercise the Observations-based observe(_:) overload. # Requires iOS 26 so tests exercise the Observations-based observe(_:) overload.
IPHONEOS_DEPLOYMENT_TARGET: '26.0' IPHONEOS_DEPLOYMENT_TARGET: '26.0'
SWIFT_VERSION: 6.2 SWIFT_VERSION: 6
SWIFT_APPROACHABLE_CONCURRENCY: YES SWIFT_APPROACHABLE_CONCURRENCY: YES
SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor
debug: debug: