From dcd769081b783bf1194da9502716ed2144d6fd74 Mon Sep 17 00:00:00 2001 From: Philipp Zagar Date: Sat, 21 Dec 2024 10:39:17 +0100 Subject: [PATCH 1/7] Swift 6 language mode --- Package.swift | 9 +++++---- Sources/SpeziChat/ChatView.swift | 14 +++++++++++++- .../Helpers/MessageInputViewHeightKey.swift | 2 +- .../Models/ChatEntity+HiddenMessageType.swift | 2 +- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Package.swift b/Package.swift index 3990699..d44ffbf 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.0 // // This source file is part of the Stanford Spezi open source project @@ -23,9 +23,10 @@ let package = Package( .library(name: "SpeziChat", targets: ["SpeziChat"]) ], dependencies: [ - .package(url: "https://github.com/StanfordSpezi/SpeziFoundation", from: "2.0.0-beta.3"), - .package(url: "https://github.com/StanfordSpezi/SpeziSpeech", from: "1.0.1"), - .package(url: "https://github.com/StanfordSpezi/SpeziViews", from: "1.3.1") + .package(url: "https://github.com/StanfordSpezi/SpeziFoundation", from: "2.0.0"), + // .package(url: "https://github.com/StanfordSpezi/SpeziSpeech", from: "1.1.0"), + .package(url: "https://github.com/StanfordSpezi/SpeziSpeech", branch: "feat/swift-6-language-mode"), + .package(url: "https://github.com/StanfordSpezi/SpeziViews", from: "1.8.0") ], targets: [ .target( diff --git a/Sources/SpeziChat/ChatView.swift b/Sources/SpeziChat/ChatView.swift index a6c70ee..8b4be5e 100644 --- a/Sources/SpeziChat/ChatView.swift +++ b/Sources/SpeziChat/ChatView.swift @@ -115,7 +115,19 @@ public struct ChatView: View { MessageInputView($chat, messagePlaceholder: messagePlaceholder, speechToText: speechToText) .disabled(disableInput) .onPreferenceChange(MessageInputViewHeightKey.self) { newValue in - messageInputHeight = newValue + 12 + // The `onPreferenceChange` view modfier now takes a `@Sendable` closure, therefore we cannot capture `@MainActor` isolated properties + // on the `View` directly anymore: https://developer.apple.com/documentation/swiftui/view/onpreferencechange(_:perform:)?changes=latest_minor + // However, as the `@Sendable` closure is still run on the MainActor (at least in my testing on 18.2 RC SDKs), we can use `MainActor.assumeIsolated` + // to avoid scheduling a `MainActor` `Task`, which could delay execution and cause unexpected UI behavior + if Thread.isMainThread { + MainActor.assumeIsolated { + messageInputHeight = newValue + 12 + } + } else { + Task { @MainActor in + messageInputHeight = newValue + 12 + } + } } } } diff --git a/Sources/SpeziChat/Helpers/MessageInputViewHeightKey.swift b/Sources/SpeziChat/Helpers/MessageInputViewHeightKey.swift index 03f4604..6d9fa9b 100644 --- a/Sources/SpeziChat/Helpers/MessageInputViewHeightKey.swift +++ b/Sources/SpeziChat/Helpers/MessageInputViewHeightKey.swift @@ -12,7 +12,7 @@ import SwiftUI /// A SwiftUI `PreferenceKey` that is used by the ``MessageInputView`` to propagate the height of the view up the view hierarchy. public struct MessageInputViewHeightKey: PreferenceKey { /// Default height of 0. - public static var defaultValue: CGFloat = 0 + public static let defaultValue: CGFloat = 0 /// Writes the received value to the `PreferenceKey`. diff --git a/Sources/SpeziChat/Models/ChatEntity+HiddenMessageType.swift b/Sources/SpeziChat/Models/ChatEntity+HiddenMessageType.swift index f277f13..89b0e38 100644 --- a/Sources/SpeziChat/Models/ChatEntity+HiddenMessageType.swift +++ b/Sources/SpeziChat/Models/ChatEntity+HiddenMessageType.swift @@ -18,7 +18,7 @@ extension ChatEntity { /// static let testType = HiddenMessageType(name: "testType") /// } /// ``` - public struct HiddenMessageType: Codable, Equatable, Hashable { + public struct HiddenMessageType: Codable, Equatable, Hashable, Sendable { /// Default ``HiddenMessageType``. public static let unknown = HiddenMessageType(name: "unknown") From 432ea6846edafa75ecff16fcc8f57084d7a210c9 Mon Sep 17 00:00:00 2001 From: Philipp Zagar Date: Sat, 21 Dec 2024 10:41:14 +0100 Subject: [PATCH 2/7] Update language version of UI test app --- Tests/UITests/UITests.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/UITests/UITests.xcodeproj/project.pbxproj b/Tests/UITests/UITests.xcodeproj/project.pbxproj index 809887a..739f44c 100644 --- a/Tests/UITests/UITests.xcodeproj/project.pbxproj +++ b/Tests/UITests/UITests.xcodeproj/project.pbxproj @@ -398,7 +398,7 @@ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Debug; @@ -437,7 +437,7 @@ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Release; @@ -584,7 +584,7 @@ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Test; From 56492ee04b06f65ea976329400f80bb9360b6f17 Mon Sep 17 00:00:00 2001 From: Philipp Zagar Date: Sat, 21 Dec 2024 11:07:07 +0100 Subject: [PATCH 3/7] Fix macOS isolation issues --- Sources/SpeziChat/ChatView+ShareSheet.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/SpeziChat/ChatView+ShareSheet.swift b/Sources/SpeziChat/ChatView+ShareSheet.swift index 99b95a7..b869ddd 100644 --- a/Sources/SpeziChat/ChatView+ShareSheet.swift +++ b/Sources/SpeziChat/ChatView+ShareSheet.swift @@ -41,6 +41,7 @@ extension ChatView { func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {} } #else + @MainActor struct ShareSheet { let sharedItem: Data let sharedItemType: ChatExportFormat From 71c452efe5fd9c13bbe47b46295a3952f6062a4f Mon Sep 17 00:00:00 2001 From: Philipp Zagar Date: Sat, 21 Dec 2024 11:23:02 +0100 Subject: [PATCH 4/7] UI test fixes --- Tests/UITests/TestAppUITests/TestAppUITests.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Tests/UITests/TestAppUITests/TestAppUITests.swift b/Tests/UITests/TestAppUITests/TestAppUITests.swift index 26ddcf3..2d4abd1 100644 --- a/Tests/UITests/TestAppUITests/TestAppUITests.swift +++ b/Tests/UITests/TestAppUITests/TestAppUITests.swift @@ -78,7 +78,13 @@ class TestAppUITests: XCTestCase { #endif sleep(3) - XCTAssert(app.buttons["Save"].waitForExistence(timeout: 2)) + + // Select "On My iPhone / iPad" directory, if necessary + let predicate = NSPredicate(format: "label BEGINSWITH[c] %@", "On My") + let matchingStaticTexts = app.staticTexts.containing(predicate) + matchingStaticTexts.allElementsBoundByIndex.first?.tap() + + XCTAssert(app.buttons["Save"].waitForExistence(timeout: 5)) app.buttons["Save"].tap() sleep(10) // Wait until file is saved From 60537e0ae856e460527ec9720832bf69d096c7b2 Mon Sep 17 00:00:00 2001 From: Philipp Zagar Date: Sat, 21 Dec 2024 11:33:38 +0100 Subject: [PATCH 5/7] macOS UI test fix --- Tests/UITests/TestAppUITests/TestAppUITests.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Tests/UITests/TestAppUITests/TestAppUITests.swift b/Tests/UITests/TestAppUITests/TestAppUITests.swift index 2d4abd1..f96da58 100644 --- a/Tests/UITests/TestAppUITests/TestAppUITests.swift +++ b/Tests/UITests/TestAppUITests/TestAppUITests.swift @@ -40,10 +40,13 @@ class TestAppUITests: XCTestCase { } func testChatExport() throws { // swiftlint:disable:this function_body_length + // Skip chat export test on visionOS and macOS #if os(visionOS) throw XCTSkip("VisionOS is unstable and are skipped at the moment") + #elseif os(macOS) + throw XCTSkip("macOS export to a file is not possible (regular sharesheet is)") #endif - + let app = XCUIApplication() let filesApp = XCUIApplication(bundleIdentifier: "com.apple.DocumentsApp") let maxRetries = 10 @@ -150,8 +153,12 @@ class TestAppUITests: XCTestCase { XCTAssert(app.staticTexts["SpeziChat"].waitForExistence(timeout: 1)) XCTAssert(app.buttons["Speaker strikethrough"].waitForExistence(timeout: 2)) XCTAssert(!app.buttons["Speaker"].waitForExistence(timeout: 2)) - + + #if os(macOS) + app.buttons["Speaker strikethrough"].firstMatch.tap() // on macOS, need to match for first speaker that is found + #else app.buttons["Speaker strikethrough"].tap() + #endif XCTAssert(!app.buttons["Speaker strikethrough"].waitForExistence(timeout: 2)) XCTAssert(app.buttons["Speaker"].waitForExistence(timeout: 2)) From cb128ed9ae67eaecdd57f1cfb0e85b88d08569ad Mon Sep 17 00:00:00 2001 From: Philipp Zagar Date: Sat, 21 Dec 2024 21:38:04 +0100 Subject: [PATCH 6/7] Revert to versioned Speech dependency --- Package.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index d44ffbf..1f945ea 100644 --- a/Package.swift +++ b/Package.swift @@ -24,8 +24,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/StanfordSpezi/SpeziFoundation", from: "2.0.0"), - // .package(url: "https://github.com/StanfordSpezi/SpeziSpeech", from: "1.1.0"), - .package(url: "https://github.com/StanfordSpezi/SpeziSpeech", branch: "feat/swift-6-language-mode"), + .package(url: "https://github.com/StanfordSpezi/SpeziSpeech", from: "1.1.1"), .package(url: "https://github.com/StanfordSpezi/SpeziViews", from: "1.8.0") ], targets: [ From 1bcf50fae5452d6b5e9a84ab0eb9a68dd3097cfb Mon Sep 17 00:00:00 2001 From: Philipp Zagar Date: Sat, 21 Dec 2024 21:44:04 +0100 Subject: [PATCH 7/7] Add docs --- Sources/SpeziChat/ChatView+ShareSheet.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SpeziChat/ChatView+ShareSheet.swift b/Sources/SpeziChat/ChatView+ShareSheet.swift index b869ddd..5e4715e 100644 --- a/Sources/SpeziChat/ChatView+ShareSheet.swift +++ b/Sources/SpeziChat/ChatView+ShareSheet.swift @@ -41,7 +41,7 @@ extension ChatView { func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {} } #else - @MainActor + @MainActor // On non-macOS SDKs, `UIViewControllerRepresentable` enforces the `MainActor` isolation struct ShareSheet { let sharedItem: Data let sharedItemType: ChatExportFormat