Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .github/actions/bootstrap/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,6 @@ runs:
steps:
- run: echo "IMAGE=${ImageOS}" >> $GITHUB_ENV
shell: bash
- run: echo "$HOME/.mint/bin" >> $GITHUB_PATH
shell: bash
- name: Cache Mint
uses: actions/cache@v4
id: mint-cache
with:
path: ~/.mint
key: ${{ env.IMAGE }}-mint-${{ hashFiles('**/Mintfile') }}
restore-keys: ${{ env.IMAGE }}-mint-
- uses: ./.github/actions/ruby-cache
- uses: ./.github/actions/xcode-cache
- run: ./Scripts/bootstrap.sh
Expand Down
2 changes: 0 additions & 2 deletions .github/actions/xcode-cache/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ runs:
steps:
- run: echo "IMAGE=${ImageOS}-${ImageVersion}" >> $GITHUB_ENV
shell: bash
- run: echo "$HOME/.mint/bin" >> $GITHUB_PATH
shell: bash
- uses: mikehardy/buildcache-action@v2
with:
cache_key: ${{ env.IMAGE }}-buildcache-
2 changes: 1 addition & 1 deletion .github/workflows/cron-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
INSTALL_ALLURE: true
INSTALL_YEETD: true
INSTALL_IPSW: true
SKIP_MINT_BOOTSTRAP: true
SKIP_SWIFT_BOOTSTRAP: true
- uses: ./.github/actions/setup-ios-runtime
if: ${{ matrix.setup_runtime }}
timeout-minutes: 60
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/smoke-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ jobs:
env:
INSTALL_ALLURE: true
INSTALL_YEETD: true
SKIP_MINT_BOOTSTRAP: true
SKIP_SWIFT_BOOTSTRAP: true
- name: Run UI Tests (Debug)
run: bundle exec fastlane test_e2e_mock device:"${{ env.IOS_SIMULATOR_DEVICE }}" batch:'${{ matrix.batch }}' test_without_building:true
timeout-minutes: 100
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sonar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- uses: ./.github/actions/bootstrap
env:
INSTALL_SONAR: true
SKIP_MINT_BOOTSTRAP: true
SKIP_SWIFT_BOOTSTRAP: true

- uses: actions/github-script@v6
id: get_pr_number
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Agents should optimize for API stability, backwards compatibility, accessibility
β€’ Xcode: 15.x or newer (Apple Silicon supported)
β€’ Platforms / deployment targets: Use the values set in Package.swift/podspecs; do not lower targets without approval
β€’ CI: GitHub Actions (assume PR validation for build + tests + lint)
β€’ Linters & docs: SwiftLint via Mint
β€’ Linters & docs: SwiftLint and SwiftFormat

### Project layout (high level)

Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### πŸ”„ Changed

# [4.89.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.89.0)
_September 22, 2025_

### βœ… Added
- Add `toolbarThemed(content:)` for creating custom views with themed navigation bar [#953](https://github.com/GetStream/stream-chat-swiftui/pull/953)
- Add support for downloading file attachments [#952](https://github.com/GetStream/stream-chat-swiftui/pull/952)
### 🐞 Fixed
- Fix updating back button tint with `ColorPalette.navigationBarTintColor` [#953](https://github.com/GetStream/stream-chat-swiftui/pull/953)
- Fix swipe to reply enabled when quoting a message is disabled [#977](https://github.com/GetStream/stream-chat-swiftui/pull/957)
- Fix composer not showing images in the composer when editing signed attachments [#956](https://github.com/GetStream/stream-chat-swiftui/pull/956)
- Fix replacing an image while editing a message not showing the new image in the message list [#956](https://github.com/GetStream/stream-chat-swiftui/pull/956)
- Improve precision when scrolling to the newest message with long text [#958](https://github.com/GetStream/stream-chat-swiftui/pull/958)
- Fix draft attachments being sent with local file urls to the server [#964](https://github.com/GetStream/stream-chat-swiftui/pull/964)
- Fix keyboard showing with attachment picker when editing a message [#965](https://github.com/GetStream/stream-chat-swiftui/pull/965)
- Fix race condition when clearing text in a regular TextField [#955](https://github.com/GetStream/stream-chat-swiftui/pull/955)
### πŸ”„ Changed
- Change the gallery header view to show the message timestamp instead of online status [#962](https://github.com/GetStream/stream-chat-swiftui/pull/962)

# [4.88.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.88.0)
_September 10, 2025_

Expand Down
3 changes: 2 additions & 1 deletion DemoAppSwiftUI/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ class AppDelegate: NSObject, UIApplicationDelegate {
skipEditedMessageLabel: { message in
message.extraData["ai_generated"]?.boolValue == true
},
draftMessagesEnabled: true
draftMessagesEnabled: true,
downloadFileAttachmentsEnabled: true
),
composerConfig: ComposerConfig(isVoiceRecordingEnabled: true)
)
Expand Down
6 changes: 3 additions & 3 deletions DemoAppSwiftUI/ChannelHeader/CustomChannelHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public struct CustomChannelHeader: ToolbarContent {
.frame(width: 24, height: 24)
.foregroundColor(Color.white)
.padding(.all, 8)
.background(colors.tintColor)
.background(colors.navigationBarTintColor)
.clipShape(Circle())
}
.accessibilityLabel(Text("New Channel"))
Expand Down Expand Up @@ -67,7 +67,7 @@ struct CustomChannelModifier: ChannelListHeaderViewModifier {
func body(content: Content) -> some View {
ZStack {
if #available(iOS 26, *) {
content.toolbar {
content.toolbarThemed {
CustomChannelHeader(
title: title,
currentUserController: chatClient.currentUserController(),
Expand All @@ -79,7 +79,7 @@ struct CustomChannelModifier: ChannelListHeaderViewModifier {
#endif
}
} else {
content.toolbar {
content.toolbarThemed {
CustomChannelHeader(
title: title,
currentUserController: chatClient.currentUserController(),
Expand Down
8 changes: 7 additions & 1 deletion DemoAppSwiftUI/ChannelHeader/NewChatView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,13 @@ struct NewChatView: View, KeyboardReadable {
Spacer()
}
}
.navigationTitle("New Chat")
.toolbarThemed {
ToolbarItem(placement: .principal) {
Text("New Chat")
.font(fonts.bodyBold)
.foregroundColor(Color(colors.navigationBarTitle))
}
}
.onReceive(keyboardWillChangePublisher) { visible in
keyboardShown = visible
}
Expand Down
2 changes: 1 addition & 1 deletion DemoAppSwiftUI/CreateGroupView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct CreateGroupView: View, KeyboardReadable {
}
.listStyle(.plain)
}
.toolbar(content: {
.toolbarThemed(content: {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink {
GroupNameView(
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ GEM
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.4.1)
rexml (3.4.2)
rouge (3.28.0)
rubocop (1.38.0)
json (~> 2.3)
Expand Down
4 changes: 3 additions & 1 deletion Githubfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
export ALLURECTL_VERSION='2.16.0'
export XCRESULTS_VERSION='1.19.1'
export YEETD_VERSION='1.0'
export MINT_VERSION='0.17.5'
export SONAR_VERSION='6.2.1.4610'
export IPSW_VERSION='3.1.592'
export INTERFACE_ANALYZER_VERSION='1.0.7'
export SWIFT_LINT_VERSION='0.55.1'
export SWIFT_FORMAT_VERSION='0.47.12'
export SWIFT_GEN_VERSION='6.5.1'
3 changes: 0 additions & 3 deletions Mintfile

This file was deleted.

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/GetStream/stream-chat-swift.git", from: "4.88.0")
.package(url: "https://github.com/GetStream/stream-chat-swift.git", from: "4.89.0")
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<p align="center">
<a href="https://sonarcloud.io/summary/new_code?id=GetStream_stream-chat-swiftui"><img src="https://sonarcloud.io/api/project_badges/measure?project=GetStream_stream-chat-swiftui&metric=coverage" /></a>

<img id="stream-chat-swiftui-label" alt="StreamChatSwiftUI" src="https://img.shields.io/badge/StreamChatSwiftUI-9.35%20MB-blue"/>
<img id="stream-chat-swiftui-label" alt="StreamChatSwiftUI" src="https://img.shields.io/badge/StreamChatSwiftUI-9.43%20MB-blue"/>
</p>

## SwiftUI StreamChat SDK
Expand Down
49 changes: 35 additions & 14 deletions Scripts/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
# shellcheck source=/dev/null
# Usage: ./bootstrap.sh
# This script will:
# - install Mint and bootstrap its dependencies
# - install Vale
# - install SwiftLint, SwiftFormat, SwiftGen
# - link git hooks
# - install allure dependencies if `INSTALL_ALLURE` environment variable is provided
# - install sonar-scanner if `INSTALL_SONAR` environment variable is provided
Expand All @@ -27,19 +26,41 @@ if [ "${GITHUB_ACTIONS:-}" != "true" ]; then
bundle exec lefthook install
fi

if [ "${SKIP_MINT_BOOTSTRAP:-}" != true ]; then
puts "Bootstrap Mint dependencies"
git clone https://github.com/yonaskolb/Mint.git fastlane/mint
root=$(pwd)
cd fastlane/mint
swift run mint install "yonaskolb/mint@${MINT_VERSION}"
cd $root
rm -rf fastlane/mint
mint bootstrap --link
if [ "${SKIP_SWIFT_BOOTSTRAP:-}" != true ]; then
puts "Install SwiftLint v${SWIFT_LINT_VERSION}"
DOWNLOAD_URL="https://github.com/realm/SwiftLint/releases/download/${SWIFT_LINT_VERSION}/SwiftLint.pkg"
DOWNLOAD_PATH="/tmp/SwiftLint-${SWIFT_LINT_VERSION}.pkg"
curl -sL "$DOWNLOAD_URL" -o "$DOWNLOAD_PATH"
sudo installer -pkg "$DOWNLOAD_PATH" -target /
swiftlint version

puts "Install SwiftFormat v${SWIFT_FORMAT_VERSION}"
DOWNLOAD_URL="https://github.com/nicklockwood/SwiftFormat/releases/download/${SWIFT_FORMAT_VERSION}/swiftformat.zip"
DOWNLOAD_PATH="/tmp/swiftformat-${SWIFT_FORMAT_VERSION}.zip"
BIN_PATH="/usr/local/bin/swiftformat"
brew uninstall swiftformat || true
curl -sL "$DOWNLOAD_URL" -o "$DOWNLOAD_PATH"
unzip -o "$DOWNLOAD_PATH" -d /tmp/swiftformat-${SWIFT_FORMAT_VERSION}
sudo mv /tmp/swiftformat-${SWIFT_FORMAT_VERSION}/swiftformat "$BIN_PATH"
sudo chmod +x "$BIN_PATH"
swiftformat --version

puts "Install SwiftGen v${SWIFT_GEN_VERSION}"
DOWNLOAD_URL="https://github.com/SwiftGen/SwiftGen/releases/download/${SWIFT_GEN_VERSION}/swiftgen-${SWIFT_GEN_VERSION}.zip"
DOWNLOAD_PATH="/tmp/swiftgen-${SWIFT_GEN_VERSION}.zip"
INSTALL_DIR="/usr/local/lib/swiftgen"
BIN_PATH="/usr/local/bin/swiftgen"
curl -sL "$DOWNLOAD_URL" -o "$DOWNLOAD_PATH"
sudo rm -rf "$INSTALL_DIR"
sudo mkdir -p "$INSTALL_DIR"
sudo unzip -o "$DOWNLOAD_PATH" -d "$INSTALL_DIR"
sudo sudo rm -f "$BIN_PATH"
sudo sudo ln -s "$INSTALL_DIR/bin/swiftgen" "$BIN_PATH"
swiftgen --version
fi

if [[ ${INSTALL_SONAR-default} == true ]]; then
puts "Install sonar scanner"
puts "Install sonar scanner v${SONAR_VERSION}"
DOWNLOAD_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_VERSION}-macosx-x64.zip"
curl -sL "${DOWNLOAD_URL}" -o ./fastlane/sonar.zip
cd fastlane
Expand All @@ -51,12 +72,12 @@ if [[ ${INSTALL_SONAR-default} == true ]]; then
fi

if [[ ${INSTALL_ALLURE-default} == true ]]; then
puts "Install allurectl"
puts "Install allurectl v${ALLURECTL_VERSION}"
DOWNLOAD_URL="https://github.com/allure-framework/allurectl/releases/download/${ALLURECTL_VERSION}/allurectl_darwin_amd64"
curl -sL "${DOWNLOAD_URL}" -o ./fastlane/allurectl
chmod +x ./fastlane/allurectl

puts "Install xcresults"
puts "Install xcresults v${XCRESULTS_VERSION}"
DOWNLOAD_URL="https://github.com/eroshenkoam/xcresults/releases/download/${XCRESULTS_VERSION}/xcresults"
curl -sL "${DOWNLOAD_URL}" -o ./fastlane/xcresults
chmod +x ./fastlane/xcresults
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ public struct DefaultChatChannelHeader<Factory: ViewFactory>: ToolbarContent {
)
.offset(x: 4)
}
.accentColor(colors.navigationBarTintColor)
.accessibilityLabel(Text(L10n.Channel.Header.Info.title))

NavigationLink(isActive: $isActive) {
Expand Down Expand Up @@ -111,8 +110,7 @@ public struct DefaultChannelHeaderModifier<Factory: ViewFactory>: ChatChannelHea
public func body(content: Content) -> some View {
if #available(iOS 26, *) {
content
.navigationBarBackground()
.toolbar {
.toolbarThemed {
DefaultChatChannelHeader(
factory: factory,
channel: channel,
Expand All @@ -125,8 +123,7 @@ public struct DefaultChannelHeaderModifier<Factory: ViewFactory>: ChatChannelHea
}
} else {
content
.navigationBarBackground()
.toolbar {
.toolbarThemed {
DefaultChatChannelHeader(
factory: factory,
channel: channel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ public struct DefaultMessageThreadHeader: ToolbarContent {
public struct DefaultMessageThreadHeaderModifier: MessageThreadHeaderViewModifier {
public func body(content: Content) -> some View {
content
.navigationBarBackground()
.toolbar {
.toolbarThemed {
DefaultMessageThreadHeader()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,7 @@ public struct ChatChannelInfoView<Factory: ViewFactory>: View, KeyboardReadable
}
}
}
.navigationBarBackground()
.toolbar {
.toolbarThemed {
ToolbarItem(placement: .principal) {
Group {
if viewModel.showSingleMemberDMView {
Expand Down Expand Up @@ -162,7 +161,6 @@ public struct ChatChannelInfoView<Factory: ViewFactory>: View, KeyboardReadable
.background(colors.tintColor)
.clipShape(Circle())
}
.accentColor(colors.navigationBarTintColor)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public struct FileAttachmentsView: View {
@Injected(\.colors) private var colors
@Injected(\.fonts) private var fonts
@Injected(\.images) private var images
@Injected(\.utils) private var utils

public init(channel: ChatChannel) {
_viewModel = StateObject(
Expand Down Expand Up @@ -45,11 +46,17 @@ public struct FileAttachmentsView: View {
Button {
viewModel.selectedAttachment = attachment
} label: {
FileAttachmentDisplayView(
url: url,
title: attachment.title ?? url.lastPathComponent,
sizeString: attachment.file.sizeString
)
HStack {
FileAttachmentDisplayView(
url: url,
title: attachment.title ?? url.lastPathComponent,
sizeString: attachment.file.sizeString
)
Spacer()
if utils.messageListConfig.downloadFileAttachmentsEnabled {
DownloadShareAttachmentView(attachment: attachment)
}
}
.onAppear {
viewModel.loadAdditionalAttachments(
after: monthlyDataSource,
Expand All @@ -59,6 +66,7 @@ public struct FileAttachmentsView: View {
.padding(.horizontal, 8)
.padding(.vertical)
}
.withDownloadingStateIndicator(for: attachment.downloadingState, url: attachment.assetURL)
.sheet(item: $viewModel.selectedAttachment) { item in
FileAttachmentPreview(title: item.title, url: item.assetURL)
}
Expand All @@ -70,14 +78,13 @@ public struct FileAttachmentsView: View {
}
}
}
.toolbar {
.toolbarThemed {
ToolbarItem(placement: .principal) {
Text(L10n.ChatInfo.Files.title)
.font(fonts.bodyBold)
.foregroundColor(Color(colors.navigationBarTitle))
}
}
.navigationBarBackground()
.navigationBarTitleDisplayMode(.inline)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,16 @@ class FileAttachmentsViewModel: ObservableObject, ChatMessageSearchControllerDel
loading = true
messageSearchController.search(query: query, completion: { [weak self] _ in
guard let self = self else { return }
self.updateAttachments()
withAnimation {
self.updateAttachments()
}
self.loading = false
})
}

private func updateAttachments() {
let messages = messageSearchController.messages
withAnimation {
self.attachmentsDataSource = self.loadAttachments(from: messages)
}
attachmentsDataSource = loadAttachments(from: messages)
}

private func loadAttachments(from messages: LazyCachedMapCollection<ChatMessage>) -> [MonthlyFileAttachments] {
Expand Down
Loading
Loading