Conversation
π WalkthroughWalkthroughThis pull request systematically converts 40+ public and private struct types to final classes throughout the StreamChatSwiftUI codebase, changing their semantics from value types to reference types. The changes preserve existing properties and initializers while altering object identity, mutation, and allocation behavior. Changes
Estimated code review effortπ― 3 (Moderate) | β±οΈ ~25 minutes The changes are largely homogeneous (struct-to-class conversions), but span 40+ files across multiple domains with occasional logic additions (weak self patterns, equality operators, new properties). While individual file changes are straightforward, the breadth requires systematic verification of reference semantics implications, particularly for memory management and identity-based equality. Poem
π₯ Pre-merge checks | β 2 | β 1β Failed checks (1 warning)
β Passed checks (2 passed)
βοΈ Tip: You can configure your own custom pre-merge checks in the settings. β¨ Finishing touches
π§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Generated by π« Danger |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and canβt be posted inline due to platform limitations.
β οΈ Outside diff range comments (15)
Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollsConfig.swift (1)
56-58:β οΈ Potential issue | π΄ CriticalShared mutable singleton β mutating any default entry silently corrupts all others.
PollsEntryConfig.defaultis a single shared class instance. Because all properties arevar, any mutation (e.g.config.multipleAnswers.configurable = false) mutates the shared.defaultin-place, silently affecting everyPollsConfigthat was (or will be) created with defaults. Additionally,PollsConfig()assigns the same object to all five properties, so mutating one entry also mutates the other four.With structs this was safe because each assignment was a copy. With classes, you need either:
- A computed property / factory method that vends a fresh instance each time, or
- Immutable (
let) properties onPollsEntryConfigso the shared instance can't be corrupted.Option 1 (factory method β minimal diff):
Proposed fix
extension PollsEntryConfig { /// The default configuration for a poll entry. - public static let `default` = PollsEntryConfig(configurable: true, defaultValue: false) + public static func makeDefault() -> PollsEntryConfig { + PollsEntryConfig(configurable: true, defaultValue: false) + } }Then update the
PollsConfiginit defaults accordingly:public init( multipleAnswers: PollsEntryConfig = .makeDefault(), anonymousPoll: PollsEntryConfig = .makeDefault(), suggestAnOption: PollsEntryConfig = .makeDefault(), addComments: PollsEntryConfig = .makeDefault(), maxVotesPerPerson: PollsEntryConfig = .makeDefault() )Sources/StreamChatSwiftUI/ChatChannel/Reactions/MessageActions/MessageActionsViewModel.swift (1)
24-53:β οΈ Potential issue | π Major
@Publishedarray of reference types won't notify on in-place property mutations.Changing
MessageActionfrom struct to class is a subtle but breaking change for@Published public var messageActions: [MessageAction]. With structs, in-place mutations of properties (like settingnavigationDestination) changed the array value itself, triggering change notifications. With classes, mutations to properties on existing array elements don't trigger the publisherβthe array reference remains unchanged.The codebase has at least two places that rely on this pattern:
DefaultChannelActions.swift:220setsnavigationDestinationon an existing actionChatChannelInfoViewModel.swift:299setsnavigationDestinationon an existing actionIf these mutations happen after the action is already in
messageActions, the UI won't update because the published array won't fireobjectWillChange.Additionally, making
ida mutablevaron anIdentifiabletype risks breaking SwiftUI's identity diffing if the ID changes after the initial render. Consider making itlet.Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/TypingSuggester.swift (1)
9-15:β οΈ Potential issue | π‘ MinorFix doc comment mismatch and consider making
TypingSuggestionOptionsproperties immutable.Line 35 states "A structure" but the type on line 36 is a
public final class. While the codebase shows no actual mutations ofsymbol,shouldTriggerOnlyAtStart, orminimumRequiredCharactersafter construction, converting these properties fromvartoletwould clarify the intent that options are configured at initialization and then treated as immutable. This also better documents the contract for consumers of this API.Suggested safer alternative: make properties immutable
- /// A structure that contains the information of the typing suggestion. + /// A class that contains the information of the typing suggestion.And convert properties to
let:- public var symbol: String - public var shouldTriggerOnlyAtStart: Bool - public var minimumRequiredCharacters: Int + public let symbol: String + public let shouldTriggerOnlyAtStart: Bool + public let minimumRequiredCharacters: IntSources/StreamChatSwiftUI/ChatChannel/MessageList/AsyncVoiceMessages/WaveformView.swift (1)
17-58:β οΈ Potential issue | π‘ MinorAPI design concern:
Contentproperties should be read-only to prevent in-place mutations.While
Contentwas converted from a struct to afinal class, the properties remainpublic var, which invites callers to mutate them directly. A struct would allowview.content.currentTime = 5.0to implicitly triggerdidSetoncontent(value semantics), but a class will notβthe reference itself remains unchanged.A search of the codebase found no current in-place mutations; all assignments use full object replacement (
view.content = Content(...)), so this is not an active regression. However, the public mutable properties create a latent risk for future maintainers.Consider marking
Contentproperties as read-only:
- Change
public vartopublic letin the initializer, or- Use
public private(set) varif mutation is needed internally.This forces callers to replace the entire
Contentobject, ensuringdidSetalways fires.Sources/StreamChatSwiftUI/ChatChannelList/MoreChannelActionsViewModel.swift (1)
94-119:β οΈ Potential issue | π‘ MinorMutable
navigationDestinationon a reference type introduces a design risk.
ChannelActionis now a class with a mutablenavigationDestinationproperty that's stored in@Publishedarrays. Current usage is safeβnavigationDestinationis always set during construction (before the action is added to any array)βbut the design permits a footgun: ifnavigationDestinationwere mutated on an action already in the@Publishedarray, SwiftUI wouldn't detect the change because class mutations don't trigger the publisher.To prevent future accidental mutations post-insertion, either:
- Add
navigationDestinationto the initializer and make itlet, or- Document that
navigationDestinationmust only be set before the action is added to a collection.Sources/StreamChatSwiftUI/ChatChannel/MessageList/QuotedMessageView.swift (1)
155-171:β οΈ Potential issue | π‘ MinorAdd
Equatableconformance to avoid unnecessary SwiftUI re-renders.Converting
QuotedMessageContentViewOptionsfrom a struct to a class removes the compiler's automaticEquatablesynthesis. Since this class is stored as a property inQuotedMessageContentView(a SwiftUIViewstruct at line 186), SwiftUI relies on property comparison to detect changes. WithoutEquatable, the comparison falls back to identity (===), meaning every new instance is treated as "different" even when all values are identical, causing unnecessary re-renders.Since all properties are
let(immutable), implementing structural equality is straightforward:Suggested fix
-public final class QuotedMessageContentViewOptions { +public final class QuotedMessageContentViewOptions: Equatable { /// The quoted message to display. public let quotedMessage: ChatMessage /// Whether the quoted container should take all the available space. public let fillAvailableSpace: Bool /// The size of the attachment preview. public let attachmentSize: CGSize + public static func == (lhs: QuotedMessageContentViewOptions, rhs: QuotedMessageContentViewOptions) -> Bool { + lhs.quotedMessage == rhs.quotedMessage + && lhs.fillAvailableSpace == rhs.fillAvailableSpace + && lhs.attachmentSize == rhs.attachmentSize + } + public init((Optional:
Sendableconformance is also valid for thisfinal classwith allletproperties, beneficial for Swift 6 strict concurrency.)Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatInfoParticipantsView.swift (1)
72-92:β οΈ Potential issue | π Major
ParticipantInfoneedsEquatableconformance for correct SwiftUI@Bindingbehavior.This type is used with
@Binding var selectedParticipant: ParticipantInfo?(line 14). Structs could rely on synthesizedEquatable, but classes do not get that for free. WithoutEquatable, SwiftUI cannot properly diff old vs. new values for the binding, which can cause missed or redundant view updates.Since all stored properties are
let, the fix is straightforward:Proposed fix
-public final class ParticipantInfo: Identifiable { +public final class ParticipantInfo: Identifiable, Equatable { + public static func == (lhs: ParticipantInfo, rhs: ParticipantInfo) -> Bool { + lhs.chatUser.id == rhs.chatUser.id + && lhs.displayName == rhs.displayName + && lhs.onlineInfoText == rhs.onlineInfoText + && lhs.isDeactivated == rhs.isDeactivated + } + public var id: String { chatUser.id }Sources/StreamChatSwiftUI/ColorPalette.swift (1)
9-19:β οΈ Potential issue | π MajorShared-mutation risk: mutable
varproperties on a reference type.
ColorPalettehas many publicvarproperties. As a struct, consumers could safely do:var myColors = appearance.colors myColors.tintColor = .red // only affects the local copyNow that it's a class, this mutates the shared instance. This is a silent behavioral change for existing consumers who relied on value-copy semantics.
Since
lazy varproperties (lines 83β105) genuinely benefit from class semantics (nomutatingaccess needed), the conversion has merit, but consider documenting the change in the SDK migration guide and/or providing acopy()method so consumers can opt into independent instances.Sources/StreamChatSwiftUI/ChatChannel/Composer/ComposerConfig.swift (1)
9-61:β οΈ Potential issue | π MajorMutable
varproperties on a reference type risk unintended shared-state mutations.
ComposerConfighas 11public varproperties. As a struct, mutations were local (copy semantics). Now that it's a class, any holder mutating a property (e.g.,config.isVoiceRecordingEnabled = true) silently affects every other holder of the same instance. This is especially risky for closures likeadjustMessageOnSendthat could be swapped at runtime.Consider either:
- Making stored properties
let(as done in the other converted types), or- Keeping this particular type as a struct if runtime mutation is intended to be local.
Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/Mentions/MentionsCommandHandler.swift (1)
152-166:β οΈ Potential issue | π‘ MinorHandle case when
selfis deallocated before the Future resolves.If
selfis deallocated before the closure runs, the earlyreturnon line 154 means theFuturenever resolves (no.successor.failure). Any downstreamsink/receivesubscriber will hang indefinitely.Fail the promise explicitly instead:
Proposed fix
private func searchAllUsers(for typingMention: String) -> Future<SuggestionInfo, Error> { Future { [weak self] promise in - guard let self else { return } + guard let self else { + promise(.failure(ClientError("MentionsCommandHandler was deallocated"))) + return + } let query = queryForMentionSuggestionsSearch(typingMention: typingMention)Sources/StreamChatSwiftUI/ChatChannel/Utils/ChatChannelHelpers.swift (1)
21-43:β οΈ Potential issue | π‘ MinorUse
structinstead ofclassforPreferenceKeyconformanceβit's the standard and expected pattern.While
PreferenceKeytechnically works as aclass(the protocol only requires static members), all three types here should bestruct. SwiftUI convention and all examples usestructfor preference keys because only the type itself is used by SwiftUI (never instantiated). Usingclassadds no benefit and creates unnecessary confusion about instance semantics.Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelNavigatableListItem.swift (2)
92-95:β οΈ Potential issue | π‘ MinorDefault
searchTypeof.messageslooks incorrect for non-search channel selections.The initializer defaults
searchTypeto.messages, and the convenienceChatChannel.channelSelectionInfo(line 120) usesmessage: nil. This means every channel opened from the list (not via search) gets tagged with.messagessearch type. Should the default be.channelsinstead?
85-106:β οΈ Potential issue | π΄ CriticalMutating class properties on a
@Publishedreference won't trigger view updates.Converting
ChannelSelectionInfofrom a struct to a class breaks SwiftUI's observation mechanism for property mutations. WhenselectedChannel?.injectedChannelInfois mutated at lines 549 and 572, the reference itself remains unchanged, so the@Publishedobserver never fires and dependent views don't re-render.With a struct, these mutations would trigger copy-on-write reassignment and fire
@Publishedwatchers. With a class, mutations happen in-place on the reference without notifying SwiftUI.selectedChannel?.injectedChannelInfo = InjectedChannelInfo(unreadCount: 0) // line 549 selectedChannel?.injectedChannelInfo = InjectedChannelInfo(...) // line 572This causes channel list items displaying
subtitle,timestamp, andunreadCount(which come frominjectedChannelInfo) to show stale data when returning from a conversation.Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListItem.swift (1)
394-418:β οΈ Potential issue | π MajorStatic class instances should be
let, notvarβ same pattern asChannelListSearchType.
default,topRightCorner, andafterChannelNamearestatic varon a class. External code could reassign them (e.g.,ChannelItemMutedLayoutStyle.default = .topRightCorner), corrupting all consumers.Proposed fix
- public static var `default`: ChannelItemMutedLayoutStyle = .init("default") - public static var topRightCorner: ChannelItemMutedLayoutStyle = .init("topRightCorner") - public static var afterChannelName: ChannelItemMutedLayoutStyle = .init("afterChannelName") + public static let `default`: ChannelItemMutedLayoutStyle = .init("default") + public static let topRightCorner: ChannelItemMutedLayoutStyle = .init("topRightCorner") + public static let afterChannelName: ChannelItemMutedLayoutStyle = .init("afterChannelName")Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift (1)
441-466:β οΈ Potential issue | π‘ MinorConsider if mutable properties on a reference type create unnecessary mutation surface.
showsMessageActions,showsBottomContainer, andkeyboardWasShownare declared asvaron a class. While the current codebase patterns (creating fresh instances inhandleLongPressat line 426, read-only access inReactionsOverlayView) avoid mutations, this API design unnecessarily exposes mutable state on a reference type. If these properties should not change after initialization, marking them asletwith proper initialization would eliminate the risk entirely and clarify intent.
π€ Fix all issues with AI agents
In
`@Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel`+Recording.swift:
- Around line 19-22: AudioRecordingInfo is now a class so in-place mutations in
MessageComposerViewModel (e.g., AudioRecordingInfo.update(with:duration:) and
direct waveform mutations) don't trigger the `@Published` publisher; after any
in-place mutation of audioRecordingInfo (calls to update(with:duration:) or
modifying audioRecordingInfo.waveform/duration), explicitly notify SwiftUI by
either calling objectWillChange.send() on MessageComposerViewModel or force a
setter by reassigning the property (audioRecordingInfo = audioRecordingInfo) so
observers update; update usages where waveform.append(...) and waveform
assignments occur to perform one of these notifications immediately after the
mutation.
In
`@Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/TypingSuggester.swift`:
- Around line 35-36: The doc comment for TypingSuggestion is stale: it says "A
structure that contains..." but TypingSuggestion is declared as a final class;
update the documentation comment above the TypingSuggestion declaration to refer
to it as a "class" (e.g., "A class that contains the information of the typing
suggestion.") so the comment matches the TypingSuggestion type.
In `@Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift`:
- Around line 488-496: Add explicit Equatable conformance to MediaAttachmentType
by declaring it conforms to Equatable and implementing the equality operator to
compare rawValue; specifically update the class declaration for
MediaAttachmentType to include Equatable and add a static func ==(lhs:
MediaAttachmentType, rhs: MediaAttachmentType) -> Bool { return lhs.rawValue ==
rhs.rawValue } so generic constraints requiring Equatable will be satisfied
(refer to MediaAttachmentType and its rawValue property).
In `@Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift`:
- Around line 655-656: Change the two shared static properties on
ChannelListSearchType from mutable to immutable to prevent external
reassignment: replace the declarations of ChannelListSearchType.channels and
ChannelListSearchType.messages (currently declared as `static var`) with `static
let` so the shared instances cannot be overwritten by consumers.
In `@Sources/StreamChatSwiftUI/Fonts.swift`:
- Around line 8-11: Fonts currently exposes all mutable var properties which
allows shared-mutation when consumers copy appearance.fonts; fix this by making
Fonts value-type or its properties immutable: either convert the public final
class Fonts into a struct Fonts (so appearance.fonts becomes a value copy) or
change each public mutable property on Fonts to let and add explicit
builder/with... methods that return a new Fonts when callers need changes;
reference the Fonts type and appearance.fonts usage and ensure any consumers are
updated to use the new immutable/struct API or builder methods to mutate safely.
π§Ή Nitpick comments (6)
Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/MediaAttachmentsViewModel.swift (1)
109-139: MakingvideoAttachmentandimageAttachmentpropertiesletstrengthens immutability.The struct-to-class conversion with
finalis soundβIdentifiableis sufficient for SwiftUI'sForEach(which uses index-based iteration), and no code comparesMediaIteminstances for equality. SincevideoAttachmentandimageAttachmentare initialized only once and never mutated afterward, making themletproperties clarifies intent and eliminates the theoretical risk of accidental mutation through a stored reference:- public var videoAttachment: ChatMessageVideoAttachment? - public var imageAttachment: ChatMessageImageAttachment? + public let videoAttachment: ChatMessageVideoAttachment? + public let imageAttachment: ChatMessageImageAttachment?This change simplifies the contract and prevents any future mutations from introducing state coherency issues.
Sources/StreamChatSwiftUI/Utils/Common/NukeImageProcessor.swift (1)
65-65: Add explicitSendableconformance to align with codebase patterns.Converting from
structtofinal classloses the implicitSendableconformance that value types with all-Sendablemembers enjoy. SincesizeProvideris already@Sendableand the class isfinalwith no mutable state, addingSendableconforms to the established pattern used throughout this codebase for similar types.Suggested change
- public final class LateResize: ImageProcessing { + public final class LateResize: ImageProcessing, `@unchecked` Sendable {(
@uncheckedis appropriate here becauseDispatchQueue.main.syncaccess isn't provable to the compiler, though the type is effectively safe.)Sources/StreamChatSwiftUI/ChatChannelList/ChannelListConfig.swift (1)
31-31: Redundant inline default onchannelItemMutedStyle.The init parameter already defaults to
.default(line 12) and always assigns to this property (line 16), so the inline= .defaulthere is dead code. The other two properties in this class don't carry inline defaults, making this inconsistent.Proposed fix
- public var channelItemMutedStyle: ChannelItemMutedLayoutStyle = .default + public var channelItemMutedStyle: ChannelItemMutedLayoutStyleSources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ParticipantInfoView.swift (1)
116-141: Class with closure properties can form retain cycles β low risk but worth noting.As a struct,
ParticipantActionwas immune to reference cycles. Now that it's a class holdingaction: () -> VoidandnavigationDestination: AnyView?, a retain cycle is possible if a closure or AnyView captures theParticipantActioninstance itself. In practice this is unlikely given typical usage patterns, but keep it in mind if you expose builder APIs that chain back to the action.Sources/StreamChatSwiftUI/DependencyInjection.swift (1)
34-45:Injected<T>should declareSendableconformance to maintain compatibility with Swift 6 strict concurrency.
Injected<T>is afinal classwith only a singlelet keyPathstored property. When this was a struct, it implicitly conformed toSendable. As a class, it loses that automatic conformance. Since@Injectedis used 510+ times across the codebaseβpredominantly in View structsβthose Views will fail to satisfySendablerequirements under Swift 6's strict concurrency checking. Since the sole stored property is immutable andWritableKeyPathisSendable, declaring conformance is safe and trivial.Proposed fix
`@propertyWrapper` -public final class Injected<T> { +public final class Injected<T>: Sendable { private let keyPath: WritableKeyPath<InjectedValues, T>Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel+Recording.swift (1)
29-33:static var initialheap-allocates a new instance on every access.As a computed property returning a new class instance, each call to
.initialtriggers a heap allocation. This is used on line 50 purely for an equality check (audioRecordingInfo == .initial), creating a throwaway object. Consider a lighter guard:Possible alternatives
- if audioRecordingInfo == .initial { return } + if audioRecordingInfo.waveform.isEmpty && audioRecordingInfo.duration == 0 { return }Or cache a shared sentinel instance if
.initialis accessed frequently (though be careful with shared mutable state on a class).Also applies to: 50-50
| func update(with entry: Float, duration: TimeInterval) { | ||
| waveform.append(entry) | ||
| self.duration = duration | ||
| } |
There was a problem hiding this comment.
π§© Analysis chain
π Script executed:
#!/bin/bash
# Find AudioRecordingInfo definition and check if it's a class or struct
rg -n 'class AudioRecordingInfo|struct AudioRecordingInfo' --type=swift -A 5Repository: GetStream/stream-chat-swiftui
Length of output: 817
π Script executed:
#!/bin/bash
# Search for `@Published` audioRecordingInfo on MessageComposerViewModel
rg -n '@Published.*audioRecordingInfo|var audioRecordingInfo' --type=swift -B 2 -A 2Repository: GetStream/stream-chat-swiftui
Length of output: 1164
π Script executed:
#!/bin/bash
# View the entire file to understand context and verify line numbers
cat -n "Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel+Recording.swift"Repository: GetStream/stream-chat-swiftui
Length of output: 5305
π Script executed:
#!/bin/bash
# Verify impact: check if RecordingView or other UI components actively observe audioRecordingInfo for live updates
rg -n 'audioRecordingInfo' --type=swift -B 2 -A 2 | grep -A 5 "RecordingView\|\.onChange\|\.onReceive"Repository: GetStream/stream-chat-swiftui
Length of output: 2375
π Script executed:
#!/bin/bash
# Check if objectWillChange is already being sent anywhere in MessageComposerViewModel
rg -n 'objectWillChange' "Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel+Recording.swift"Repository: GetStream/stream-chat-swiftui
Length of output: 55
π Script executed:
#!/bin/bash
# Check RecordingView to see if it's a struct or view and how audioRecordingInfo is used
cat -n "Sources/StreamChatSwiftUI/ChatChannel/Composer/VoiceRecording/RecordingView.swift" | head -50Repository: GetStream/stream-chat-swiftui
Length of output: 1808
π Script executed:
#!/bin/bash
# Check RecordingDurationView to see if it updates with duration changes
rg -n 'RecordingDurationView' --type=swift -B 2 -A 10Repository: GetStream/stream-chat-swiftui
Length of output: 8450
π Script executed:
#!/bin/bash
# Check how RecordingView is embedded in the parent view and if it's responding to viewModel changes
rg -n 'RecordingView\(' --type=swift -B 3 -A 3 | head -40Repository: GetStream/stream-chat-swiftui
Length of output: 3563
π Script executed:
#!/bin/bash
# Check if the parent view observes the viewModel
cat -n "Sources/StreamChatSwiftUI/DefaultViewFactory.swift" | sed -n '720,735p'Repository: GetStream/stream-chat-swiftui
Length of output: 695
π Script executed:
#!/bin/bash
# Check RecordingWaveform to see if it also uses audioRecordingInfo properties
rg -n 'RecordingWaveform' --type=swift -B 2 -A 10 | head -60Repository: GetStream/stream-chat-swiftui
Length of output: 3202
π Script executed:
#!/bin/bash
# Check LockedView to see if it observes viewModel
cat -n "Sources/StreamChatSwiftUI/ChatChannel/Composer/VoiceRecording/LockedView.swift" | head -30Repository: GetStream/stream-chat-swiftui
Length of output: 1105
π Script executed:
#!/bin/bash
# Check the parent view structure - where makeComposerRecordingView is called from
rg -n 'makeComposerRecordingView' --type=swift -B 5 -A 5Repository: GetStream/stream-chat-swiftui
Length of output: 5387
π Script executed:
#!/bin/bash
# Check MessageComposerView to see how viewModel observes changes
cat -n "Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerView.swift" | head -50Repository: GetStream/stream-chat-swiftui
Length of output: 2151
π Script executed:
#!/bin/bash
# Check if RecordingView might be wrapped with `@ObservedObject` or similar
rg -n 'RecordingView' "Sources/StreamChatSwiftUI/DefaultViewFactory.swift" -B 10 -A 10Repository: GetStream/stream-chat-swiftui
Length of output: 1640
π Script executed:
#!/bin/bash
# Final verification: confirm the parent view structure
cat -n "Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerView.swift" | sed -n '118,135p'Repository: GetStream/stream-chat-swiftui
Length of output: 1177
Critical: In-place mutations on a class won't trigger @Published / SwiftUI updates.
When AudioRecordingInfo was a struct, mutating it through audioRecordingInfo.update(β¦) (line 40) or audioRecordingInfo.waveform = β¦ (line 66) would trigger the property setter on the @Published var audioRecordingInfo, firing objectWillChange and refreshing any observing SwiftUI views. Now that it's a reference type, these in-place mutations modify the object's internals without replacing the reference, so @Published never fires.
This silently breaks live waveform and duration UI updates during recording. Only full reassignments like audioRecordingInfo = .initial (lines 70, 87, 118, 127) will trigger SwiftUI.
You need to manually fire objectWillChange after in-place mutations, or reassign the property to itself:
Possible fix β explicitly notify after in-place mutation
public func audioRecorder(
_ audioRecorder: AudioRecording,
didUpdateContext: AudioRecordingContext
) {
audioRecordingInfo.update(
with: didUpdateContext.averagePower,
duration: didUpdateContext.duration
)
+ objectWillChange.send()
}And similarly on line 66:
if self.recordingState == .stopped {
self.pendingAudioRecording = recording
self.audioRecordingInfo.waveform = waveform
+ self.objectWillChange.send()
} else {Alternatively, consider keeping AudioRecordingInfo as a struct if the value semantics were intentionally leveraged for SwiftUI reactivity.
π€ Prompt for AI Agents
In
`@Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerViewModel`+Recording.swift
around lines 19 - 22, AudioRecordingInfo is now a class so in-place mutations in
MessageComposerViewModel (e.g., AudioRecordingInfo.update(with:duration:) and
direct waveform mutations) don't trigger the `@Published` publisher; after any
in-place mutation of audioRecordingInfo (calls to update(with:duration:) or
modifying audioRecordingInfo.waveform/duration), explicitly notify SwiftUI by
either calling objectWillChange.send() on MessageComposerViewModel or force a
setter by reassigning the property (audioRecordingInfo = audioRecordingInfo) so
observers update; update usages where waveform.append(...) and waveform
assignments occur to perform one of these notifications immediately after the
mutation.
| /// A structure that contains the information of the typing suggestion. | ||
| public struct TypingSuggestion { | ||
| public final class TypingSuggestion { |
There was a problem hiding this comment.
Stale doc comment: "structure" should be "class".
Line 35 still reads "A structure that contains..." but the type is now a final class.
Suggested fix
-/// A structure that contains the information of the typing suggestion.
+/// A class that contains the information of the typing suggestion.π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /// A structure that contains the information of the typing suggestion. | |
| public struct TypingSuggestion { | |
| public final class TypingSuggestion { | |
| /// A class that contains the information of the typing suggestion. | |
| public final class TypingSuggestion { |
π€ Prompt for AI Agents
In
`@Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/TypingSuggester.swift`
around lines 35 - 36, The doc comment for TypingSuggestion is stale: it says "A
structure that contains..." but TypingSuggestion is declared as a final class;
update the documentation comment above the TypingSuggestion declaration to refer
to it as a "class" (e.g., "A class that contains the information of the typing
suggestion.") so the comment matches the TypingSuggestion type.
| public final class MediaAttachmentType: RawRepresentable { | ||
| public let rawValue: String | ||
| public init(rawValue: String) { | ||
| self.rawValue = rawValue | ||
| } | ||
|
|
||
| public static let image = Self(rawValue: "image") | ||
| public static let video = Self(rawValue: "video") | ||
| public static let image = MediaAttachmentType(rawValue: "image") | ||
| public static let video = MediaAttachmentType(rawValue: "video") | ||
| } |
There was a problem hiding this comment.
π§© Analysis chain
π Script executed:
# First, let's verify the file exists and examine the MediaAttachmentType implementation
cat -n Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift | sed -n '480,510p'Repository: GetStream/stream-chat-swiftui
Length of output: 952
π Script executed:
# Search for all usages of MediaAttachmentType across the codebase
rg -n --type swift 'MediaAttachmentType' | head -60Repository: GetStream/stream-chat-swiftui
Length of output: 814
π Script executed:
# Search for broader context: look for comparisons, usage in SwiftUI views, arrays, etc.
rg -n --type swift 'MediaAttachmentType' -B 2 -A 2Repository: GetStream/stream-chat-swiftui
Length of output: 2152
π Script executed:
# Check if there are any Equatable constraints or comparisons involving MediaAttachmentType
rg -n --type swift '(\.image|\.video|== .*MediaAttachmentType|MediaAttachmentType.*==)'Repository: GetStream/stream-chat-swiftui
Length of output: 50386
π Script executed:
# Search for the struct that contains MediaAttachmentType property to see if it needs Equatable
rg -n --type swift -B 10 'public let type: MediaAttachmentType' Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift | head -30Repository: GetStream/stream-chat-swiftui
Length of output: 376
π Script executed:
# Get the full MediaAttachment class definition
rg -n --type swift 'public final class MediaAttachment' -A 50 Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift | head -60Repository: GetStream/stream-chat-swiftui
Length of output: 2140
π Script executed:
# Search for any custom == implementation or Equatable conformance issues
rg -n --type swift '(func ==|extension.*Equatable)' Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swiftRepository: GetStream/stream-chat-swiftui
Length of output: 157
π Script executed:
# Check if MediaAttachmentType is used directly in any comparisons or SwiftUI state
rg -n --type swift 'MediaAttachmentType.*=='Repository: GetStream/stream-chat-swiftui
Length of output: 55
π Script executed:
# Look for ForEach with MediaAttachmentType or similar SwiftUI patterns
rg -n --type swift 'ForEach.*MediaAttachmentType|\.image|\.video' -B 2 -A 2 | head -50Repository: GetStream/stream-chat-swiftui
Length of output: 4013
π Script executed:
# Check if MediaAttachmentType is used in any Array or Collection contexts that might need Equatable
rg -n --type swift '\[.*MediaAttachmentType.*\]|\@State.*MediaAttachmentType|\.filter.*type.*==' Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swiftRepository: GetStream/stream-chat-swiftui
Length of output: 242
π Script executed:
# Check the GalleryView file which seems to reference source.type == .image (line 77 from earlier search)
cat -n Sources/StreamChatSwiftUI/ChatChannel/Gallery/GalleryView.swift | sed -n '70,90p'Repository: GetStream/stream-chat-swiftui
Length of output: 1320
π Script executed:
# Check if MediaAttachmentType is used in any SwiftUI state/binding contexts
rg -n --type swift '@State.*source|@Binding.*source|id: \.self.*mediaAttachments' Sources/StreamChatSwiftUI/ChatChannel/Gallery/GalleryView.swiftRepository: GetStream/stream-chat-swiftui
Length of output: 55
π Script executed:
# Check if there are any Hashable constraints on MediaAttachmentType (which would require Equatable)
rg -n --type swift 'Hashable.*MediaAttachmentType|MediaAttachmentType.*Hashable'Repository: GetStream/stream-chat-swiftui
Length of output: 55
π Script executed:
# Verify the exact line where == is used on MediaAttachmentType in MediaAttachment's == implementation
cat -n Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift | sed -n '465,470p'Repository: GetStream/stream-chat-swiftui
Length of output: 322
Add explicit Equatable conformance to MediaAttachmentType.
MediaAttachmentType currently relies on the free-function == overload from RawRepresentable, which allows direct == calls to work but does not satisfy Equatable protocol constraints. While comparisons function in the current codebase, explicit conformance ensures the type can be used safely in generic contexts requiring where T: Equatable.
Proposed fix
-public final class MediaAttachmentType: RawRepresentable {
+public final class MediaAttachmentType: RawRepresentable, Equatable {
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
public static let image = MediaAttachmentType(rawValue: "image")
public static let video = MediaAttachmentType(rawValue: "video")
+
+ public static func == (lhs: MediaAttachmentType, rhs: MediaAttachmentType) -> Bool {
+ lhs.rawValue == rhs.rawValue
+ }
}π€ Prompt for AI Agents
In `@Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift`
around lines 488 - 496, Add explicit Equatable conformance to
MediaAttachmentType by declaring it conforms to Equatable and implementing the
equality operator to compare rawValue; specifically update the class declaration
for MediaAttachmentType to include Equatable and add a static func ==(lhs:
MediaAttachmentType, rhs: MediaAttachmentType) -> Bool { return lhs.rawValue ==
rhs.rawValue } so generic constraints requiring Equatable will be satisfied
(refer to MediaAttachmentType and its rawValue property).
Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift
Outdated
Show resolved
Hide resolved
| public final class Fonts { | ||
| public init() { | ||
| // Public init. | ||
| } |
There was a problem hiding this comment.
Same shared-mutation risk as ColorPalette β all properties are mutable var.
Fonts has all var properties, so the value-to-reference semantics shift carries the same risk described for ColorPalette: consumers copying appearance.fonts into a local variable and mutating it will now unintentionally modify the shared instance. Unlike ColorPalette, there are no lazy var properties here that benefit from class semantics.
π€ Prompt for AI Agents
In `@Sources/StreamChatSwiftUI/Fonts.swift` around lines 8 - 11, Fonts currently
exposes all mutable var properties which allows shared-mutation when consumers
copy appearance.fonts; fix this by making Fonts value-type or its properties
immutable: either convert the public final class Fonts into a struct Fonts (so
appearance.fonts becomes a value copy) or change each public mutable property on
Fonts to let and add explicit builder/with... methods that return a new Fonts
when callers need changes; reference the Fonts type and appearance.fonts usage
and ensure any consumers are updated to use the new immutable/struct API or
builder methods to mutate safely.
SDK Size
|
Public Interface+ public final class PaddingsConfig
+
+ public let top: CGFloat
+ public let bottom: CGFloat
+ public let leading: CGFloat
+ public let trailing: CGFloat
+ public var horizontal: CGFloat
+ public var vertical: CGFloat
+
+
+ public init(top: CGFloat,bottom: CGFloat,leading: CGFloat,trailing: CGFloat)
+ public final class LateResize: ImageProcessing
+
+ public var identifier: String
+
+
+ public init(sizeProvider: @escaping @Sendable () -> CGSize)
+
+
+ public func process(_ image: PlatformImage)-> PlatformImage?
+ public final class MessageActionInfo
+
+ public let message: ChatMessage
+ public let identifier: String
+
+
+ public init(message: ChatMessage,identifier: String)
+ public final class MessagePaddings
+
+ public let horizontal: CGFloat
+ public let quotedViewPadding: CGFloat
+ public let singleBottom: CGFloat
+ public let groupBottom: CGFloat
+
+
+ public init(horizontal: CGFloat = 8,quotedViewPadding: CGFloat = 8,singleBottom: CGFloat = 8,groupBottom: CGFloat = 2)
+ public final class Fonts
+
+ public var caption1
+ public var footnoteBold
+ public var footnote
+ public var subheadline
+ public var subheadlineBold
+ public var body
+ public var bodyBold
+ public var bodyItalic
+ public var headline
+ public var headlineBold
+ public var title
+ public var title2
+ public var title3
+ public var emoji
+
+
+ public init()
+ public final class CustomAttachment: Identifiable, Equatable
+
+ public let id: String
+ public let content: AnyAttachmentPayload
+
+
+ public init(id: String,content: AnyAttachmentPayload)
+
+
+ public static func ==(lhs: CustomAttachment,rhs: CustomAttachment)-> Bool
+ public final class ChannelListSearchType: Equatable
+
+ public static let channels
+ public static let messages
+
+
+ public static func ==(lhs: ChannelListSearchType,rhs: ChannelListSearchType)-> Bool
+ public final class ParticipantInfo: Identifiable
+
+ public var id: String
+ public let chatUser: ChatUser
+ public let displayName: String
+ public let onlineInfoText: String
+ public let isDeactivated: Bool
+
+
+ public init(chatUser: ChatUser,displayName: String,onlineInfoText: String,isDeactivated: Bool = false)
+ public final class PollsConfig
+
+ public var multipleAnswers: PollsEntryConfig
+ public var anonymousPoll: PollsEntryConfig
+ public var suggestAnOption: PollsEntryConfig
+ public var addComments: PollsEntryConfig
+ public var maxVotesPerPerson: PollsEntryConfig
+
+
+ public init(multipleAnswers: PollsEntryConfig = .default,anonymousPoll: PollsEntryConfig = .default,suggestAnOption: PollsEntryConfig = .default,addComments: PollsEntryConfig = .default,maxVotesPerPerson: PollsEntryConfig = .default)
+ public final class PHFetchResultCollection: RandomAccessCollection, Equatable
+
+ public let fetchResult: PHFetchResult<PHAsset>
+ public var endIndex: Int
+ public var startIndex: Int
+
+
+ public subscript(position: Int)-> PHAsset
+
+
+ public init(fetchResult: PHFetchResult<PHAsset>)
+
+
+ public static func ==(lhs: PHFetchResultCollection,rhs: PHFetchResultCollection)-> Bool
+ public final class ChatThreadListItemViewModel
+
+ public var parentMessageText: String
+ public var latestReplyMessageText: String
+ public var latestReplyTimestampText: String
+ public var draftReplyText: String?
+ public var unreadRepliesCount: Int
+ public var latestReplyAuthorNameText: String
+ public var isLatestReplyAuthorOnline: Bool
+ public var latestReplyAuthorImageURL: URL?
+ public var latestReplyAuthorId: String
+ public var channelNameText: String
+
+
+ public init(thread: ChatThread)
+ public final class ChannelSelectionInfo: Identifiable
+
+ public let id: String
+ public let channel: ChatChannel
+ public let message: ChatMessage?
+ public var injectedChannelInfo: InjectedChannelInfo?
+ public var searchType: ChannelListSearchType
+
+
+ public init(channel: ChatChannel,message: ChatMessage?,searchType: ChannelListSearchType = .messages)
+ public final class InjectedValues
+
+ public static subscript<K>(key: K.Type)-> K.Value where K: InjectionKey
+ public static subscript<T>(_ keyPath: WritableKeyPath<InjectedValues, T>)-> T
+ public final class ColorPalette
+
+ public var tintColor: Color
+ public var text: UIColor
+ public var textInverted: UIColor
+ public var textLowEmphasis: UIColor
+ public var staticColorText: UIColor
+ public var subtitleText: UIColor
+ public var highlightedColorForColor: (UIColor) -> UIColor
+ public var disabledColorForColor: (UIColor) -> UIColor
+ public var unselectedColorForColor: (UIColor) -> UIColor
+ public var background: UIColor
+ public var background1: UIColor
+ public var background2: UIColor
+ public var background3: UIColor
+ public var background4: UIColor
+ public var background5: UIColor
+ public var background6: UIColor
+ public var background7: UIColor
+ public var background8: UIColor
+ public var overlayBackground: UIColor
+ public var popoverBackground: UIColor
+ public var highlightedBackground: UIColor
+ public var highlightedAccentBackground: UIColor
+ public var highlightedAccentBackground1: UIColor
+ public var pinnedBackground: UIColor
+ public var messageCellHighlightBackground: UIColor
+ public var shadow: UIColor
+ public var lightBorder: UIColor
+ public var innerBorder: UIColor
+ public var border: UIColor
+ public var border2: UIColor
+ public var border3: UIColor
+ public var alert: UIColor
+ public var alternativeActiveTint: UIColor
+ public var inactiveTint: UIColor
+ public var alternativeInactiveTint: UIColor
+ public lazy var messageCurrentUserBackground: [UIColor]
+ public lazy var messageCurrentUserEmphemeralBackground: [UIColor]
+ public lazy var messageOtherUserBackground: [UIColor]
+ public lazy var messageCurrentUserTextColor: UIColor
+ public lazy var messageOtherUserTextColor: UIColor
+ public lazy var quotedMessageBackgroundCurrentUser: UIColor
+ public lazy var quotedMessageBackgroundOtherUser: UIColor
+ public lazy var reactionCurrentUserColor: UIColor?
+ public lazy var reactionOtherUserColor: UIColor?
+ public lazy var selectedReactionBackgroundColor: UIColor?
+ public var voiceMessageControlBackground: UIColor
+ public var messageLinkAttachmentAuthorColor: Color
+ public var messageLinkAttachmentTitleColor: Color
+ public var messageLinkAttachmentTextColor: Color
+ public lazy var composerPlaceholderColor: UIColor
+ public lazy var composerInputBackground: UIColor
+ public lazy var composerInputHighlightedBorder: UIColor
+ public var navigationBarGlyph: UIColor
+ public var navigationBarTitle: UIColor
+ public var navigationBarSubtitle: UIColor
+ public var navigationBarTintColor: Color
+ public var navigationBarBackground: UIColor?
+ public var bannerBackgroundColor: UIColor
+
+
+ public init()
+ public final class ConfirmationPopup
+
+ public let title: String
+ public let message: String?
+ public let buttonTitle: String
+
+
+ public init(title: String,message: String?,buttonTitle: String)
+ public final class TypingSuggestion
+
+ public let text: String
+ public let locationRange: NSRange
+
+
+ public init(text: String,locationRange: NSRange)
+ public final class UserDisplayInfo
+
+ public let id: String
+ public let name: String
+ public let imageURL: URL?
+ public let role: UserRole?
+ public let size: CGSize?
+ public let extraData: [String: RawJSON]
+
+
+ public init(id: String,name: String,imageURL: URL?,role: UserRole? = nil,size: CGSize? = nil,extraData: [String: RawJSON] = [:])
+ public final class ParticipantAction: Identifiable
+
+ public var id: String
+ public let title: String
+ public let iconName: String
+ public let action: () -> Void
+ public let confirmationPopup: ConfirmationPopup?
+ public let isDestructive: Bool
+ public var navigationDestination: AnyView?
+
+
+ public init(title: String,iconName: String,action: @escaping () -> Void,confirmationPopup: ConfirmationPopup?,isDestructive: Bool)
+ public final class MediaAttachmentType: RawRepresentable
+
+ public let rawValue: String
+ public static let image
+ public static let video
+
+
+ public init(rawValue: String)
+ public final class QuotedMessageContentViewOptions
+
+ public let quotedMessage: ChatMessage
+ public let fillAvailableSpace: Bool
+ public let attachmentSize: CGSize
+
+
+ public init(quotedMessage: ChatMessage,fillAvailableSpace: Bool,attachmentSize: CGSize = CGSize(width: 36, height: 36))
+ public final class AddedAsset: Identifiable, Equatable
+
+ public let image: UIImage
+ public let id: String
+ public let url: URL
+ public let type: AssetType
+ public var extraData: [String: RawJSON]
+ public var payload: AttachmentPayload?
+
+
+ public init(image: UIImage,id: String,url: URL,type: AssetType,extraData: [String: RawJSON] = [:],payload: AttachmentPayload? = nil)
+
+
+ public static func ==(lhs: AddedAsset,rhs: AddedAsset)-> Bool
+ public final class ComposerCommand
+
+ public let id: String
+ public var typingSuggestion: TypingSuggestion
+ public let displayInfo: CommandDisplayInfo?
+ public var replacesMessageSent: Bool
+
+
+ public init(id: String,typingSuggestion: TypingSuggestion,displayInfo: CommandDisplayInfo?,replacesMessageSent: Bool = false)
+ public final class TypingSuggestionOptions
+
+ public var symbol: String
+ public var shouldTriggerOnlyAtStart: Bool
+ public var minimumRequiredCharacters: Int
+
+
+ public init(symbol: String,shouldTriggerOnlyAtStart: Bool = false,minimumRequiredCharacters: Int = 0)
+ public final class MediaAttachment: Identifiable, Equatable
+
+ public let url: URL
+ public let type: MediaAttachmentType
+ public var uploadingState: AttachmentUploadingState?
+ public var id: String
+
+
+ public init(url: URL,type: MediaAttachmentType,uploadingState: AttachmentUploadingState? = nil)
+
+
+ public static func ==(lhs: MediaAttachment,rhs: MediaAttachment)-> Bool
+ public final class InjectedChannelInfo
+
+ public var subtitle: String?
+ public var unreadCount: Int
+ public var timestamp: String?
+ public var lastMessageAt: Date?
+ public var latestMessages: [ChatMessage]?
+
+
+ public init(subtitle: String? = nil,unreadCount: Int,timestamp: String? = nil,lastMessageAt: Date? = nil,latestMessages: [ChatMessage]? = nil)
+ public final class ChannelAction: Identifiable
+
+ public var id: String
+ public let title: String
+ public let iconName: String
+ public let action: () -> Void
+ public let confirmationPopup: ConfirmationPopup?
+ public let isDestructive: Bool
+ public var navigationDestination: AnyView?
+
+
+ public init(title: String,iconName: String,action: @escaping () -> Void,confirmationPopup: ConfirmationPopup?,isDestructive: Bool)
+ public final class MediaViewsOptions
+
+ public let selectedIndex: Int
+
+
+ public init(selectedIndex: Int)
+ public final class StreamChatError: Error
+
+ public let errorCode: StreamChatErrorCode
+ public let description: String?
+ public let additionalInfo: [String: Any]?
+ public static let unknown
+ public static let missingData
+ public static let wrongConfig
+ public static let noSuggestionsAvailable
+
+
+ public init(errorCode: StreamChatErrorCode,description: String?,additionalInfo: [String: Any]?)
+ public final class GiphyCommandHandler: CommandHandler
+
+ public let id: String
+ public var displayInfo: CommandDisplayInfo?
+
+
+ public init(commandSymbol: String,id: String = "/giphy")
+
+
+ public func canHandleCommand(in text: String,caretLocation: Int)-> ComposerCommand?
+ public func handleCommand(for text: Binding<String>,selectedRangeLocation: Binding<Int>,command: Binding<ComposerCommand?>,extraData: [String: Any])
+ public func commandHandler(for command: ComposerCommand)-> CommandHandler?
+ public func showSuggestions(for command: ComposerCommand)-> Future<SuggestionInfo, Error>
+ public final class AddUsersOptions
+
+ public let loadedUsers: [ChatUser]
+
+
+ public init(loadedUsers: [ChatUser])
+ public final class MessageAction: Identifiable, Equatable
+
+ public var id: String
+ public let title: String
+ public let iconName: String
+ public let action: () -> Void
+ public let confirmationPopup: ConfirmationPopup?
+ public let isDestructive: Bool
+ public var navigationDestination: AnyView?
+
+
+ public init(id: String = UUID().uuidString,title: String,iconName: String,action: @escaping () -> Void,confirmationPopup: ConfirmationPopup?,isDestructive: Bool)
+
+
+ public static func ==(lhs: MessageAction,rhs: MessageAction)-> Bool
+ public final class ChannelListConfig
+
+ public var messageRelativeDateFormatEnabled: Bool
+ public var showChannelListDividerOnLastItem: Bool
+ public var channelItemMutedStyle: ChannelItemMutedLayoutStyle
+
+
+ public init(messageRelativeDateFormatEnabled: Bool = false,showChannelListDividerOnLastItem: Bool = true,channelItemMutedStyle: ChannelItemMutedLayoutStyle = .default)
+ public final class ChannelAvatarViewOptions
+
+ public var showOnlineIndicator: Bool
+ public var size: CGSize
+ public var avatar: UIImage?
+
+
+ public init(showOnlineIndicator: Bool,size: CGSize = .defaultAvatarSize,avatar: UIImage? = nil)
+ public final class MessageDisplayInfo
+
+ public let message: ChatMessage
+ public let frame: CGRect
+ public let contentWidth: CGFloat
+ public let isFirst: Bool
+ public var showsMessageActions: Bool
+ public var showsBottomContainer: Bool
+ public var keyboardWasShown: Bool
+
+
+ public init(message: ChatMessage,frame: CGRect,contentWidth: CGFloat,isFirst: Bool,showsMessageActions: Bool = true,showsBottomContainer: Bool = true,keyboardWasShown: Bool = false)
+ public final class SuggestionInfo
+
+ public let key: String
+ public let value: Any
+
+
+ public init(key: String,value: Any)
+ public final class MessageModifierInfo
+
+ public var message: ChatMessage
+ public var isFirst: Bool
+ public var injectedBackgroundColor: UIColor?
+ public var cornerRadius: CGFloat
+ public var forceLeftToRight
+
+
+ public init(message: ChatMessage,isFirst: Bool,injectedBackgroundColor: UIColor? = nil,cornerRadius: CGFloat = 18,forceLeftToRight: Bool = false)
+ public final class ChannelItemMutedLayoutStyle: Hashable
+
+ public static var `default`: ChannelItemMutedLayoutStyle
+ public static var topRightCorner: ChannelItemMutedLayoutStyle
+ public static var afterChannelName: ChannelItemMutedLayoutStyle
+
+
+ public static func ==(lhs: ChannelItemMutedLayoutStyle,rhs: ChannelItemMutedLayoutStyle)-> Bool
+ public func hash(into hasher: inout Hasher)
+ public final class PollsEntryConfig
+
+ public var configurable: Bool
+ public var defaultValue: Bool
+
+
+ public init(configurable: Bool,defaultValue: Bool)
+ public final class MentionsCommandHandler: CommandHandler
+
+ public let id: String
+ public var displayInfo: CommandDisplayInfo?
+
+
+ public init(channelController: ChatChannelController,userSearchController: ChatUserSearchController? = nil,commandSymbol: String,mentionAllAppUsers: Bool,id: String = "mentions")
+
+
+ public func canHandleCommand(in text: String,caretLocation: Int)-> ComposerCommand?
+ public func handleCommand(for text: Binding<String>,selectedRangeLocation: Binding<Int>,command: Binding<ComposerCommand?>,extraData: [String: Any])
+ public func commandHandler(for command: ComposerCommand)-> CommandHandler?
+ public func showSuggestions(for command: ComposerCommand)-> Future<SuggestionInfo, Error>
+ public final class MessageDisplayOptions
+
+ public let showAvatars: Bool
+ public let showAvatarsInGroups: Bool
+ public let showMessageDate: Bool
+ public let showAuthorName: Bool
+ public let animateChanges: Bool
+ public let dateLabelSize: CGFloat
+ public let lastInGroupHeaderSize: CGFloat
+ public let newMessagesSeparatorSize: CGFloat
+ public let minimumSwipeGestureDistance: CGFloat
+ public let currentUserMessageTransition: AnyTransition
+ public let otherUserMessageTransition: AnyTransition
+ public let shouldAnimateReactions: Bool
+ public let reactionsPlacement: ReactionsPlacement
+ public let showOriginalTranslatedButton: Bool
+ public let messageLinkDisplayResolver: (ChatMessage) -> [NSAttributedString.Key: Any]
+ public let spacerWidth: (CGFloat) -> CGFloat
+ public let reactionsTopPadding: (ChatMessage) -> CGFloat
+ public let dateSeparator: (ChatMessage, ChatMessage) -> Date?
+ public static var defaultLinkDisplay: (ChatMessage) -> [NSAttributedString.Key: Any]
+ public static var defaultSpacerWidth: (CGFloat) -> (CGFloat)
+ public static var defaultReactionsTopPadding: (ChatMessage) -> CGFloat
+
+
+ public init(showAvatars: Bool = true,showAvatarsInGroups: Bool? = nil,showMessageDate: Bool = true,showAuthorName: Bool = true,animateChanges: Bool = true,overlayDateLabelSize: CGFloat = 40,lastInGroupHeaderSize: CGFloat = 0,newMessagesSeparatorSize: CGFloat = 50,minimumSwipeGestureDistance: CGFloat = 20,currentUserMessageTransition: AnyTransition = .identity,otherUserMessageTransition: AnyTransition = .identity,shouldAnimateReactions: Bool = true,reactionsPlacement: ReactionsPlacement = .top,showOriginalTranslatedButton: Bool = false,messageLinkDisplayResolver: @escaping (ChatMessage) -> [NSAttributedString.Key: Any] = MessageDisplayOptions
+ .defaultLinkDisplay,spacerWidth: @escaping (CGFloat) -> CGFloat = MessageDisplayOptions.defaultSpacerWidth,reactionsTopPadding: @escaping (ChatMessage) -> CGFloat = MessageDisplayOptions.defaultReactionsTopPadding,dateSeparator: @escaping (ChatMessage, ChatMessage) -> Date? = MessageDisplayOptions.defaultDateSeparator)
+
+
+ public func showAvatars(for channel: ChatChannel)-> Bool
+ public static func defaultDateSeparator(message: ChatMessage,previous: ChatMessage)-> Date?
+ public final class MediaItem: Identifiable
+
+ public let id: String
+ public let isVideo: Bool
+ public let message: ChatMessage
+ public var videoAttachment: ChatMessageVideoAttachment?
+ public var imageAttachment: ChatMessageImageAttachment?
+ public var mediaAttachment: MediaAttachment?
+
+
+ public init(id: String,isVideo: Bool,message: ChatMessage,videoAttachment: ChatMessageVideoAttachment?,imageAttachment: ChatMessageImageAttachment?)
+ public final class TypingSuggester
+
+ public let options: TypingSuggestionOptions
+
+
+ public init(options: TypingSuggestionOptions)
+
+
+ public func typingSuggestion(in text: String,caretLocation: Int)-> TypingSuggestion?
+ public final class ChatMessageReactionAppearance: ChatMessageReactionAppearanceType
+
+ public let smallIcon: UIImage
+ public let largeIcon: UIImage
+
+
+ public init(smallIcon: UIImage,largeIcon: UIImage)
+ public final class CommandDisplayInfo
+
+ public let displayName: String
+ public let icon: UIImage
+ public let format: String
+ public let isInstant: Bool
+
+
+ public init(displayName: String,icon: UIImage,format: String,isInstant: Bool)
+ public final class ThreadSelectionInfo: Identifiable
+
+ public let id: String
+ public let thread: ChatThread
+
+
+ public init(thread: ChatThread)
+ public final class ComposerConfig
+
+ public var isVoiceRecordingEnabled: Bool
+ public var inputViewMinHeight: CGFloat
+ public var inputViewMaxHeight: CGFloat
+ public var inputViewCornerRadius: CGFloat
+ public var inputFont: UIFont
+ public var gallerySupportedTypes: GallerySupportedTypes
+ public var maxGalleryAssetsCount: Int?
+ public var inputPaddingsConfig: PaddingsConfig
+ public var adjustMessageOnSend: (String) -> (String)
+ public var adjustMessageOnRead: (String) -> (String)
+ @available(
+ *,
+ deprecated,
+ message: """
+ Override the MessageComposerViewModel.inputAttachmentsAsPayloads() in order to convert the message attachments to payloads.
+ """
+ ) public var attachmentPayloadConverter: (ChatMessage) -> [AnyAttachmentPayload]
+ public static var defaultAttachmentPayloadConverter: (ChatMessage) -> [AnyAttachmentPayload]
+
+
+ public init(isVoiceRecordingEnabled: Bool = false,inputViewMinHeight: CGFloat = 38,inputViewMaxHeight: CGFloat = 76,inputViewCornerRadius: CGFloat = 20,inputFont: UIFont = UIFont.preferredFont(forTextStyle: .body),gallerySupportedTypes: GallerySupportedTypes = .imagesAndVideo,maxGalleryAssetsCount: Int? = nil,inputPaddingsConfig: PaddingsConfig = .composerInput,adjustMessageOnSend: @escaping (String) -> (String) = { $0 },adjustMessageOnRead: @escaping (String) -> (String) = { $0 },attachmentPayloadConverter: @escaping (ChatMessage) -> [AnyAttachmentPayload]
+ = ComposerConfig.defaultAttachmentPayloadConverter)
+ public final class AddedVoiceRecording: Identifiable, Equatable
+
+ public var id: String
+ public let url: URL
+ public let duration: TimeInterval
+ public let waveform: [Float]
+
+
+ public init(url: URL,duration: TimeInterval,waveform: [Float])
+
+
+ public static func ==(lhs: AddedVoiceRecording,rhs: AddedVoiceRecording)-> Bool
+ public final class MessageListConfig
+
+ public let messageListType: MessageListType
+ public let typingIndicatorPlacement: TypingIndicatorPlacement
+ public let groupMessages: Bool
+ public let messageDisplayOptions: MessageDisplayOptions
+ public let messagePaddings: MessagePaddings
+ public let dateIndicatorPlacement: DateIndicatorPlacement
+ public let pageSize: Int
+ public let messagePopoverEnabled: Bool
+ public let doubleTapOverlayEnabled: Bool
+ public let becomesFirstResponderOnOpen: Bool
+ public let resignsFirstResponderOnScrollDown: Bool
+ public let updateChannelsFromMessageList: Bool
+ public let maxTimeIntervalBetweenMessagesInGroup: TimeInterval
+ public let cacheSizeOnChatDismiss: Int
+ public let iPadSplitViewEnabled: Bool
+ public let scrollingAnchor: UnitPoint
+ public let showNewMessagesSeparator: Bool
+ public let handleTabBarVisibility: Bool
+ public let messageListAlignment: MessageListAlignment
+ public let uniqueReactionsEnabled: Bool
+ public let localLinkDetectionEnabled: Bool
+ public let isMessageEditedLabelEnabled: Bool
+ public let markdownSupportEnabled: Bool
+ public let userBlockingEnabled: Bool
+ public let hidesCommandsOverlayOnMessageListTap: Bool
+ public let hidesAttachmentsPickersOnMessageListTap: Bool
+ public let bouncedMessagesAlertActionsEnabled: Bool
+ public let skipEditedMessageLabel: (ChatMessage) -> Bool
+ public let draftMessagesEnabled: Bool
+ public let downloadFileAttachmentsEnabled: Bool
+ public let highlightMessageWhenJumping: Bool
+
+
+ public init(messageListType: MessageListType = .messaging,typingIndicatorPlacement: TypingIndicatorPlacement = .bottomOverlay,groupMessages: Bool = true,messageDisplayOptions: MessageDisplayOptions = MessageDisplayOptions(),messagePaddings: MessagePaddings = MessagePaddings(),dateIndicatorPlacement: DateIndicatorPlacement = .overlay,pageSize: Int = 25,messagePopoverEnabled: Bool = true,doubleTapOverlayEnabled: Bool = false,becomesFirstResponderOnOpen: Bool = false,resignsFirstResponderOnScrollDown: Bool = true,updateChannelsFromMessageList: Bool = false,maxTimeIntervalBetweenMessagesInGroup: TimeInterval = 60,cacheSizeOnChatDismiss: Int = 1024 * 1024 * 100,iPadSplitViewEnabled: Bool = true,scrollingAnchor: UnitPoint = .center,showNewMessagesSeparator: Bool = true,highlightMessageWhenJumping: Bool = true,handleTabBarVisibility: Bool = true,messageListAlignment: MessageListAlignment = .standard,uniqueReactionsEnabled: Bool = false,localLinkDetectionEnabled: Bool = true,isMessageEditedLabelEnabled: Bool = true,markdownSupportEnabled: Bool = true,userBlockingEnabled: Bool = false,bouncedMessagesAlertActionsEnabled: Bool = true,skipEditedMessageLabel: @escaping (ChatMessage) -> Bool = { _ in false },draftMessagesEnabled: Bool = false,downloadFileAttachmentsEnabled: Bool = false,hidesCommandsOverlayOnMessageListTap: Bool = true,hidesAttachmentsPickersOnMessageListTap: Bool = true)
+ @propertyWrapper public final class Injected
+
+ public var wrappedValue: T
+
+
+ public init(_ keyPath: WritableKeyPath<InjectedValues, T>)
- public struct ChannelSelectionInfo: Identifiable
-
- public let id: String
- public let channel: ChatChannel
- public let message: ChatMessage?
- public var injectedChannelInfo: InjectedChannelInfo?
- public var searchType: ChannelListSearchType
-
-
- public init(channel: ChatChannel,message: ChatMessage?,searchType: ChannelListSearchType = .messages)
- public struct ChannelListSearchType: Equatable
-
- public static var channels
- public static var messages
- public struct InjectedChannelInfo
-
- public var subtitle: String?
- public var unreadCount: Int
- public var timestamp: String?
- public var lastMessageAt: Date?
- public var latestMessages: [ChatMessage]?
-
-
- public init(subtitle: String? = nil,unreadCount: Int,timestamp: String? = nil,lastMessageAt: Date? = nil,latestMessages: [ChatMessage]? = nil)
- public struct MessageActionInfo
-
- public let message: ChatMessage
- public let identifier: String
-
-
- public init(message: ChatMessage,identifier: String)
- public struct MessageModifierInfo
-
- public var message: ChatMessage
- public var isFirst: Bool
- public var injectedBackgroundColor: UIColor?
- public var cornerRadius: CGFloat
- public var forceLeftToRight
-
-
- public init(message: ChatMessage,isFirst: Bool,injectedBackgroundColor: UIColor? = nil,cornerRadius: CGFloat = 18,forceLeftToRight: Bool = false)
- public struct GiphyCommandHandler: CommandHandler
-
- public let id: String
- public var displayInfo: CommandDisplayInfo?
-
-
- public init(commandSymbol: String,id: String = "/giphy")
-
-
- public func canHandleCommand(in text: String,caretLocation: Int)-> ComposerCommand?
- public func handleCommand(for text: Binding<String>,selectedRangeLocation: Binding<Int>,command: Binding<ComposerCommand?>,extraData: [String: Any])
- public func commandHandler(for command: ComposerCommand)-> CommandHandler?
- public func showSuggestions(for command: ComposerCommand)-> Future<SuggestionInfo, Error>
- public struct ChannelListConfig
-
- public var messageRelativeDateFormatEnabled: Bool
- public var showChannelListDividerOnLastItem: Bool
- public var channelItemMutedStyle: ChannelItemMutedLayoutStyle
-
-
- public init(messageRelativeDateFormatEnabled: Bool = false,showChannelListDividerOnLastItem: Bool = true,channelItemMutedStyle: ChannelItemMutedLayoutStyle = .default)
- public struct ChannelAvatarViewOptions
-
- public var showOnlineIndicator: Bool
- public var size: CGSize
- public var avatar: UIImage?
-
-
- public init(showOnlineIndicator: Bool,size: CGSize = .defaultAvatarSize,avatar: UIImage? = nil)
- public struct LateResize: ImageProcessing
-
- public var identifier: String
-
-
- public init(sizeProvider: @escaping @Sendable () -> CGSize)
-
-
- public func process(_ image: PlatformImage)-> PlatformImage?
- public struct ColorPalette
-
- public var tintColor: Color
- public var text: UIColor
- public var textInverted: UIColor
- public var textLowEmphasis: UIColor
- public var staticColorText: UIColor
- public var subtitleText: UIColor
- public var highlightedColorForColor: (UIColor) -> UIColor
- public var disabledColorForColor: (UIColor) -> UIColor
- public var unselectedColorForColor: (UIColor) -> UIColor
- public var background: UIColor
- public var background1: UIColor
- public var background2: UIColor
- public var background3: UIColor
- public var background4: UIColor
- public var background5: UIColor
- public var background6: UIColor
- public var background7: UIColor
- public var background8: UIColor
- public var overlayBackground: UIColor
- public var popoverBackground: UIColor
- public var highlightedBackground: UIColor
- public var highlightedAccentBackground: UIColor
- public var highlightedAccentBackground1: UIColor
- public var pinnedBackground: UIColor
- public var messageCellHighlightBackground: UIColor
- public var shadow: UIColor
- public var lightBorder: UIColor
- public var innerBorder: UIColor
- public var border: UIColor
- public var border2: UIColor
- public var border3: UIColor
- public var alert: UIColor
- public var alternativeActiveTint: UIColor
- public var inactiveTint: UIColor
- public var alternativeInactiveTint: UIColor
- public lazy var messageCurrentUserBackground: [UIColor]
- public lazy var messageCurrentUserEmphemeralBackground: [UIColor]
- public lazy var messageOtherUserBackground: [UIColor]
- public lazy var messageCurrentUserTextColor: UIColor
- public lazy var messageOtherUserTextColor: UIColor
- public lazy var quotedMessageBackgroundCurrentUser: UIColor
- public lazy var quotedMessageBackgroundOtherUser: UIColor
- public lazy var reactionCurrentUserColor: UIColor?
- public lazy var reactionOtherUserColor: UIColor?
- public lazy var selectedReactionBackgroundColor: UIColor?
- public var voiceMessageControlBackground: UIColor
- public var messageLinkAttachmentAuthorColor: Color
- public var messageLinkAttachmentTitleColor: Color
- public var messageLinkAttachmentTextColor: Color
- public lazy var composerPlaceholderColor: UIColor
- public lazy var composerInputBackground: UIColor
- public lazy var composerInputHighlightedBorder: UIColor
- public var navigationBarGlyph: UIColor
- public var navigationBarTitle: UIColor
- public var navigationBarSubtitle: UIColor
- public var navigationBarTintColor: Color
- public var navigationBarBackground: UIColor?
- public var bannerBackgroundColor: UIColor
-
-
- public init()
- public struct ChatThreadListItemViewModel
-
- public var parentMessageText: String
- public var latestReplyMessageText: String
- public var latestReplyTimestampText: String
- public var draftReplyText: String?
- public var unreadRepliesCount: Int
- public var latestReplyAuthorNameText: String
- public var isLatestReplyAuthorOnline: Bool
- public var latestReplyAuthorImageURL: URL?
- public var latestReplyAuthorId: String
- public var channelNameText: String
-
-
- public init(thread: ChatThread)
- public struct ConfirmationPopup
-
- public let title: String
- public let message: String?
- public let buttonTitle: String
-
-
- public init(title: String,message: String?,buttonTitle: String)
- public struct ChatMessageReactionAppearance: ChatMessageReactionAppearanceType
-
- public let smallIcon: UIImage
- public let largeIcon: UIImage
-
-
- public init(smallIcon: UIImage,largeIcon: UIImage)
- public struct MediaViewsOptions
-
- public let selectedIndex: Int
-
-
- public init(selectedIndex: Int)
- public struct PollsEntryConfig
-
- public var configurable: Bool
- public var defaultValue: Bool
-
-
- public init(configurable: Bool,defaultValue: Bool)
- public struct TypingSuggester
-
- public let options: TypingSuggestionOptions
-
-
- public init(options: TypingSuggestionOptions)
-
-
- public func typingSuggestion(in text: String,caretLocation: Int)-> TypingSuggestion?
- public struct PaddingsConfig
-
- public let top: CGFloat
- public let bottom: CGFloat
- public let leading: CGFloat
- public let trailing: CGFloat
- public var horizontal: CGFloat
- public var vertical: CGFloat
-
-
- public init(top: CGFloat,bottom: CGFloat,leading: CGFloat,trailing: CGFloat)
- public struct MessageListConfig
-
- public let messageListType: MessageListType
- public let typingIndicatorPlacement: TypingIndicatorPlacement
- public let groupMessages: Bool
- public let messageDisplayOptions: MessageDisplayOptions
- public let messagePaddings: MessagePaddings
- public let dateIndicatorPlacement: DateIndicatorPlacement
- public let pageSize: Int
- public let messagePopoverEnabled: Bool
- public let doubleTapOverlayEnabled: Bool
- public let becomesFirstResponderOnOpen: Bool
- public let resignsFirstResponderOnScrollDown: Bool
- public let updateChannelsFromMessageList: Bool
- public let maxTimeIntervalBetweenMessagesInGroup: TimeInterval
- public let cacheSizeOnChatDismiss: Int
- public let iPadSplitViewEnabled: Bool
- public let scrollingAnchor: UnitPoint
- public let showNewMessagesSeparator: Bool
- public let handleTabBarVisibility: Bool
- public let messageListAlignment: MessageListAlignment
- public let uniqueReactionsEnabled: Bool
- public let localLinkDetectionEnabled: Bool
- public let isMessageEditedLabelEnabled: Bool
- public let markdownSupportEnabled: Bool
- public let userBlockingEnabled: Bool
- public let hidesCommandsOverlayOnMessageListTap: Bool
- public let hidesAttachmentsPickersOnMessageListTap: Bool
- public let bouncedMessagesAlertActionsEnabled: Bool
- public let skipEditedMessageLabel: (ChatMessage) -> Bool
- public let draftMessagesEnabled: Bool
- public let downloadFileAttachmentsEnabled: Bool
- public let highlightMessageWhenJumping: Bool
-
-
- public init(messageListType: MessageListType = .messaging,typingIndicatorPlacement: TypingIndicatorPlacement = .bottomOverlay,groupMessages: Bool = true,messageDisplayOptions: MessageDisplayOptions = MessageDisplayOptions(),messagePaddings: MessagePaddings = MessagePaddings(),dateIndicatorPlacement: DateIndicatorPlacement = .overlay,pageSize: Int = 25,messagePopoverEnabled: Bool = true,doubleTapOverlayEnabled: Bool = false,becomesFirstResponderOnOpen: Bool = false,resignsFirstResponderOnScrollDown: Bool = true,updateChannelsFromMessageList: Bool = false,maxTimeIntervalBetweenMessagesInGroup: TimeInterval = 60,cacheSizeOnChatDismiss: Int = 1024 * 1024 * 100,iPadSplitViewEnabled: Bool = true,scrollingAnchor: UnitPoint = .center,showNewMessagesSeparator: Bool = true,highlightMessageWhenJumping: Bool = true,handleTabBarVisibility: Bool = true,messageListAlignment: MessageListAlignment = .standard,uniqueReactionsEnabled: Bool = false,localLinkDetectionEnabled: Bool = true,isMessageEditedLabelEnabled: Bool = true,markdownSupportEnabled: Bool = true,userBlockingEnabled: Bool = false,bouncedMessagesAlertActionsEnabled: Bool = true,skipEditedMessageLabel: @escaping (ChatMessage) -> Bool = { _ in false },draftMessagesEnabled: Bool = false,downloadFileAttachmentsEnabled: Bool = false,hidesCommandsOverlayOnMessageListTap: Bool = true,hidesAttachmentsPickersOnMessageListTap: Bool = true)
- public struct CommandDisplayInfo
-
- public let displayName: String
- public let icon: UIImage
- public let format: String
- public let isInstant: Bool
-
-
- public init(displayName: String,icon: UIImage,format: String,isInstant: Bool)
- public struct ComposerConfig
-
- public var isVoiceRecordingEnabled: Bool
- public var inputViewMinHeight: CGFloat
- public var inputViewMaxHeight: CGFloat
- public var inputViewCornerRadius: CGFloat
- public var inputFont: UIFont
- public var gallerySupportedTypes: GallerySupportedTypes
- public var maxGalleryAssetsCount: Int?
- public var inputPaddingsConfig: PaddingsConfig
- public var adjustMessageOnSend: (String) -> (String)
- public var adjustMessageOnRead: (String) -> (String)
- @available(
- *,
- deprecated,
- message: """
- Override the MessageComposerViewModel.inputAttachmentsAsPayloads() in order to convert the message attachments to payloads.
- """
- ) public var attachmentPayloadConverter: (ChatMessage) -> [AnyAttachmentPayload]
- public static var defaultAttachmentPayloadConverter: (ChatMessage) -> [AnyAttachmentPayload]
-
-
- public init(isVoiceRecordingEnabled: Bool = false,inputViewMinHeight: CGFloat = 38,inputViewMaxHeight: CGFloat = 76,inputViewCornerRadius: CGFloat = 20,inputFont: UIFont = UIFont.preferredFont(forTextStyle: .body),gallerySupportedTypes: GallerySupportedTypes = .imagesAndVideo,maxGalleryAssetsCount: Int? = nil,inputPaddingsConfig: PaddingsConfig = .composerInput,adjustMessageOnSend: @escaping (String) -> (String) = { $0 },adjustMessageOnRead: @escaping (String) -> (String) = { $0 },attachmentPayloadConverter: @escaping (ChatMessage) -> [AnyAttachmentPayload]
- = ComposerConfig.defaultAttachmentPayloadConverter)
- public struct ChannelItemMutedLayoutStyle: Hashable
-
- public static var `default`: ChannelItemMutedLayoutStyle
- public static var topRightCorner: ChannelItemMutedLayoutStyle
- public static var afterChannelName: ChannelItemMutedLayoutStyle
- public struct MediaAttachment: Identifiable, Equatable
-
- public let url: URL
- public let type: MediaAttachmentType
- public var uploadingState: AttachmentUploadingState?
- public var id: String
-
-
- public init(url: URL,type: MediaAttachmentType,uploadingState: AttachmentUploadingState? = nil)
-
-
- public static func ==(lhs: MediaAttachment,rhs: MediaAttachment)-> Bool
- public struct ParticipantAction: Identifiable
-
- public var id: String
- public let title: String
- public let iconName: String
- public let action: () -> Void
- public let confirmationPopup: ConfirmationPopup?
- public let isDestructive: Bool
- public var navigationDestination: AnyView?
-
-
- public init(title: String,iconName: String,action: @escaping () -> Void,confirmationPopup: ConfirmationPopup?,isDestructive: Bool)
- public struct TypingSuggestionOptions
-
- public var symbol: String
- public var shouldTriggerOnlyAtStart: Bool
- public var minimumRequiredCharacters: Int
-
-
- public init(symbol: String,shouldTriggerOnlyAtStart: Bool = false,minimumRequiredCharacters: Int = 0)
- public struct PHFetchResultCollection: RandomAccessCollection, Equatable
-
- public let fetchResult: PHFetchResult<PHAsset>
- public var endIndex: Int
- public var startIndex: Int
-
-
- public subscript(position: Int)-> PHAsset
-
-
- public init(fetchResult: PHFetchResult<PHAsset>)
- public struct MessageDisplayOptions
-
- public let showAvatars: Bool
- public let showAvatarsInGroups: Bool
- public let showMessageDate: Bool
- public let showAuthorName: Bool
- public let animateChanges: Bool
- public let dateLabelSize: CGFloat
- public let lastInGroupHeaderSize: CGFloat
- public let newMessagesSeparatorSize: CGFloat
- public let minimumSwipeGestureDistance: CGFloat
- public let currentUserMessageTransition: AnyTransition
- public let otherUserMessageTransition: AnyTransition
- public let shouldAnimateReactions: Bool
- public let reactionsPlacement: ReactionsPlacement
- public let showOriginalTranslatedButton: Bool
- public let messageLinkDisplayResolver: (ChatMessage) -> [NSAttributedString.Key: Any]
- public let spacerWidth: (CGFloat) -> CGFloat
- public let reactionsTopPadding: (ChatMessage) -> CGFloat
- public let dateSeparator: (ChatMessage, ChatMessage) -> Date?
- public static var defaultLinkDisplay: (ChatMessage) -> [NSAttributedString.Key: Any]
- public static var defaultSpacerWidth: (CGFloat) -> (CGFloat)
- public static var defaultReactionsTopPadding: (ChatMessage) -> CGFloat
-
-
- public init(showAvatars: Bool = true,showAvatarsInGroups: Bool? = nil,showMessageDate: Bool = true,showAuthorName: Bool = true,animateChanges: Bool = true,overlayDateLabelSize: CGFloat = 40,lastInGroupHeaderSize: CGFloat = 0,newMessagesSeparatorSize: CGFloat = 50,minimumSwipeGestureDistance: CGFloat = 20,currentUserMessageTransition: AnyTransition = .identity,otherUserMessageTransition: AnyTransition = .identity,shouldAnimateReactions: Bool = true,reactionsPlacement: ReactionsPlacement = .top,showOriginalTranslatedButton: Bool = false,messageLinkDisplayResolver: @escaping (ChatMessage) -> [NSAttributedString.Key: Any] = MessageDisplayOptions
- .defaultLinkDisplay,spacerWidth: @escaping (CGFloat) -> CGFloat = MessageDisplayOptions.defaultSpacerWidth,reactionsTopPadding: @escaping (ChatMessage) -> CGFloat = MessageDisplayOptions.defaultReactionsTopPadding,dateSeparator: @escaping (ChatMessage, ChatMessage) -> Date? = MessageDisplayOptions.defaultDateSeparator)
-
-
- public func showAvatars(for channel: ChatChannel)-> Bool
- public static func defaultDateSeparator(message: ChatMessage,previous: ChatMessage)-> Date?
- public struct MediaItem: Identifiable
-
- public let id: String
- public let isVideo: Bool
- public let message: ChatMessage
- public var videoAttachment: ChatMessageVideoAttachment?
- public var imageAttachment: ChatMessageImageAttachment?
- public var mediaAttachment: MediaAttachment?
-
-
- public init(id: String,isVideo: Bool,message: ChatMessage,videoAttachment: ChatMessageVideoAttachment?,imageAttachment: ChatMessageImageAttachment?)
- public struct TypingSuggestion
-
- public let text: String
- public let locationRange: NSRange
-
-
- public init(text: String,locationRange: NSRange)
- @propertyWrapper public struct Injected
-
- public var wrappedValue: T
-
-
- public init(_ keyPath: WritableKeyPath<InjectedValues, T>)
- public struct PollsConfig
-
- public var multipleAnswers: PollsEntryConfig
- public var anonymousPoll: PollsEntryConfig
- public var suggestAnOption: PollsEntryConfig
- public var addComments: PollsEntryConfig
- public var maxVotesPerPerson: PollsEntryConfig
-
-
- public init(multipleAnswers: PollsEntryConfig = .default,anonymousPoll: PollsEntryConfig = .default,suggestAnOption: PollsEntryConfig = .default,addComments: PollsEntryConfig = .default,maxVotesPerPerson: PollsEntryConfig = .default)
- public struct ThreadSelectionInfo: Identifiable
-
- public let id: String
- public let thread: ChatThread
-
-
- public init(thread: ChatThread)
- public struct Fonts
-
- public var caption1
- public var footnoteBold
- public var footnote
- public var subheadline
- public var subheadlineBold
- public var body
- public var bodyBold
- public var bodyItalic
- public var headline
- public var headlineBold
- public var title
- public var title2
- public var title3
- public var emoji
-
-
- public init()
- public struct UserDisplayInfo
-
- public let id: String
- public let name: String
- public let imageURL: URL?
- public let role: UserRole?
- public let size: CGSize?
- public let extraData: [String: RawJSON]
-
-
- public init(id: String,name: String,imageURL: URL?,role: UserRole? = nil,size: CGSize? = nil,extraData: [String: RawJSON] = [:])
- public struct MessageDisplayInfo
-
- public let message: ChatMessage
- public let frame: CGRect
- public let contentWidth: CGFloat
- public let isFirst: Bool
- public var showsMessageActions: Bool
- public var showsBottomContainer: Bool
- public var keyboardWasShown: Bool
-
-
- public init(message: ChatMessage,frame: CGRect,contentWidth: CGFloat,isFirst: Bool,showsMessageActions: Bool = true,showsBottomContainer: Bool = true,keyboardWasShown: Bool = false)
- public struct MediaAttachmentType: RawRepresentable
-
- public let rawValue: String
- public static let image
- public static let video
-
-
- public init(rawValue: String)
- public struct InjectedValues
-
- public static subscript<K>(key: K.Type)-> K.Value where K: InjectionKey
- public static subscript<T>(_ keyPath: WritableKeyPath<InjectedValues, T>)-> T
- public struct ComposerCommand
-
- public let id: String
- public var typingSuggestion: TypingSuggestion
- public let displayInfo: CommandDisplayInfo?
- public var replacesMessageSent: Bool
-
-
- public init(id: String,typingSuggestion: TypingSuggestion,displayInfo: CommandDisplayInfo?,replacesMessageSent: Bool = false)
- public struct ParticipantInfo: Identifiable
-
- public var id: String
- public let chatUser: ChatUser
- public let displayName: String
- public let onlineInfoText: String
- public let isDeactivated: Bool
-
-
- public init(chatUser: ChatUser,displayName: String,onlineInfoText: String,isDeactivated: Bool = false)
- public struct AddUsersOptions
-
- public let loadedUsers: [ChatUser]
-
-
- public init(loadedUsers: [ChatUser])
- public struct AddedAsset: Identifiable, Equatable
-
- public let image: UIImage
- public let id: String
- public let url: URL
- public let type: AssetType
- public var extraData: [String: RawJSON]
- public var payload: AttachmentPayload?
-
-
- public init(image: UIImage,id: String,url: URL,type: AssetType,extraData: [String: RawJSON] = [:],payload: AttachmentPayload? = nil)
-
-
- public static func ==(lhs: AddedAsset,rhs: AddedAsset)-> Bool
- public struct AddedVoiceRecording: Identifiable, Equatable
-
- public var id: String
- public let url: URL
- public let duration: TimeInterval
- public let waveform: [Float]
-
-
- public init(url: URL,duration: TimeInterval,waveform: [Float])
- public struct MessageAction: Identifiable, Equatable
-
- public var id: String
- public let title: String
- public let iconName: String
- public let action: () -> Void
- public let confirmationPopup: ConfirmationPopup?
- public let isDestructive: Bool
- public var navigationDestination: AnyView?
-
-
- public init(id: String = UUID().uuidString,title: String,iconName: String,action: @escaping () -> Void,confirmationPopup: ConfirmationPopup?,isDestructive: Bool)
-
-
- public static func ==(lhs: MessageAction,rhs: MessageAction)-> Bool
- public struct QuotedMessageContentViewOptions
-
- public let quotedMessage: ChatMessage
- public let fillAvailableSpace: Bool
- public let attachmentSize: CGSize
-
-
- public init(quotedMessage: ChatMessage,fillAvailableSpace: Bool,attachmentSize: CGSize = CGSize(width: 36, height: 36))
- public struct StreamChatError: Error
-
- public let errorCode: StreamChatErrorCode
- public let description: String?
- public let additionalInfo: [String: Any]?
- public static let unknown
- public static let missingData
- public static let wrongConfig
- public static let noSuggestionsAvailable
- public struct SuggestionInfo
-
- public let key: String
- public let value: Any
-
-
- public init(key: String,value: Any)
- public struct CustomAttachment: Identifiable, Equatable
-
- public let id: String
- public let content: AnyAttachmentPayload
-
-
- public init(id: String,content: AnyAttachmentPayload)
-
-
- public static func ==(lhs: CustomAttachment,rhs: CustomAttachment)-> Bool
- public struct MessagePaddings
-
- public let horizontal: CGFloat
- public let quotedViewPadding: CGFloat
- public let singleBottom: CGFloat
- public let groupBottom: CGFloat
-
-
- public init(horizontal: CGFloat = 8,quotedViewPadding: CGFloat = 8,singleBottom: CGFloat = 8,groupBottom: CGFloat = 2)
- public struct ChannelAction: Identifiable
-
- public var id: String
- public let title: String
- public let iconName: String
- public let action: () -> Void
- public let confirmationPopup: ConfirmationPopup?
- public let isDestructive: Bool
- public var navigationDestination: AnyView?
-
-
- public init(title: String,iconName: String,action: @escaping () -> Void,confirmationPopup: ConfirmationPopup?,isDestructive: Bool)
- public struct MentionsCommandHandler: CommandHandler
-
- public let id: String
- public var displayInfo: CommandDisplayInfo?
-
-
- public init(channelController: ChatChannelController,userSearchController: ChatUserSearchController? = nil,commandSymbol: String,mentionAllAppUsers: Bool,id: String = "mentions")
-
-
- public func canHandleCommand(in text: String,caretLocation: Int)-> ComposerCommand?
- public func handleCommand(for text: Binding<String>,selectedRangeLocation: Binding<Int>,command: Binding<ComposerCommand?>,extraData: [String: Any])
- public func commandHandler(for command: ComposerCommand)-> CommandHandler?
- public func showSuggestions(for command: ComposerCommand)-> Future<SuggestionInfo, Error>
|
StreamChatSwiftUI XCSize
Show 174 more objects
|
|


π Issue Links
Provide all Linear and/or Github issues related to this PR, if applicable.
π― Goal
Describe why we are making this change.
π Summary
Provide bullet points with the most important changes in the codebase.
π Implementation
Provide a detailed description of the implementation and explain your decisions if you find them relevant.
π¨ Showcase
Add relevant screenshots and/or videos/gifs to easily see what this PR changes, if applicable.
π§ͺ Manual Testing Notes
Explain how this change can be tested manually, if applicable.
βοΈ Contributor Checklist
docs-contentrepoSummary by CodeRabbit
Release Notes