diff --git a/Makefile b/Makefile index 461beee44..03e9610b8 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,10 @@ update_dependencies: make update_nuke version=11.3.1 echo "👉 Updating SwiftyGif" make update_swiftygif version=5.4.2 + echo "👉 Updating MarkdownUI" + make update_markdown_ui version=2.4.1 + echo "👉 Updating Splash" + make update_splash version=0.16.0 update_nuke: check_version_parameter ./Scripts/updateDependency.sh $(version) Dependencies/Nuke Sources/StreamChatSwiftUI/StreamNuke Sources @@ -14,6 +18,14 @@ update_swiftygif: check_version_parameter ./Scripts/updateDependency.sh $(version) Dependencies/SwiftyGif Sources/StreamChatSwiftUI/StreamSwiftyGif SwiftyGif ./Scripts/removePublicDeclarations.sh Sources/StreamChatSwiftUI/StreamSwiftyGif +update_markdown_ui: check_version_parameter + ./Scripts/updateDependency.sh $(version) Dependencies/MarkdownUI Sources/StreamChatAISwiftUI/StreamMarkdownUI Sources + ./Scripts/removePublicDeclarations.sh Sources/StreamChatAISwiftUI/StreamMarkdownUI + +update_splash: check_version_parameter + ./Scripts/updateDependency.sh $(version) Dependencies/Splash Sources/StreamChatAISwiftUI/StreamSplash Sources + ./Scripts/removePublicDeclarations.sh Sources/StreamChatAISwiftUI/StreamSplash + check_version_parameter: @if [ "$(version)" = "" ]; then\ echo "❌ Missing version parameter"; \ diff --git a/Package.swift b/Package.swift index d61121b0e..12aeb27a7 100644 --- a/Package.swift +++ b/Package.swift @@ -1,29 +1,48 @@ // swift-tools-version:5.9 -import Foundation import PackageDescription let package = Package( name: "StreamChatSwiftUI", defaultLocalization: "en", platforms: [ - .iOS(.v14), .macOS(.v11) + .iOS(.v14), .macOS(.v11) // General platform settings ], products: [ .library( name: "StreamChatSwiftUI", targets: ["StreamChatSwiftUI"] + ), + .library( + name: "StreamChatAISwiftUI", + targets: ["StreamChatAISwiftUI"] ) ], dependencies: [ .package(url: "https://github.com/GetStream/stream-chat-swift.git", from: "4.66.0"), + .package(url: "https://github.com/JohnSundell/Splash.git", from: "0.16.0"), + .package(url: "https://github.com/gonzalezreal/swift-markdown-ui.git", from: "2.4.1") ], targets: [ .target( name: "StreamChatSwiftUI", - dependencies: [.product(name: "StreamChat", package: "stream-chat-swift")], + dependencies: [ + .product(name: "StreamChat", package: "stream-chat-swift") + ], exclude: ["README.md", "Info.plist", "Generated/L10n_template.stencil"], resources: [.process("Resources")] + ), + .target( + name: "StreamChatAISwiftUI", + dependencies: [ + .product(name: "Splash", package: "Splash"), + .product(name: "MarkdownUI", package: "swift-markdown-ui") + ], + exclude: [], + resources: [], + swiftSettings: [ + .define("PLATFORM_IOS15_OR_LATER") + ] ) ] ) @@ -32,4 +51,4 @@ let package = Package( package.dependencies.append( .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0") ) -#endif +#endif \ No newline at end of file diff --git a/Scripts/updateDependency.sh b/Scripts/updateDependency.sh index e71419e42..82cddecb0 100755 --- a/Scripts/updateDependency.sh +++ b/Scripts/updateDependency.sh @@ -24,6 +24,10 @@ if [[ $dependency_directory == *"Nuke"* ]]; then dependency_url="git@github.com:kean/Nuke.git" elif [[ $dependency_directory == *"SwiftyGif"* ]]; then dependency_url="git@github.com:kirualex/SwiftyGif.git" +elif [[ $dependency_directory == *"Splash"* ]]; then + dependency_url="git@github.com:JohnSundell/Splash.git" +elif [[ $dependency_directory == *"MarkdownUI"* ]]; then + dependency_url="git@github.com:gonzalezreal/swift-markdown-ui.git" else echo "→ Unknown dependency at $dependency_directory" exit 1 diff --git a/Sources/StreamChatAISwiftUI/CodeSyntaxHighlighter.swift b/Sources/StreamChatAISwiftUI/CodeSyntaxHighlighter.swift new file mode 100644 index 000000000..089636f35 --- /dev/null +++ b/Sources/StreamChatAISwiftUI/CodeSyntaxHighlighter.swift @@ -0,0 +1,30 @@ +// +// Copyright © 2024 Stream.io Inc. All rights reserved. +// + +internal import MarkdownUI +internal import Splash +import SwiftUI + +struct SplashCodeSyntaxHighlighter: CodeSyntaxHighlighter { + private let syntaxHighlighter: SyntaxHighlighter + + init(theme: Splash.Theme) { + self.syntaxHighlighter = SyntaxHighlighter(format: TextOutputFormat(theme: theme)) + } + + func highlightCode(_ content: String, language: String?) -> Text { + guard language != nil else { + return Text(content) + } + + return self.syntaxHighlighter.highlight(content) + } +} + +extension CodeSyntaxHighlighter where Self == SplashCodeSyntaxHighlighter { + static func splash(theme: Splash.Theme) -> Self { + SplashCodeSyntaxHighlighter(theme: theme) + } +} + diff --git a/Sources/StreamChatAISwiftUI/StreamAITextView.swift b/Sources/StreamChatAISwiftUI/StreamAITextView.swift new file mode 100644 index 000000000..9d1140f6e --- /dev/null +++ b/Sources/StreamChatAISwiftUI/StreamAITextView.swift @@ -0,0 +1,202 @@ +// +// Copyright © 2024 Stream.io Inc. All rights reserved. +// + +import Combine +import SwiftUI +internal import MarkdownUI +internal import Splash + +public struct StreamAITextView: View { + + var content: String + var isGenerating: Bool + + @State private var displayedText: String = "" + @State private var characterQueue: [Character] = [] + @State private var typingTimer: Timer? + @State private var chunkTimer: Timer? + @State var queue = DispatchQueue(label: "com.streamai.textview") + + public init(content: String, isGenerating: Bool) { + self.content = content + self.isGenerating = isGenerating + } + + public var body: some View { + Markdown(displayedText) + .markdownBlockStyle(\.codeBlock) { + codeBlock($0) + } + .markdownCodeSyntaxHighlighter(.splash(theme: self.theme)) + .onAppear { + if !isGenerating { + self.displayedText = content + return + } + if self.characterQueue.isEmpty { + self.characterQueue.append(contentsOf: content) + } + startTypingTimer() + } + .onDisappear { + typingTimer?.invalidate() + chunkTimer?.invalidate() + } + .onChange(of: characterQueue, perform: { newValue in + if characterQueue.isEmpty && !isGenerating { + self.displayedText = content + } + }) + .addChangeListeners( + content: content, + isGenerating: isGenerating, + onContentChange: { oldValue, newValue in + queue.sync { + if !isGenerating { + if oldValue.isEmpty && !newValue.isEmpty { + self.displayedText = newValue + } + return + } + let newChunk = getNewChunk(oldText: oldValue, newText: newValue) + self.characterQueue.append(contentsOf: newChunk) + } + }, + onIsGeneratingChange: { oldValue, newValue in + queue.sync { + if newValue { + if typingTimer == nil { + if self.characterQueue.isEmpty { + self.characterQueue.append(contentsOf: content) + } + startTypingTimer() + } + } else if oldValue && !newValue { + let newChunk = getNewChunk(oldText: displayedText, newText: content) + self.characterQueue.append(contentsOf: newChunk) + } + } + } + ) + } + + func getNewChunk(oldText: String, newText: String) -> String { + if newText.hasPrefix(oldText) { + // Old text is a prefix of new text + let startIndex = newText.index(newText.startIndex, offsetBy: oldText.count) + let newChunk = String(newText[startIndex...]) + return newChunk + } else { + // Find the longest common prefix + let commonPrefix = oldText.commonPrefix(with: newText) + let startIndex = newText.index(newText.startIndex, offsetBy: commonPrefix.count) + let newChunk = String(newText[startIndex...]) + return newChunk + } + } + + func startTypingTimer() { + typingTimer = Timer.scheduledTimer(withTimeInterval: 0.005, repeats: true) { _ in + guard !self.characterQueue.isEmpty else { return } + let nextCharacter = self.characterQueue.removeFirst() + self.displayedText.append(nextCharacter) + } + } + + @ViewBuilder + private func codeBlock(_ configuration: CodeBlockConfiguration) -> some View { + VStack(spacing: 0) { + HStack { + Text(configuration.language ?? "plain text") + .font(.system(.caption, design: .monospaced)) + .fontWeight(.semibold) + .foregroundColor(Color(theme.plainTextColor)) + Spacer() + + Image(systemName: "clipboard") + .onTapGesture { + copyToClipboard(configuration.content) + } + } + .padding(.horizontal) + .padding(.vertical, 8) + .background { + Color(theme.backgroundColor) + } + + Divider() + + ScrollView(.horizontal) { + configuration.label + .relativeLineSpacing(.em(0.25)) + .markdownTextStyle { + FontFamilyVariant(.monospaced) + FontSize(.em(0.85)) + } + .padding() + } + } + .background(Color(.secondarySystemBackground)) + .clipShape(RoundedRectangle(cornerRadius: 8)) + .markdownMargin(top: .zero, bottom: .em(0.8)) + } + + private var theme: Splash.Theme { + .sunset(withFont: .init(size: 16)) + } + + private func copyToClipboard(_ string: String) { + UIPasteboard.general.string = string + } +} + +struct StreamAITextViewChangeListeners: ViewModifier { + + @State var previousValue: String = "" + + var text: String + var isGenerating: Bool + + var onContentChange: (_ oldValue: String, _ newValue: String) -> Void + var onIsGeneratingChange: (_ oldValue: Bool, _ newValue: Bool) -> Void + + func body(content: Content) -> some View { + if #available(iOS 17.0, *) { + content + .onChange(of: text) { oldValue, newValue in + onContentChange(oldValue, newValue) + } + .onChange(of: isGenerating) { oldValue, newValue in + onIsGeneratingChange(oldValue, newValue) + } + } else { + content + .onChange(of: text) { newValue in + onContentChange(previousValue, newValue) + previousValue = newValue + } + .onChange(of: isGenerating) { newValue in + onIsGeneratingChange(!newValue, newValue) + } + } + } +} + +extension View { + func addChangeListeners( + content: String, + isGenerating: Bool, + onContentChange: @escaping (_ oldValue: String, _ newValue: String) -> Void, + onIsGeneratingChange: @escaping (_ oldValue: Bool, _ newValue: Bool) -> Void + ) -> some View { + self.modifier( + StreamAITextViewChangeListeners( + text: content, + isGenerating: isGenerating, + onContentChange: onContentChange, + onIsGeneratingChange: onIsGeneratingChange + ) + ) + } +} diff --git a/Sources/StreamChatAISwiftUI/StreamChatAISwiftUI.h b/Sources/StreamChatAISwiftUI/StreamChatAISwiftUI.h new file mode 100644 index 000000000..16dd6c66d --- /dev/null +++ b/Sources/StreamChatAISwiftUI/StreamChatAISwiftUI.h @@ -0,0 +1,15 @@ +// +// Copyright © 2024 Stream.io Inc. All rights reserved. +// + +#import + +//! Project version number for StreamChatAISwiftUI. +FOUNDATION_EXPORT double StreamChatAISwiftUIVersionNumber; + +//! Project version string for StreamChatAISwiftUI. +FOUNDATION_EXPORT const unsigned char StreamChatAISwiftUIVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Sources/StreamChatAISwiftUI/TextOutputFormat.swift b/Sources/StreamChatAISwiftUI/TextOutputFormat.swift new file mode 100644 index 000000000..ae9b9e3ec --- /dev/null +++ b/Sources/StreamChatAISwiftUI/TextOutputFormat.swift @@ -0,0 +1,49 @@ +// +// Copyright © 2024 Stream.io Inc. All rights reserved. +// + +internal import Splash +import SwiftUI + +struct TextOutputFormat: OutputFormat { + private let theme: Theme + + init(theme: Theme) { + self.theme = theme + } + + func makeBuilder() -> Builder { + Builder(theme: self.theme) + } +} + +extension TextOutputFormat { + struct Builder: OutputBuilder { + private let theme: Theme + private var accumulatedText: [Text] + + fileprivate init(theme: Theme) { + self.theme = theme + self.accumulatedText = [] + } + + mutating func addToken(_ token: String, ofType type: TokenType) { + let color = self.theme.tokenColors[type] ?? self.theme.plainTextColor + self.accumulatedText.append(Text(token).foregroundColor(.init(uiColor: color))) + } + + mutating func addPlainText(_ text: String) { + self.accumulatedText.append( + Text(text).foregroundColor(.init(uiColor: self.theme.plainTextColor)) + ) + } + + mutating func addWhitespace(_ whitespace: String) { + self.accumulatedText.append(Text(whitespace)) + } + + func build() -> Text { + self.accumulatedText.reduce(Text(""), +) + } + } +} diff --git a/Sources/StreamChatSwiftUI/StreamNuke/Nuke/Processing/ImageDecompression.swift b/Sources/StreamChatSwiftUI/StreamNuke/Nuke/Processing/ImageDecompression.swift index d9db20145..5b2870749 100644 --- a/Sources/StreamChatSwiftUI/StreamNuke/Nuke/Processing/ImageDecompression.swift +++ b/Sources/StreamChatSwiftUI/StreamNuke/Nuke/Processing/ImageDecompression.swift @@ -15,14 +15,10 @@ enum ImageDecompression { static var isDecompressionNeededAK = "ImageDecompressor.isDecompressionNeeded.AssociatedKey" static func setDecompressionNeeded(_ isDecompressionNeeded: Bool, for image: PlatformImage) { - withUnsafePointer(to: &isDecompressionNeededAK) { keyPointer in - objc_setAssociatedObject(image, keyPointer, isDecompressionNeeded, .OBJC_ASSOCIATION_RETAIN) - } + objc_setAssociatedObject(image, &isDecompressionNeededAK, isDecompressionNeeded, .OBJC_ASSOCIATION_RETAIN) } static func isDecompressionNeeded(for image: PlatformImage) -> Bool? { - return withUnsafePointer(to: &isDecompressionNeededAK) { keyPointer in - objc_getAssociatedObject(image, keyPointer) as? Bool - } + objc_getAssociatedObject(image, &isDecompressionNeededAK) as? Bool } } diff --git a/Sources/StreamChatSwiftUI/StreamNuke/NukeExtensions/ImageViewExtensions.swift b/Sources/StreamChatSwiftUI/StreamNuke/NukeExtensions/ImageViewExtensions.swift index 2e6aa4d88..bc2c08659 100644 --- a/Sources/StreamChatSwiftUI/StreamNuke/NukeExtensions/ImageViewExtensions.swift +++ b/Sources/StreamChatSwiftUI/StreamNuke/NukeExtensions/ImageViewExtensions.swift @@ -172,16 +172,11 @@ private final class ImageViewController { // Lazily create a controller for a given view and associate it with a view. static func controller(for view: ImageDisplayingView) -> ImageViewController { - if let controller = withUnsafePointer(to: &ImageViewController.controllerAK, { keyPointer in - objc_getAssociatedObject(view, keyPointer) as? ImageViewController - }) { + if let controller = objc_getAssociatedObject(view, &ImageViewController.controllerAK) as? ImageViewController { return controller } - let controller = ImageViewController(view: view) - withUnsafePointer(to: &ImageViewController.controllerAK) { keyPointer in - objc_setAssociatedObject(view, keyPointer, controller, .OBJC_ASSOCIATION_RETAIN) - } + objc_setAssociatedObject(view, &ImageViewController.controllerAK, controller, .OBJC_ASSOCIATION_RETAIN) return controller } diff --git a/Sources/StreamChatSwiftUI/StreamNuke/NukeUI/LazyImage.swift b/Sources/StreamChatSwiftUI/StreamNuke/NukeUI/LazyImage.swift index 37fb2c32e..ad8922477 100644 --- a/Sources/StreamChatSwiftUI/StreamNuke/NukeUI/LazyImage.swift +++ b/Sources/StreamChatSwiftUI/StreamNuke/NukeUI/LazyImage.swift @@ -3,9 +3,15 @@ // Copyright (c) 2015-2021 Alexander Grebenyuk (github.com/kean). import Foundation + import SwiftUI import Combine + + + + + private struct HashableRequest: Hashable { let request: ImageRequest diff --git a/StreamChatAISwiftUITests/StreamChatAISwiftUITests.swift b/StreamChatAISwiftUITests/StreamChatAISwiftUITests.swift new file mode 100644 index 000000000..7c20421bc --- /dev/null +++ b/StreamChatAISwiftUITests/StreamChatAISwiftUITests.swift @@ -0,0 +1,33 @@ +// +// Copyright © 2024 Stream.io Inc. All rights reserved. +// + +import XCTest +@testable import StreamChatAISwiftUI + +final class StreamChatAISwiftUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/StreamChatSwiftUI.xcodeproj/project.pbxproj b/StreamChatSwiftUI.xcodeproj/project.pbxproj index 09620d43e..d191e0722 100644 --- a/StreamChatSwiftUI.xcodeproj/project.pbxproj +++ b/StreamChatSwiftUI.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 55; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -326,6 +326,7 @@ 846B8E2C2C5B8117006A6249 /* BlockedUsersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846B8E2B2C5B8117006A6249 /* BlockedUsersView.swift */; }; 846B8E2E2C5B8130006A6249 /* BlockedUsersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846B8E2D2C5B8130006A6249 /* BlockedUsersViewModel.swift */; }; 846D6564279FF0800094B36E /* ReactionUserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846D6563279FF0800094B36E /* ReactionUserView.swift */; }; + 846FC1BC2CF0DBC30087C9CD /* StreamChatAISwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846FC1B42CF0DBC30087C9CD /* StreamChatAISwiftUI.framework */; }; 847110B628611033004A46D6 /* MessageActions_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840008BA27E8D64A00282D88 /* MessageActions_Tests.swift */; }; 847305BB28241D8D004AC770 /* ChatChannelHeader_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847305BA28241D8D004AC770 /* ChatChannelHeader_Tests.swift */; }; 847305BD28243D25004AC770 /* WebView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847305BC28243D25004AC770 /* WebView_Tests.swift */; }; @@ -555,6 +556,13 @@ remoteGlobalIDString = 8465FBB42746873A00AF091E; remoteInfo = StreamChatSwiftUI; }; + 846FC1BD2CF0DBC30087C9CD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8465FBAC2746873A00AF091E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 846FC1B32CF0DBC20087C9CD; + remoteInfo = StreamChatAISwiftUI; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -913,6 +921,8 @@ 846B8E2B2C5B8117006A6249 /* BlockedUsersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersView.swift; sourceTree = ""; }; 846B8E2D2C5B8130006A6249 /* BlockedUsersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersViewModel.swift; sourceTree = ""; }; 846D6563279FF0800094B36E /* ReactionUserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionUserView.swift; sourceTree = ""; }; + 846FC1B42CF0DBC30087C9CD /* StreamChatAISwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = StreamChatAISwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 846FC1BB2CF0DBC30087C9CD /* StreamChatAISwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StreamChatAISwiftUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 847305BA28241D8D004AC770 /* ChatChannelHeader_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatChannelHeader_Tests.swift; sourceTree = ""; }; 847305BC28243D25004AC770 /* WebView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView_Tests.swift; sourceTree = ""; }; 847305BE28243DF9004AC770 /* mock.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = mock.html; sourceTree = ""; }; @@ -1116,6 +1126,21 @@ C14A465A284665B100EF498E /* SDKIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKIdentifier.swift; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 846FC1C92CF0DBC30087C9CD /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + publicHeaders = ( + StreamChatAISwiftUI.h, + ); + target = 846FC1B32CF0DBC20087C9CD /* StreamChatAISwiftUI */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 846FC1B52CF0DBC30087C9CD /* StreamChatAISwiftUI */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (846FC1C92CF0DBC30087C9CD /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = StreamChatAISwiftUI; sourceTree = ""; }; + 846FC1BF2CF0DBC30087C9CD /* StreamChatAISwiftUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = StreamChatAISwiftUITests; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 8400A34E282E6BE30067D3A0 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -1166,6 +1191,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 846FC1B12CF0DBC20087C9CD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 846FC1B82CF0DBC30087C9CD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 846FC1BC2CF0DBC30087C9CD /* StreamChatAISwiftUI.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -1557,6 +1597,7 @@ 8465FCBD27468B6900AF091E /* DemoAppSwiftUI */, 8402EAC4282BF69900CCA696 /* StreamChatSwiftUITestsApp */, 8400A352282E6BE30067D3A0 /* StreamChatSwiftUITestsAppTests */, + 846FC1BF2CF0DBC30087C9CD /* StreamChatAISwiftUITests */, 8465FCE5274695B400AF091E /* Frameworks */, ); sourceTree = ""; @@ -1569,6 +1610,8 @@ 8465FCBC27468B6900AF091E /* DemoAppSwiftUI.app */, 8402EAC3282BF69900CCA696 /* StreamChatSwiftUITestsApp.app */, 8400A351282E6BE30067D3A0 /* StreamChatSwiftUITestsAppTests.xctest */, + 846FC1B42CF0DBC30087C9CD /* StreamChatAISwiftUI.framework */, + 846FC1BB2CF0DBC30087C9CD /* StreamChatAISwiftUITests.xctest */, ); name = Products; path = ..; @@ -1578,6 +1621,7 @@ isa = PBXGroup; children = ( 8465FCEB2746A95600AF091E /* StreamChatSwiftUI */, + 846FC1B52CF0DBC30087C9CD /* StreamChatAISwiftUI */, ); path = Sources; sourceTree = ""; @@ -2285,6 +2329,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 846FC1AF2CF0DBC20087C9CD /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -2403,6 +2454,52 @@ productReference = 8465FCBC27468B6900AF091E /* DemoAppSwiftUI.app */; productType = "com.apple.product-type.application"; }; + 846FC1B32CF0DBC20087C9CD /* StreamChatAISwiftUI */ = { + isa = PBXNativeTarget; + buildConfigurationList = 846FC1CA2CF0DBC30087C9CD /* Build configuration list for PBXNativeTarget "StreamChatAISwiftUI" */; + buildPhases = ( + 846FC1AF2CF0DBC20087C9CD /* Headers */, + 846FC1B02CF0DBC20087C9CD /* Sources */, + 846FC1B12CF0DBC20087C9CD /* Frameworks */, + 846FC1B22CF0DBC20087C9CD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 846FC1B52CF0DBC30087C9CD /* StreamChatAISwiftUI */, + ); + name = StreamChatAISwiftUI; + packageProductDependencies = ( + ); + productName = StreamChatAISwiftUI; + productReference = 846FC1B42CF0DBC30087C9CD /* StreamChatAISwiftUI.framework */; + productType = "com.apple.product-type.framework"; + }; + 846FC1BA2CF0DBC30087C9CD /* StreamChatAISwiftUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 846FC1CB2CF0DBC30087C9CD /* Build configuration list for PBXNativeTarget "StreamChatAISwiftUITests" */; + buildPhases = ( + 846FC1B72CF0DBC30087C9CD /* Sources */, + 846FC1B82CF0DBC30087C9CD /* Frameworks */, + 846FC1B92CF0DBC30087C9CD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 846FC1BE2CF0DBC30087C9CD /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 846FC1BF2CF0DBC30087C9CD /* StreamChatAISwiftUITests */, + ); + name = StreamChatAISwiftUITests; + packageProductDependencies = ( + ); + productName = StreamChatAISwiftUITests; + productReference = 846FC1BB2CF0DBC30087C9CD /* StreamChatAISwiftUITests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -2410,7 +2507,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1330; + LastSwiftUpdateCheck = 1610; LastUpgradeCheck = 1330; TargetAttributes = { 8400A350282E6BE30067D3A0 = { @@ -2431,6 +2528,12 @@ 8465FCBB27468B6900AF091E = { CreatedOnToolsVersion = 13.1; }; + 846FC1B32CF0DBC20087C9CD = { + CreatedOnToolsVersion = 16.1; + }; + 846FC1BA2CF0DBC30087C9CD = { + CreatedOnToolsVersion = 16.1; + }; }; }; buildConfigurationList = 8465FBAF2746873A00AF091E /* Build configuration list for PBXProject "StreamChatSwiftUI" */; @@ -2457,6 +2560,8 @@ 8465FCBB27468B6900AF091E /* DemoAppSwiftUI */, 8402EAC2282BF69900CCA696 /* StreamChatSwiftUITestsApp */, 8400A350282E6BE30067D3A0 /* StreamChatSwiftUITestsAppTests */, + 846FC1B32CF0DBC20087C9CD /* StreamChatAISwiftUI */, + 846FC1BA2CF0DBC30087C9CD /* StreamChatAISwiftUITests */, ); }; /* End PBXProject section */ @@ -2509,6 +2614,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 846FC1B22CF0DBC20087C9CD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 846FC1B92CF0DBC30087C9CD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -3056,6 +3175,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 846FC1B02CF0DBC20087C9CD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 846FC1B72CF0DBC30087C9CD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -3084,6 +3217,11 @@ target = 8465FBB42746873A00AF091E /* StreamChatSwiftUI */; targetProxy = 8465FCE8274695B400AF091E /* PBXContainerItemProxy */; }; + 846FC1BE2CF0DBC30087C9CD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 846FC1B32CF0DBC20087C9CD /* StreamChatAISwiftUI */; + targetProxy = 846FC1BD2CF0DBC30087C9CD /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -3726,6 +3864,191 @@ }; name = Release; }; + 846FC1C32CF0DBC30087C9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = EHV7XZLAHA; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.getstream.StreamChatAISwiftUI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 846FC1C42CF0DBC30087C9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = EHV7XZLAHA; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.getstream.StreamChatAISwiftUI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 846FC1C52CF0DBC30087C9CD /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = EHV7XZLAHA; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.getstream.StreamChatAISwiftUI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Profile; + }; + 846FC1C62CF0DBC30087C9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = EHV7XZLAHA; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.getstream.StreamChatAISwiftUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 846FC1C72CF0DBC30087C9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = EHV7XZLAHA; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.getstream.StreamChatAISwiftUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 846FC1C82CF0DBC30087C9CD /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = EHV7XZLAHA; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.getstream.StreamChatAISwiftUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Profile; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -3789,6 +4112,26 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 846FC1CA2CF0DBC30087C9CD /* Build configuration list for PBXNativeTarget "StreamChatAISwiftUI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 846FC1C32CF0DBC30087C9CD /* Debug */, + 846FC1C42CF0DBC30087C9CD /* Release */, + 846FC1C52CF0DBC30087C9CD /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 846FC1CB2CF0DBC30087C9CD /* Build configuration list for PBXNativeTarget "StreamChatAISwiftUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 846FC1C62CF0DBC30087C9CD /* Debug */, + 846FC1C72CF0DBC30087C9CD /* Release */, + 846FC1C82CF0DBC30087C9CD /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/StreamChatSwiftUI.xcodeproj/xcuserdata/martinmitrevski.xcuserdatad/xcschemes/xcschememanagement.plist b/StreamChatSwiftUI.xcodeproj/xcuserdata/martinmitrevski.xcuserdatad/xcschemes/xcschememanagement.plist index 564e79e93..b70bdbadb 100644 --- a/StreamChatSwiftUI.xcodeproj/xcuserdata/martinmitrevski.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/StreamChatSwiftUI.xcodeproj/xcuserdata/martinmitrevski.xcuserdatad/xcschemes/xcschememanagement.plist @@ -48,6 +48,11 @@ isShown + orderHint + 3 + + StreamChatAISwiftUI.xcscheme_^#shared#^_ + orderHint 4