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
8 changes: 6 additions & 2 deletions .github/workflows/sdk-size-metrics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
runs-on: macos-15
env:
GITHUB_TOKEN: '${{ secrets.CI_BOT_GITHUB_TOKEN }}'
GITHUB_PR_NUM: ${{ github.event.pull_request.number }}
steps:
- name: Connect Bot
uses: webfactory/ssh-agent@v0.7.0
Expand All @@ -28,10 +29,13 @@ jobs:

- uses: ./.github/actions/bootstrap

- name: Run SDK Size Metrics
- name: Run General SDK Size Metrics
run: bundle exec fastlane show_frameworks_sizes
timeout-minutes: 30
env:
GITHUB_PR_NUM: ${{ github.event.pull_request.number }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APPSTORE_API_KEY: ${{ secrets.APPSTORE_API_KEY }}

- name: Run Detailed SDK Size Metrics
run: bundle exec fastlane size_analyze
timeout-minutes: 30
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ App Thinning Size Report.txt
app-thinning.plist
*.dmg
yeetd-normal.pkg
*LinkMap.txt

# VSCode
.vscode
Expand Down
2 changes: 1 addition & 1 deletion .spi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ builder:
- platform: ios
documentation_targets: [StreamChatSwiftUI]
scheme: StreamChatSwiftUI
swift_version: 5.9
swift_version: '6.1'
4 changes: 2 additions & 2 deletions .swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
--rules redundantRawValues
--rules redundantVoidReturnType
--rules semicolons
--rules sortedImports
--rules sortImports
--rules spaceAroundBraces
--rules spaceAroundBrackets
--rules spaceAroundComments
Expand Down Expand Up @@ -81,4 +81,4 @@
--wrapcollections before-first

# Exclude paths
--exclude Sources/StreamChatSwiftUI/Generated,Sources/StreamChatSwiftUI/StreamSwiftyGif,Sources/StreamChatSwiftUI/StreamNuke
--exclude Sources/StreamChatSwiftUI/Generated,Sources/StreamChatSwiftUI/StreamSwiftyGif,Sources/StreamChatSwiftUI/StreamNuke,vendor/bundle,Pods,spm_cache,derived_data,.build
5 changes: 1 addition & 4 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ excluded:
- Sources/StreamChatSwiftUI/Generated
- Sources/StreamChatSwiftUI/StreamSwiftyGif
- Sources/StreamChatSwiftUI/StreamNuke
- vendor/bundle

only_rules:
- attribute_name_spacing
Expand Down Expand Up @@ -43,7 +44,6 @@ only_rules:
- trailing_comma
- trailing_newline
- trailing_semicolon
- trailing_whitespace
- unneeded_break_in_switch
- unneeded_override
- unused_closure_parameter
Expand All @@ -55,8 +55,5 @@ only_rules:
multiline_arguments:
only_enforce_after_first_closure_on_first_line: true

trailing_whitespace:
ignores_empty_lines: true

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

### 🔄 Changed

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

### ✅ Added
- Add the `makeAttachmentTextView` method to ViewFactory [#1013](https://github.com/GetStream/stream-chat-swiftui/pull/1013)
- Allow dismissing commands overlay when tapping the message list [#1024](https://github.com/GetStream/stream-chat-swiftui/pull/1024)
- Allows dismissing the keyboard attachments picker when tapping the message list [#1024](https://github.com/GetStream/stream-chat-swiftui/pull/1024)
### 🐞 Fixed
- Fix composer not being locked after the channel was frozen [#1015](https://github.com/GetStream/stream-chat-swiftui/pull/1015)
- Fix `PollOptionAllVotesView` not updated on poll cast events [#1025](https://github.com/GetStream/stream-chat-swiftui/pull/1025)
- Fix action sheet not showing when discarding Poll creation on iOS 26 [#1027](https://github.com/GetStream/stream-chat-swiftui/pull/1027)

# [4.90.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.90.0)
_October 08, 2025_

Expand Down
19 changes: 12 additions & 7 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,11 @@ GEM
fastlane
pry
fastlane-plugin-sonarcloud_metric_kit (0.2.1)
fastlane-plugin-stream_actions (0.3.90)
fastlane-plugin-stream_actions (0.3.101)
xctest_list (= 1.2.1)
fastlane-plugin-versioning (0.7.1)
fastlane-plugin-xcsize (1.1.0)
xcsize (= 1.1.0)
fastlane-sirp (1.0.0)
sysrandom (~> 1.0)
ffi (1.17.2)
Expand Down Expand Up @@ -283,7 +285,7 @@ GEM
molinillo (0.8.0)
multi_json (1.17.0)
multipart-post (2.4.1)
mustermann (3.0.3)
mustermann (3.0.4)
ruby2_keywords (~> 0.0.1)
mutex_m (0.3.0)
nanaimo (0.4.0)
Expand Down Expand Up @@ -316,8 +318,8 @@ GEM
puma (6.6.1)
nio4r (~> 2.0)
racc (1.8.1)
rack (3.2.0)
rack-protection (4.1.1)
rack (3.2.3)
rack-protection (4.2.0)
base64 (>= 0.1.0)
logger (>= 1.6.0)
rack (>= 3.0.0, < 4)
Expand Down Expand Up @@ -372,11 +374,11 @@ GEM
simctl (1.6.10)
CFPropertyList
naturally
sinatra (4.1.1)
sinatra (4.2.0)
logger (>= 1.6.0)
mustermann (~> 3.0)
rack (>= 3.0.0, < 4)
rack-protection (= 4.1.1)
rack-protection (= 4.2.0)
rack-session (>= 2.0.0, < 3)
tilt (~> 2.0)
slather (2.8.5)
Expand Down Expand Up @@ -413,6 +415,8 @@ GEM
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
xcsize (1.1.0)
commander (>= 4.6, < 6.0)
xctest_list (1.2.1)

PLATFORMS
Expand All @@ -426,8 +430,9 @@ DEPENDENCIES
fastlane-plugin-create_xcframework
fastlane-plugin-lizard
fastlane-plugin-sonarcloud_metric_kit
fastlane-plugin-stream_actions (= 0.3.90)
fastlane-plugin-stream_actions (= 0.3.101)
fastlane-plugin-versioning
fastlane-plugin-xcsize (= 1.1.0)
json
lefthook
plist
Expand Down
4 changes: 1 addition & 3 deletions 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.90.0")
.package(url: "https://github.com/GetStream/stream-chat-swift.git", from: "4.91.0")
],
targets: [
.target(
Expand All @@ -28,8 +28,6 @@ let package = Package(
]
)

#if swift(>=5.6)
package.dependencies.append(
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0")
)
#endif
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.57%20MB-blue"/>
<img id="stream-chat-swiftui-label" alt="StreamChatSwiftUI" src="https://img.shields.io/badge/StreamChatSwiftUI-9.58%20MB-blue"/>
</p>

## SwiftUI StreamChat SDK
Expand Down
4 changes: 1 addition & 3 deletions Scripts/GenerateSPMFileLists.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func sourceFileList(at url: URL) -> [String] {
let basePathRange = path.range(of: url.path + "/")!
return String(path[basePathRange.upperBound...])
}
.filter { $0.hasSuffix("_Tests.swift") || $0.hasSuffix("_Mock.swift") || $0.contains("__Snapshots__")}
.filter { $0.hasSuffix("_Tests.swift") || $0.hasSuffix("_Mock.swift") || $0.contains("__Snapshots__") }

return sourceFiles
}
Expand All @@ -59,8 +59,6 @@ newGeneratedContent += "] }\n"

newGeneratedContent += "\n"



// StreamChatUI excluded source files
let streamChatUIExcludedFiles = sourceFileList(at: URL(string: "Sources/StreamChatUI")!)
newGeneratedContent += "var streamChatUIFilesExcluded: [String] { [\n"
Expand Down
25 changes: 13 additions & 12 deletions Sources/StreamChatSwiftUI/ChatChannel/ChatChannelView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ public struct ChatChannelView<Factory: ViewFactory>: View, KeyboardReadable {
},
onJumpToMessage: viewModel.jumpToMessage(messageId:)
)
.dismissKeyboardOnTap(enabled: true) {
hideComposerCommandsAndAttachmentsPicker()
}
.overlay(
viewModel.currentDateString != nil ?
factory.makeDateIndicatorView(dateString: viewModel.currentDateString!)
Expand All @@ -81,7 +84,9 @@ public struct ChatChannelView<Factory: ViewFactory>: View, KeyboardReadable {
} else {
ZStack {
factory.makeEmptyMessagesView(for: channel, colors: colors)
.dismissKeyboardOnTap(enabled: keyboardShown)
.dismissKeyboardOnTap(enabled: keyboardShown) {
hideComposerCommandsAndAttachmentsPicker()
}
if viewModel.shouldShowTypingIndicator {
factory.makeTypingIndicatorBottomView(
channel: channel,
Expand Down Expand Up @@ -183,13 +188,6 @@ public struct ChatChannelView<Factory: ViewFactory>: View, KeyboardReadable {
viewModel.reactionsShown = false
messageDisplayInfo = nil
}
.onChange(of: presentationMode.wrappedValue, perform: { newValue in
if newValue.isPresented == false {
viewModel.onViewDissappear()
} else {
viewModel.setActive()
}
})
.background(
Color(colors.background).background(
TabBarAccessor { _ in
Expand Down Expand Up @@ -220,10 +218,13 @@ public struct ChatChannelView<Factory: ViewFactory>: View, KeyboardReadable {
let bottomPadding = topVC()?.view.safeAreaInsets.bottom ?? 0
return bottomPadding
}
}

extension PresentationMode: Equatable {
public static func == (lhs: PresentationMode, rhs: PresentationMode) -> Bool {
lhs.isPresented == rhs.isPresented
private func hideComposerCommandsAndAttachmentsPicker() {
NotificationCenter.default.post(
name: .attachmentPickerHiddenNotification, object: nil
)
NotificationCenter.default.post(
name: .commandsOverlayHiddenNotification, object: nil
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public struct AttachmentPickerTypeView: View {
HStack(spacing: 16) {
switch pickerTypeState {
case let .expanded(attachmentPickerType):
if composerViewModel.channelController.channel?.canUploadFile == true {
if composerViewModel.channelController.channel?.canUploadFile == true && composerViewModel.isSendMessageEnabled {
PickerTypeButton(
pickerTypeState: $pickerTypeState,
pickerType: .media,
Expand All @@ -60,7 +60,7 @@ public struct AttachmentPickerTypeView: View {
.accessibilityIdentifier("PickerTypeButtonMedia")
}

if commandsAvailable {
if commandsAvailable && composerViewModel.isSendMessageEnabled {
PickerTypeButton(
pickerTypeState: $pickerTypeState,
pickerType: .instantCommands,
Expand All @@ -70,16 +70,18 @@ public struct AttachmentPickerTypeView: View {
.accessibilityIdentifier("PickerTypeButtonCommands")
}
case .collapsed:
Button {
withAnimation {
pickerTypeState = .expanded(.none)
if composerViewModel.isSendMessageEnabled {
Button {
withAnimation {
pickerTypeState = .expanded(.none)
}
} label: {
Image(uiImage: images.shrinkInputArrow)
.renderingMode(.template)
.foregroundColor(Color(colors.highlightedAccentBackground))
}
} label: {
Image(uiImage: images.shrinkInputArrow)
.renderingMode(.template)
.foregroundColor(Color(colors.highlightedAccentBackground))
.accessibilityIdentifier("PickerTypeButtonCollapsed")
}
.accessibilityIdentifier("PickerTypeButtonCollapsed")
}
}
.accessibilityElement(children: .contain)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import SwiftUI
public struct MessageComposerView<Factory: ViewFactory>: View, KeyboardReadable {
@Injected(\.colors) private var colors
@Injected(\.fonts) private var fonts
@Injected(\.utils) private var utils

// Initial popup size, before the keyboard is shown.
@State private var popupSize: CGFloat = 350
Expand Down Expand Up @@ -228,6 +229,18 @@ public struct MessageComposerView<Factory: ViewFactory>: View, KeyboardReadable
viewModel.updateDraftMessage(quotedMessage: quotedMessage)
}
})
.onReceive(NotificationCenter.default.publisher(for: .commandsOverlayHiddenNotification)) { _ in
guard utils.messageListConfig.hidesCommandsOverlayOnMessageListTap else {
return
}
viewModel.composerCommand = nil
}
.onReceive(NotificationCenter.default.publisher(for: .attachmentPickerHiddenNotification)) { _ in
guard utils.messageListConfig.hidesAttachmentsPickersOnMessageListTap else {
return
}
viewModel.pickerTypeState = .expanded(.none)
}
.accessibilityElement(children: .contain)
}
}
Expand Down Expand Up @@ -375,8 +388,8 @@ public struct ComposerInputView<Factory: ViewFactory>: View, KeyboardReadable {
text: $text,
height: $textHeight,
selectedRangeLocation: $selectedRangeLocation,
placeholder: isInCooldown ? L10n.Composer.Placeholder.slowMode : L10n.Composer.Placeholder.message,
editable: !isInCooldown,
placeholder: isInCooldown ? L10n.Composer.Placeholder.slowMode : (isChannelFrozen ? L10n.Composer.Placeholder.messageDisabled : L10n.Composer.Placeholder.message),
editable: !isInputDisabled,
maxMessageLength: maxMessageLength,
currentHeight: textFieldHeight
)
Expand Down Expand Up @@ -435,4 +448,22 @@ public struct ComposerInputView<Factory: ViewFactory>: View, KeyboardReadable {
private var isInCooldown: Bool {
cooldownDuration > 0
}

private var isChannelFrozen: Bool {
!viewModel.isSendMessageEnabled
}

private var isInputDisabled: Bool {
isInCooldown || isChannelFrozen
}
}

// MARK: - Notification Names

extension Notification.Name {
/// Notification sent when the attachments picker should be hidden.
static let attachmentPickerHiddenNotification = Notification.Name("attachmentPickerHiddenNotification")

/// Notification sent when the commands overlay should be hidden.
static let commandsOverlayHiddenNotification = Notification.Name("commandsOverlayHiddenNotification")
}
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,11 @@ open class MessageComposerViewModel: ObservableObject {
}
}

/// A Boolean value indicating whether sending message is enabled.
public var isSendMessageEnabled: Bool {
channelController.channel?.canSendMessage ?? true
}

public var sendButtonEnabled: Bool {
if let composerCommand = composerCommand,
let handler = commandsHandler.commandHandler(for: composerCommand) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public struct TrailingComposerView: View {

public var body: some View {
Group {
if viewModel.cooldownDuration == 0 {
if viewModel.cooldownDuration == 0 && viewModel.isSendMessageEnabled {
HStack(spacing: 16) {
SendMessageButton(
enabled: viewModel.sendButtonEnabled,
Expand All @@ -28,7 +28,7 @@ public struct TrailingComposerView: View {
}
}
.padding(.bottom, 8)
} else {
} else if viewModel.cooldownDuration > 0 {
SlowModeView(
cooldownDuration: viewModel.cooldownDuration
)
Expand Down
Loading
Loading