-
Notifications
You must be signed in to change notification settings - Fork 222
Add Static Location and Live Location Support #3531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't test a lot, since a had constant crashes (shared details on Slack). Let's figure that out first, then I will do another round.
...treamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift
Outdated
Show resolved
Hide resolved
5e5a9ab
to
70218c2
Compare
Generated by 🚫 Danger |
SDK Size
|
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
Outdated
Show resolved
Hide resolved
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
Outdated
Show resolved
Hide resolved
Sources/StreamChat/Controllers/MessageController/MessageController.swift
Outdated
Show resolved
Hide resolved
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
Outdated
Show resolved
Hide resolved
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
Show resolved
Hide resolved
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
Outdated
Show resolved
Hide resolved
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
Outdated
Show resolved
Hide resolved
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Left few comments, let me know what you think.
...eamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift
Outdated
Show resolved
Hide resolved
...eamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift
Outdated
Show resolved
Hide resolved
Sources/StreamChat/Controllers/MessageController/MessageController.swift
Outdated
Show resolved
Hide resolved
Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
Show resolved
Hide resolved
...eamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift
Show resolved
Hide resolved
...eamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! ✅
...eamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift
Show resolved
Hide resolved
502d6b5
to
f1a4fac
Compare
## Walkthrough
This change introduces comprehensive support for location attachments in the StreamChat iOS SDK. It adds static and live location sharing, new data models, database schema updates, API endpoints, controller methods, UI components, and extensive tests. Previous custom location attachment code is removed and replaced with a more robust, integrated solution for sharing and managing user locations in chat messages.
## Changes
| File(s) / Group | Change Summary |
|---|---|
| `CHANGELOG.md` | Documents new location attachment features and partial message updates. |
| `DemoApp/Info.plist` | Adds location usage descriptions, background location mode, and reorganizes privacy keys. |
| `DemoApp/LocationProvider.swift` | Adds a singleton class for location permission and updates using `CLLocationManager`. |
| `DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift` | Enables location attachments by default. |
| `DemoApp/Screens/DemoAppTabBarController.swift` | Integrates live location sharing lifecycle and updates via delegate methods. |
| `DemoApp/StreamChat/Components/CustomAttachments/DemoAttachmentViewCatalog.swift` | Switches location detection to use new `sharedLocation` property. |
| `DemoApp/StreamChat/Components/CustomAttachments/DemoComposerVC.swift` | Replaces dummy locations with device location, adds static/live location actions, and permission handling. |
| `DemoApp/StreamChat/Components/CustomAttachments/DemoQuotedChatMessageView.swift` | Refactors preview logic to use `sharedLocation` and differentiate live/static. |
| `DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload.swift`<br>`LocationAttachmentPayload+AttachmentViewProvider.swift` | Removes legacy custom location attachment types and preview provider. |
| `DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift` | Refactors to support live location UI, avatar, stop sharing, and snapshot caching. |
| `DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swift` | Changes delegate protocol to use new `SharedLocation`, adds stop sharing, updates VC logic. |
| `DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewInjector.swift` | Refactors to use `SharedLocation` and adds tap handlers for stop sharing. |
| `DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift` | Refactors to use `ChatMessageController`, adds live location tracking, banner, and animations. |
| `DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationSharingStatusView.swift`<br>`UserAnnotation.swift`<br>`UserAnnotationView.swift` | Adds new UI components for location sharing status and map annotations. |
| `DemoApp/StreamChat/Components/DemoChatChannelListItemView.swift` | Adds subtitle text for live/static location in channel list. |
| `DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift` | Hides edit action for messages with location attachments. |
| `DemoApp/StreamChat/StreamChatWrapper+DemoApp.swift` | Removes legacy location attachment injector registration. |
| `Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift` | Re-formats method signature. |
| `Sources/StreamChat/APIClient/Endpoints/MessageEndpoints.swift` | Adds partial update endpoint and support for updating attachments, text, extraData. |
| `Sources/StreamChat/APIClient/Endpoints/Payloads/IdentifiableModel.swift` | Adds `IdentifiableModel` conformance to `LocationDTO`. |
| `Sources/StreamChat/APIClient/Endpoints/Payloads/LocationPayloads.swift` | Adds new payload types for shared/live location. |
| `Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift` | Adds support for location property in message payloads. |
| `Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift`<br>`EndpointPath+OfflineRequest.swift` | Adds `.liveLocations` endpoint path. |
| `Sources/StreamChat/APIClient/Endpoints/LocationEndpoints.swift` | Adds endpoints for updating, stopping, and fetching live locations. |
| `Sources/StreamChat/Controllers/ChannelController/ChannelController.swift` | Adds methods for sending static/live location messages and error handling. |
| `Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift` | Adds live location observation, update, throttling, and delegate notifications. |
| `Sources/StreamChat/Controllers/MessageController/MessageController.swift` | Adds partial update, live location update/stop, and related errors. |
| `Sources/StreamChat/Database/DTOs/LocationDTO.swift` | Adds new Core Data entity and logic for location data. |
| `Sources/StreamChat/Database/DTOs/MessageDTO.swift` | Adds location property, fetch methods, and model conversion for location. |
| `Sources/StreamChat/Database/DatabaseSession.swift` | Adds location to message creation, new protocol for saving location, and updates session conformance. |
| `Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/.../contents` | Adds `LocationDTO` entity and relationship to `MessageDTO`. |
| `Sources/StreamChat/Models/ChatMessage.swift` | Adds `sharedLocation` property and updates initializer, equality, and copy methods. |
| `Sources/StreamChat/Models/DraftMessage.swift` | Sets `sharedLocation` to nil in `ChatMessage` init from draft. |
| `Sources/StreamChat/Models/Location/LocationInfo.swift`<br>`SharedLocation.swift` | Adds new models for location info and shared location. |
| `Sources/StreamChat/Models/UserInfo.swift` | Updates documentation comment. |
| `Sources/StreamChat/Repositories/MessageRepository.swift` | Adds method to fetch active live location messages for current user. |
| `Sources/StreamChat/Utils/Throttler.swift` | Makes `Throttler` public for throttling location updates. |
| `Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift` | Adds `sharedLocation` to model mapping. |
| `Sources/StreamChat/Workers/ChannelUpdater.swift` | Adds location parameter to message creation. |
| `Sources/StreamChat/Workers/CurrentUserUpdater.swift` | Adds method to load active live locations from backend. |
| `Sources/StreamChat/Workers/MessageUpdater.swift` | Adds partial update, live location update/stop, and makes session extension public. |
| `StreamChat.xcodeproj/project.pbxproj` | Removes old files, adds new location-related files, updates package dependency. |
| `TestTools/StreamChatTestTools/...` | Updates mocks, test data, and spies to support new location features and partial update. |
| `Tests/StreamChatTests/...` | Adds extensive tests for location features, partial updates, and database integration. |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant User
participant App (DemoApp)
participant LocationProvider
participant ChatChannelController
participant MessageUpdater
participant Backend
User->>App: Taps "Share Location"
App->>LocationProvider: getCurrentLocation()
LocationProvider-->>App: CLLocation (lat, lon)
App->>ChatChannelController: sendStaticLocation(location, ...)
ChatChannelController->>MessageUpdater: createNewMessage with location
MessageUpdater->>Backend: Send message with location payload
Backend-->>MessageUpdater: Success/Failure
MessageUpdater-->>ChatChannelController: Result
ChatChannelController-->>App: Completion handler
App-->>User: Shows message in chat sequenceDiagram
participant User
participant App (DemoApp)
participant LocationProvider
participant CurrentChatUserController
participant Backend
User->>App: Starts live location sharing
App->>LocationProvider: startMonitoringLocation()
loop While sharing active
LocationProvider-->>App: didUpdateLocation(CLLocation)
App->>CurrentChatUserController: updateLiveLocation(LocationInfo)
CurrentChatUserController->>Backend: updateLiveLocation API
Backend-->>CurrentChatUserController: Ack
end
User->>App: Stops live location sharing
App->>CurrentChatUserController: stopLiveLocationSharing()
CurrentChatUserController->>Backend: stopLiveLocation API
Backend-->>CurrentChatUserController: Ack
App-->>User: Shows live location ended
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Suggested reviewers
Poem
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 24
🔭 Outside diff range comments (1)
Sources/StreamChat/Workers/MessageUpdater.swift (1)
1033-1052
: 💡 Verification agent🧩 Analysis chain
Extension visibility change requires verification.
The
DatabaseSession
extension was changed from private to public, which expands the API surface. Ensure this change is intentional and that themessageEditableByCurrentUser
method should be publicly accessible.
🏁 Script executed:
#!/bin/bash # Description: Check if the messageEditableByCurrentUser method is used outside this file # Expected: Find usages to confirm the visibility change is necessary rg "messageEditableByCurrentUser" --type swift -A 2 -B 2Length of output: 3204
Review Extension Visibility: Revert to
internal
The
messageEditableByCurrentUser(_:)
helper is only used within theStreamChat
module (inMessageUpdater.swift
andMessageController.swift
). Making itpublic
unnecessarily expands the library’s public API. Please revert its access level tointernal
(the default) unless there’s a specific need for external clients to call it.• Sources/StreamChat/Workers/MessageUpdater.swift
– Declaration ofmessageEditableByCurrentUser
• Sources/StreamChat/Controllers/MessageController/MessageController.swift
– Calls tosession.messageEditableByCurrentUser(self.messageId)
Suggested diff:
- public extension DatabaseSession { + extension DatabaseSession { /// Returns the message if it can be edited by the current user... func messageEditableByCurrentUser(_ messageId: MessageId) throws -> MessageDTO { … } }
🧹 Nitpick comments (24)
Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift (1)
4195-4273
: Well-structured test with minor documentation issue.This test effectively covers the
loadActiveLiveLocationMessages
functionality with comprehensive test scenarios. The setup creates appropriate test data covering different users, channels, and active states, and the assertions properly validate both global and channel-specific filtering.However, there's a misleading comment on line 4220:
(.unique, currentUserId, channel1Id, false) // Current user, channel 1, no locationThe comment says "no location" but the code still creates a live location attachment for this message - it's just inactive (with
stoppedSharing: true
). The comment should be corrected to reflect this.Apply this diff to fix the misleading comment:
- (.unique, currentUserId, channel1Id, false) // Current user, channel 1, no location + (.unique, currentUserId, channel1Id, false) // Current user, channel 1, inactiveTests/StreamChatTests/Models/Attachments/LiveLocationAttachmentPayload_Tests.swift (2)
10-32
: Enhance test to verify true default value behavior.The test name suggests testing default values, but
stoppedSharing
is explicitly provided in the JSON. Consider testing what happens whenstoppedSharing
is omitted to verify the model's default value handling.Consider adding a separate test for truly default values:
+ func test_decodingWithMissingStoppedSharing() throws { + let latitude: Double = 51.5074 + let longitude: Double = -0.1278 + + let json = """ + { + "latitude": \(latitude), + "longitude": \(longitude) + } + """.data(using: .utf8)! + + let payload = try JSONDecoder.stream.decode(LiveLocationAttachmentPayload.self, from: json) + + XCTAssertEqual(payload.latitude, latitude) + XCTAssertEqual(payload.longitude, longitude) + // Verify default value for stoppedSharing when not provided + XCTAssertEqual(payload.stoppedSharing, false) // or true, depending on expected default + }
34-56
: Consider testing stoppedSharing in the extra data test.The extra data test could be more comprehensive by including the
stoppedSharing
field to ensure it works correctly alongside extra data.func test_decodingExtraData() throws { // Create attachment field values let latitude: Double = 51.5074 let longitude: Double = -0.1278 + let stoppedSharing = false let locationName: String = .unique // Create JSON with the given values let json = """ { "latitude": \(latitude), "longitude": \(longitude), + "stopped_sharing": \(stoppedSharing), "locationName": "\(locationName)" } """.data(using: .utf8)! // Decode attachment from JSON let payload = try JSONDecoder.stream.decode(LiveLocationAttachmentPayload.self, from: json) // Assert values are decoded correctly XCTAssertEqual(payload.latitude, latitude) XCTAssertEqual(payload.longitude, longitude) + XCTAssertEqual(payload.stoppedSharing, stoppedSharing) XCTAssertEqual(payload.extraData?["locationName"]?.stringValue, locationName) }Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift (1)
703-724
: Strengthen failure-path assertionsThe test correctly checks that an error is emitted when the current user is missing, but it currently only verifies the error’s type.
It doesn’t assert that theResult
itself is a failure, which means a future refactor that accidentally returns a success value containing an error‐like payload could still pass this test.- repository.getCurrentUserActiveLiveLocationMessages(for: cid) { result in - if case .failure(let error) = result { - receivedError = error - } + repository.getCurrentUserActiveLiveLocationMessages(for: cid) { result in + guard case .failure(let error) = result else { + XCTFail("Expected failure, got \(result)"); return + } + receivedError = errorThis small change hardens the test against false–positives.
DemoApp/StreamChat/Components/CustomAttachments/DemoComposerVC.swift (1)
72-87
: Present permission alert on the correct queue
showLocationPermissionAlert()
is called from potentially arbitrary threads.
Wrap thepresent
call in a main-queue dispatch to prevent runtime warnings:- present(alert, animated: true) + DispatchQueue.main.async { + self.present(alert, animated: true) + }(Alternatively, incorporate this into the previous refactor.)
Tests/StreamChatTests/Workers/MessageUpdater_Tests.swift (1)
2977-3095
: Excellent test coverage for the newupdatePartialMessage
functionality!The new test suite provides comprehensive coverage for the partial message update feature with well-structured tests that follow existing patterns in the codebase. The three test methods effectively cover:
- API Contract Verification - Ensures correct endpoint and payload formatting
- Error Handling - Validates proper network error propagation
- Data Persistence - Confirms successful database updates
The tests are well-organized and use appropriate assertion patterns consistent with the existing test infrastructure.
Minor suggestion for potential improvement:
The attachment payload conversion logic (lines 2987-2996) could potentially be extracted to a helper method if this pattern is reused elsewhere, but it's acceptable as-is since it clearly demonstrates the expected transformation for this specific test case.
Consider extracting the attachment conversion logic to a helper method if similar conversions are needed in other tests:
+ private func convertAttachmentsToMessagePayloads(_ attachments: [AnyAttachmentPayload]) -> [MessageAttachmentPayload] { + return attachments.compactMap { attachment in + guard let payloadData = try? JSONEncoder.default.encode(attachment.payload), + let payloadRawJSON = try? JSONDecoder.default.decode(RawJSON.self, from: payloadData) else { + return nil + } + return MessageAttachmentPayload( + type: attachment.type, + payload: payloadRawJSON + ) + } + }This would simplify the test and make the conversion logic reusable across other test methods.
DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/UserAnnotationView.swift (2)
46-76
: Consider animation performance optimization.The pulsing animation creates new layers each time
startPulsingAnimation()
is called. While the guard statement prevents duplicate layers, consider optimizing for scenarios where animations start/stop frequently during live location updates.Consider reusing the pulse layer instead of recreating it:
func startPulsingAnimation() { - guard pulseLayer == nil else { - return - } - let pulseLayer = CALayer() + if let existingLayer = pulseLayer { + existingLayer.removeAllAnimations() + } else { + let pulseLayer = CALayer() + // ... setup code ... + self.pulseLayer = pulseLayer + } // ... animation setup ... }
20-34
: Consider making avatar size configurable.The hardcoded size of 40x40 points works well for most use cases, but consider making it configurable for different map zoom levels or accessibility requirements.
-private var size: CGSize = .init(width: 40, height: 40) +private var avatarSize: CGSize +init(annotation: MKAnnotation?, reuseIdentifier: String?, size: CGSize = .init(width: 40, height: 40)) { + self.avatarSize = size // ... rest of init ... + frame = CGRect(x: 0, y: 0, width: avatarSize.width, height: avatarSize.height) + layer.cornerRadius = avatarSize.width / 2StreamChat.xcodeproj/project.pbxproj (3)
1567-1568
: Remove duplicateSharedLocation.swift
build entries
There are two identical PBXBuildFile entries forSharedLocation.swift
. Please remove one to avoid duplication in the build phase.
1671-1672
: Remove duplicateLocationDTO.swift
build entries
Duplicate build entries forLocationDTO.swift
appear in this section. Keeping a single entry will prevent potential build warnings.
1838-1842
: Remove duplicate entries forLocationInfo.swift
andThrottler.swift
This hunk introduces duplicate PBXBuildFile entries forLocationInfo.swift
(1838–1839) andThrottler.swift
(1841–1842). Please remove the redundant lines to clean up the build phase.DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swift (2)
20-26
: Solid message controller creation approach.The implementation correctly creates a message controller for the specific location context, enabling real-time updates in the detail view. Consider adding error handling for cases where the message controller creation might fail.
34-44
: Consider extracting device-specific presentation logic.The presentation logic handling iPad vs other devices could be extracted to a reusable utility method for better maintainability.
Consider creating a utility extension:
private extension UIViewController { func presentViewController(_ viewController: UIViewController, animated: Bool = true) { if UIDevice.current.userInterfaceIdiom == .pad { let nav = UINavigationController(rootViewController: viewController) navigationController?.present(nav, animated: animated) } else { navigationController?.pushViewController(viewController, animated: animated) } } }Then simplify the method:
private func showDetailViewController(messageController: ChatMessageController) { let mapViewController = LocationDetailViewController( messageController: messageController ) - if UIDevice.current.userInterfaceIdiom == .pad { - let nav = UINavigationController(rootViewController: mapViewController) - navigationController?.present(nav, animated: true) - return - } - navigationController?.pushViewController(mapViewController, animated: true) + presentViewController(mapViewController) }Sources/StreamChat/APIClient/Endpoints/MessageEndpoints.swift (1)
110-113
: Remove redundant= nil
initialisation
unset
is already an optional; explicitly initialising it withnil
is unnecessary and triggers Swift-Lint’sredundant_optional_initialization
warning.- var unset: [String]? = nil + var unset: [String]?🧰 Tools
🪛 SwiftLint (0.57.0)
[Warning] 112-112: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
Tests/StreamChatTests/Controllers/MessageController/MessageController_Tests.swift (1)
2692-2750
: Test names don’t match the scenario they verify
test_updateLiveLocation_whenNoLiveLocationAttachment…
supplies an attachment whosestoppedSharing
flag is true (i.e. already stopped), while
test_updateLiveLocation_whenLiveLocationHasAlreadyStopped…
supplies no live-location attachment at all.Although the assertions are correct, the misleading names make future maintenance error-prone.
Consider swapping the names (or fixing the fixtures) so that the intent of each test is self-documenting.Sources/StreamChat/Database/DTOs/LocationDTO.swift (1)
45-46
: Unnecessary sort descriptor harms fetch-by-ID performanceThe fetch uses
messageId == %@
, which is fully selective.
AddingsortDescriptors = [ ... createdAt ]
forces Core Data to create an extraORDER BY
, possibly preventing index usage.- request.sortDescriptors = [NSSortDescriptor(keyPath: \LocationDTO.message.createdAt, ascending: false)] + request.sortDescriptors = []Removing the descriptor speeds up single-row fetches and avoids pointless work.
Sources/StreamChat/Controllers/MessageController/MessageController.swift (1)
969-984
: Same attachment-clearing risk when stopping live-location sharing
stopLiveLocationSharing
also transmitsattachments: []
, which could unintentionally remove the attachment rather than just updating itsendAt
field. Consider passingnil
or usingunset
for the specific keys you actually want cleared.Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (2)
46-46
: Remember to release the mock aftercleanUp()
messageUpdater
is cleaned up but still referenced byenv
.
Consider also null-ing the reference (env?.messageUpdater = nil
) to make sure ARC can reclaim it immediately and avoid a (very small) risk of accidental reuse in later tests.
6160-6186
:TestEnvironment
– ensure the extra mock is released
messageUpdater
was added to the environment but is not set tonil
incleanUp()
.
Just like the other mocks, consider:func cleanUp() { messageUpdater = nil // existing clean-up calls... }Doing so avoids hidden strong-reference cycles should the mock capture closures.
DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift (2)
99-104
: Rename misleading selectorhandleTapOnWorkoutAttachment
The view shows a location snapshot, but the selector still mentions “Workout”. Renaming improves readability and future discoverability.- @objc func handleTapOnWorkoutAttachment() { + @objc func handleTapOnLocationAttachment() {and update the target registration accordingly.
Also applies to: 143-146
202-208
: Live-location snapshots are cached forever → stale thumbnails
getCachedSnapshot()
is consulted even for live locations, so once a snapshot is cached the map never refreshes while the user moves. Consider:
- Skipping the cache when
content.isLive == true
, or- Keying the cache with latitude/longitude (rounded) as well as
messageId
, or- Adding a TTL to the cache entry.
This prevents outdated location previews.
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
863-869
: Emptytext
parameter sent as""
– is that intended?
Whentext
isnil
, the method forcestext ?? ""
. If the backend differentiates between “no text” and “empty string”, this could create empty bubbles or violate validation rules. Consider makingtext
optional increateNewMessage
or omitting the field entirely when it’s not provided.DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift (1)
115-121
: Animation length exceeds update interval → janky motion
The live-location is refreshed every ~3 s but the pin animation lasts 5 s. This means multiple animations overlap, producing visible stutter on long sessions. Either reduce the duration to the update cadence or cancel any running animation before starting a new one.UIView.animate(withDuration: 3, …) { … }or store the current animator and call
stopAnimation(true)
before re-creating it.Sources/StreamChat/Database/DTOs/MessageDTO.swift (1)
774-786
: Duplicate user lookup & slight cleanlinessInside the
createNewMessage
flow you already havecurrentUserDTO
.
UsingcurrentUser?.currentDevice?.id
re-queries CoreData and shadows the earlier variable.- if let location, let deviceId = currentUser?.currentDevice?.id { + if let location, let deviceId = currentUserDTO.currentDevice?.id {This avoids an extra fetch and keeps naming consistent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (54)
CHANGELOG.md
(1 hunks)DemoApp/Info.plist
(2 hunks)DemoApp/LocationProvider.swift
(1 hunks)DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift
(1 hunks)DemoApp/Screens/DemoAppTabBarController.swift
(3 hunks)DemoApp/StreamChat/Components/CustomAttachments/DemoAttachmentViewCatalog.swift
(1 hunks)DemoApp/StreamChat/Components/CustomAttachments/DemoComposerVC.swift
(1 hunks)DemoApp/StreamChat/Components/CustomAttachments/DemoQuotedChatMessageView.swift
(1 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload+AttachmentViewProvider.swift
(0 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload.swift
(0 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift
(4 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swift
(1 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewInjector.swift
(1 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift
(2 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationSharingStatusView.swift
(1 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/UserAnnotation.swift
(1 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/UserAnnotationView.swift
(1 hunks)DemoApp/StreamChat/Components/DemoChatChannelListItemView.swift
(1 hunks)DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift
(1 hunks)DemoApp/StreamChat/StreamChatWrapper+DemoApp.swift
(0 hunks)Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/MessageEndpoints.swift
(2 hunks)Sources/StreamChat/APIClient/Endpoints/Payloads/IdentifiableModel.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/Payloads/LocationPayloads.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift
(9 hunks)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
(7 hunks)Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
(8 hunks)Sources/StreamChat/Controllers/MessageController/MessageController.swift
(7 hunks)Sources/StreamChat/Database/DTOs/LocationDTO.swift
(1 hunks)Sources/StreamChat/Database/DTOs/MessageDTO.swift
(10 hunks)Sources/StreamChat/Database/DatabaseSession.swift
(4 hunks)Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents
(2 hunks)Sources/StreamChat/Models/ChatMessage.swift
(4 hunks)Sources/StreamChat/Models/DraftMessage.swift
(1 hunks)Sources/StreamChat/Models/Location/LocationInfo.swift
(1 hunks)Sources/StreamChat/Models/Location/SharedLocation.swift
(1 hunks)Sources/StreamChat/Models/UserInfo.swift
(1 hunks)Sources/StreamChat/Repositories/MessageRepository.swift
(1 hunks)Sources/StreamChat/Utils/Throttler.swift
(3 hunks)Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift
(1 hunks)Sources/StreamChat/Workers/ChannelUpdater.swift
(3 hunks)Sources/StreamChat/Workers/MessageUpdater.swift
(27 hunks)StreamChat.xcodeproj/project.pbxproj
(36 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/MessageUpdater_Mock.swift
(3 hunks)TestTools/StreamChatTestTools/TestData/DummyData/MessageAttachmentPayload.swift
(2 hunks)Tests/StreamChatTests/APIClient/Endpoints/MessageEndpoints_Tests.swift
(1 hunks)Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift
(4 hunks)Tests/StreamChatTests/Controllers/MessageController/MessageController_Tests.swift
(1 hunks)Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift
(1 hunks)Tests/StreamChatTests/Models/Attachments/LiveLocationAttachmentPayload_Tests.swift
(1 hunks)Tests/StreamChatTests/Models/Attachments/StaticLocationAttachmentPayload_Tests.swift
(1 hunks)Tests/StreamChatTests/Models/ChatMessage_Tests.swift
(1 hunks)Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift
(1 hunks)Tests/StreamChatTests/Workers/MessageUpdater_Tests.swift
(1 hunks)
💤 Files with no reviewable changes (3)
- DemoApp/StreamChat/StreamChatWrapper+DemoApp.swift
- DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload+AttachmentViewProvider.swift
- DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentPayload.swift
🧰 Additional context used
🧬 Code Graph Analysis (7)
Tests/StreamChatTests/Models/Attachments/StaticLocationAttachmentPayload_Tests.swift (2)
Tests/StreamChatTests/Models/Attachments/LiveLocationAttachmentPayload_Tests.swift (3)
test_decodingDefaultValues
(10-32)test_decodingExtraData
(34-56)test_encoding
(58-76)TestTools/StreamChatTestTools/Extensions/Unique/URL+Unique.swift (1)
unique
(9-11)
DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationSharingStatusView.swift (2)
DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentSnapshotView.swift (1)
setUpLayout
(112-141)Sources/StreamChatUI/ViewContainerBuilder/ViewContainerBuilder.swift (1)
HContainer
(96-109)
Tests/StreamChatTests/Models/Attachments/LiveLocationAttachmentPayload_Tests.swift (2)
Tests/StreamChatTests/Models/Attachments/StaticLocationAttachmentPayload_Tests.swift (3)
test_decodingDefaultValues
(10-29)test_decodingExtraData
(31-53)test_encoding
(55-71)TestTools/StreamChatTestTools/Extensions/Unique/URL+Unique.swift (1)
unique
(9-11)
Sources/StreamChat/Models/Location/SharedLocation.swift (1)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
messageId
(2089-2091)
Tests/StreamChatTests/APIClient/Endpoints/MessageEndpoints_Tests.swift (4)
TestTools/StreamChatTestTools/Extensions/Unique/URL+Unique.swift (1)
unique
(9-11)Sources/StreamChat/Controllers/MessageController/MessageController.swift (2)
editMessage
(272-299)partialUpdateMessage
(311-329)Sources/StreamChat/Workers/MessageUpdater.swift (2)
editMessage
(108-182)editMessage
(1173-1191)Sources/StreamChat/APIClient/Endpoints/MessageEndpoints.swift (2)
editMessage
(28-40)partialUpdateMessage
(53-62)
Sources/StreamChat/Database/DTOs/LocationDTO.swift (4)
Sources/StreamChat/Database/DTOs/MessageDTO.swift (4)
loadOrCreate
(491-506)load
(452-471)load
(487-489)asModel
(1585-1585)Sources/StreamChat/APIClient/Endpoints/Payloads/IdentifiableModel.swift (1)
model
(14-17)Sources/StreamChat/Utils/Database/NSManagedObject+Extensions.swift (2)
insertNewObject
(14-22)applyPrefetchingState
(52-56)Sources/StreamChat/Database/DTOs/NSManagedObject+Validation.swift (1)
isNotDeleted
(19-22)
Sources/StreamChat/Database/DatabaseSession.swift (1)
Sources/StreamChat/Database/DTOs/LocationDTO.swift (1)
saveLocation
(67-82)
🪛 SwiftLint (0.57.0)
Sources/StreamChat/APIClient/Endpoints/MessageEndpoints.swift
[Warning] 112-112: Initializing an optional variable with nil is redundant
(redundant_optional_initialization)
🔇 Additional comments (111)
Sources/StreamChat/Models/UserInfo.swift (1)
7-7
: Documentation clarification looks good.The revised comment is clearer and aligns well with the existing model documentation style.
Sources/StreamChat/Models/DraftMessage.swift (1)
165-165
: LGTM! Proper initialization of the new location property.The addition of
sharedLocation = nil
is appropriate when creating aChatMessage
from aDraftMessage
, as draft messages wouldn't contain location data. This initialization is consistent with the new location sharing functionality being introduced.Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift (1)
297-298
: LGTM! Consistent property initialization in payload mapping.The addition of
sharedLocation: nil
properly initializes the new location property when mappingMessagePayload
toChatMessage
. This is consistent with the other property initializations in this workaround method for deleted messages.DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift (1)
52-52
: LGTM! Appropriate default configuration for demonstrating new features.Enabling
isLocationAttachmentsEnabled
by default makes sense for showcasing the new location sharing functionality. This change allows users to immediately explore the location features in the demo app.Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
138-143
: LGTM! Good formatting improvement for readability.The multi-line formatting of the
sendMessage
function signature improves code readability without any functional changes. This is a good practice for functions with multiple parameters.DemoApp/StreamChat/Components/CustomAttachments/DemoAttachmentViewCatalog.swift (2)
11-11
: LGTM! Clean migration to the new location attachment model.The change from checking
attachmentCounts[.location]
tomessage.sharedLocation != nil
is a clean migration that aligns with the new unified location attachment model introduced in this PR. This approach is more direct and efficient than counting attachments.
11-11
: LGTM! Correctly migrates to the new location detection approach.The change from checking
attachmentCounts[.location]
tomessage.sharedLocation != nil
aligns with the migration to the new shared location model mentioned in the PR summary. This approach is cleaner and more direct for detecting location attachments.Sources/StreamChat/Models/Location/LocationInfo.swift (2)
7-21
: LGTM! Well-designed foundational data structure.The
LocationInfo
struct is clean, simple, and appropriately designed as a foundational data type for location features. The public API design with clear property names and straightforward initializer makes it easy to use.
7-21
: LGTM! Clean and well-documented coordinate data structure.The
LocationInfo
struct is well-implemented with:
- Proper documentation for all properties
- Public accessibility for SDK consumers
- Simple, focused design for representing geographic coordinates
- Clear initializer
This serves as a solid foundation for location-based features.
DemoApp/StreamChat/Components/DemoChatMessageActionsVC.swift (2)
29-32
: LGTM! Sensible restriction on editing location messages.Preventing message editing when location attachments are present makes good UX sense, as edited location messages could lose their contextual meaning. The implementation correctly identifies location messages and filters out edit actions.
29-32
: LGTM! Appropriate business logic for location messages.Preventing editing of messages with location attachments is a sensible UX decision. Location messages, especially live location sharing, shouldn't typically be editable as this could disrupt the location sharing functionality. The implementation correctly uses the new
sharedLocation
property and cleanly filters out edit actions.Sources/StreamChat/APIClient/Endpoints/Payloads/IdentifiableModel.swift (2)
102-106
: LGTM! Standard conformance implementation.The
LocationDTO
conformance toIdentifiableModel
follows the established pattern used by other DTOs in the file. UsingmessageId
as the identifier is appropriate since location data is tied to specific chat messages.
102-106
: LGTM! Proper integration of LocationDTO into the identification system.The
LocationDTO
extension correctly implements theIdentifiableModel
protocol:
- Uses
messageId
as the identifier, which appropriately links location data to messages- Follows the established pattern used by other DTO extensions in the file
- Maintains consistency with the existing codebase architecture
This enables proper identification and retrieval of location data objects within the database layer.
Tests/StreamChatTests/APIClient/Endpoints/MessageEndpoints_Tests.swift (1)
193-215
: LGTM! Well-structured test for the new partial update endpoint.The test correctly validates the endpoint construction with appropriate test data including
set
,unset
, andskipEnrichUrl
parameters. The assertions properly verify both the endpoint equality and path string.Sources/StreamChat/Workers/ChannelUpdater.swift (3)
381-381
: Good addition of location parameter for message creation.The optional
location
parameter follows the existing pattern of other optional parameters in the method signature and enables location data to be associated with new messages.
405-405
: Correct parameter forwarding to database layer.The location parameter is properly passed through to the database write operation, maintaining consistency with the method signature.
1147-1152
: Well-designed location data model.The
NewLocationInfo
struct provides a clean interface for location data with:
- Required latitude and longitude coordinates as
Double
- Optional
endAt
date for live location expiration- Appropriate naming and structure
Sources/StreamChat/Models/ChatMessage.swift (4)
181-182
: Proper addition of location property to message model.The
sharedLocation
property is correctly declared as optional and follows the existing documentation pattern with a clear description.
224-224
: Initializer parameter correctly added.The
sharedLocation
parameter is appropriately placed in the initializer parameter list, maintaining consistency with the property order.
266-266
: Property assignment in initializer is correct.The
sharedLocation
property is properly assigned from the initializer parameter.
314-315
: Replacing method correctly preserves location data.The
sharedLocation
property is properly included in thereplacing
method, ensuring location data is preserved when creating modified message instances.DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationSharingStatusView.swift (4)
9-16
: Well-structured label component with proper theming.The
statusLabel
is correctly configured with:
- Proper auto layout setup
- Theme-aware font and color
- Clean lazy initialization
18-34
: Good use of system images for location status.The image configuration properly uses:
- Appropriate system symbols (
location.fill
andlocation.slash.fill
)- Consistent symbol scale (
.medium
)- Proper image view setup with content mode
36-45
: Clean layout implementation using container helpers.The layout setup correctly:
- Uses
HContainer
for horizontal arrangement- Applies appropriate spacing and alignment
- Sets proper constraints for icon size
- Embeds the container properly
47-54
: Effective status update method with proper theming.The
updateStatus
method provides:
- Clear text labels for both states
- Appropriate icon switching
- Theme-aware color changes (accent for active, subtitle for inactive)
- Simple boolean parameter for state management
DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/UserAnnotation.swift (1)
9-18
: LGTM! Clean MKAnnotation implementation.The implementation correctly uses the
dynamic
keyword for the coordinate property, which is essential for MapKit's KVO-based coordinate change notifications. The class structure is simple and follows standard MKAnnotation patterns.DemoApp/StreamChat/Components/DemoChatChannelListItemView.swift (1)
9-22
: LGTM! Well-structured location preview handling.The implementation correctly:
- Handles both live and static location previews
- Uses appropriate conditional logic to check for shared locations
- Leverages existing helper methods for consistent formatting
- Provides proper fallback to superclass implementation
The code follows established patterns in the codebase and integrates well with the location sharing feature.
Tests/StreamChatTests/Models/ChatMessage_Tests.swift (1)
396-527
: LGTM! Comprehensive test coverage for location attachments.The test implementation provides excellent coverage:
- Tests both empty and populated attachment scenarios
- Verifies proper filtering of static vs live location attachments
- Uses realistic mock data with proper coordinate values
- Employs Set comparison for ID verification to avoid order dependency
- Follows established testing patterns in the codebase
The tests properly validate the new computed properties
staticLocationAttachments
andliveLocationAttachments
onChatMessage
.Sources/StreamChat/Repositories/MessageRepository.swift (1)
302-325
: LGTM! Well-implemented repository method for live location queries.The implementation correctly:
- Uses
backgroundReadOnlyContext
for read-only database operations- Validates current user existence before proceeding
- Follows proper async patterns with
context.perform
- Maps DTOs to models with appropriate error handling
- Uses consistent completion callback patterns
The method integrates well with the live location sharing feature and provides a clean interface for fetching active live location messages for the current user.
Tests/StreamChatTests/Models/Attachments/StaticLocationAttachmentPayload_Tests.swift (1)
9-72
: LGTM! Comprehensive test coverage for StaticLocationAttachmentPayload.The test suite properly covers all key scenarios:
- Decoding minimal JSON with required fields
- Handling extra data fields for extensibility
- Round-trip encoding/decoding validation
The test data uses realistic London coordinates and follows established patterns from the codebase.
Sources/StreamChat/Utils/Throttler.swift (3)
11-11
: Good API design decision making the Throttler public.Making this utility class public enables external usage for live location throttling, which aligns with the PR objectives.
18-18
: Excellent change allowing runtime interval modification.Changing
interval
from a constant to a public variable enables dynamic throttling adjustments, which could be valuable for adapting to different network conditions or user preferences in live location sharing.
28-28
: Good improvement to queue initialization.The explicit queue label and QoS setting improves debuggability and ensures appropriate priority for throttling operations.
Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents (2)
178-191
: Well-designed Core Data entity for location data.The LocationDTO entity design is solid:
- Appropriate attribute types (Double for coordinates, optional Date for endAt)
- Good uniqueness constraint on messageId prevents duplicate location data
- Proper nullable relationships with MessageDTO
- Supports both static and live location scenarios
265-265
: Proper relationship design between MessageDTO and LocationDTO.The optional to-one relationship with nullify deletion rule is the correct design choice, preventing cascade deletion issues while allowing messages without location data.
Tests/StreamChatTests/Models/Attachments/LiveLocationAttachmentPayload_Tests.swift (1)
58-76
: LGTM! Comprehensive encoding test.The encoding test properly validates round-trip serialization including the
stoppedSharing
field and extra data.Sources/StreamChat/Models/Location/SharedLocation.swift (1)
7-48
: LGTM! Well-designed location model.The
SharedLocation
struct is well-designed with clear separation between static and live location concepts. The computed propertiesisLive
andisLiveSharingActive
provide intuitive ways to check location sharing status, and the implementation logic is correct.DemoApp/Info.plist (1)
35-40
: LGTM! Proper location permissions and background mode configuration.The location permission descriptions are clear and user-friendly, and the background location mode is correctly configured for live location sharing functionality.
Also applies to: 62-65
DemoApp/StreamChat/Components/CustomAttachments/DemoQuotedChatMessageView.swift (1)
10-26
: LGTM! Clean implementation using the new shared location model.The updated implementation properly leverages the new
sharedLocation
property and provides clear visual differentiation between live and static locations using appropriate system icons and labels.DemoApp/Screens/DemoAppTabBarController.swift (2)
11-11
: LGTM! Proper LocationProvider integration.The LocationProvider integration is well-implemented with proper weak self capture to prevent retain cycles, and the location transformation logic correctly converts CLLocation to LocationInfo.
Also applies to: 69-75
89-118
: LGTM! Well-designed location sharing lifecycle management.The delegate method implementations properly coordinate the location sharing lifecycle by starting/stopping location monitoring based on sharing status and providing useful debug logging for troubleshooting location updates.
Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift (1)
726-763
: Explicitly guarantee messages are considered “active”The repository’s implementation is likely to filter out expired live-location messages (e.g.
endAt <= Date()
).
The dummy attachments created here do not specify anendAt
, relying on implementation details that currently treat anil
value as “active forever”.Providing an explicit future date clarifies intent and prevents flakiness if the definition of “active” changes:
- attachments: [.dummy(type: .liveLocation)] + attachments: [.dummy(type: .liveLocation, endAt: Date().addingTimeInterval(3600))](Adjust helper signature if needed.)
This also makes the test name (returnsMessagesForChannel
) fully reflect that the messages are indeed active.TestTools/StreamChatTestTools/TestData/DummyData/MessageAttachmentPayload.swift (4)
2-2
: Copyright year updated appropriately.The copyright year has been updated to 2025, which is appropriate for new code additions.
177-188
: Well-implemented static location factory method.The static location factory method follows the established pattern with reasonable default coordinates (London) and proper payload structure. The latitude and longitude are correctly stored as numbers in the payload dictionary.
190-203
: Live location factory method includes proper state management.The live location factory method appropriately includes the
stoppedSharing
parameter, which is essential for managing live location sharing state. The default value offalse
makes sense for testing scenarios where location sharing is active.
205-213
: Decoder properties follow established patterns.Both decoder properties (
decodedStaticLocationPayload
anddecodedLiveLocationPayload
) follow the same pattern as existing decoders in the file, using proper error handling with optional return types and consistent encoding/decoding approach.TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/MessageUpdater_Mock.swift (3)
117-123
: Atomic properties properly defined for partial message updates.The atomic properties correctly track all parameters of the
updatePartialMessage
method, including optional text, attachments, extraData, and completion handler. This follows the established pattern used throughout the mock class.
258-263
: Cleanup method properly updated.The cleanup method has been correctly updated to reset all the new atomic properties related to
updatePartialMessage
, ensuring proper test isolation between test runs.
533-547
: Mock implementation follows established patterns.The
updatePartialMessage
mock implementation correctly:
- Stores all method parameters in atomic properties
- Handles optional parameters appropriately
- Invokes the completion result when available
- Follows the same pattern as other mock methods in the class
This enables comprehensive testing of the partial message update functionality.
Sources/StreamChat/Workers/MessageUpdater.swift (2)
184-255
: Well-designed partial message update implementation.The
updatePartialMessage
method is correctly implemented with proper error handling and database synchronization. The attachment encoding logic properly convertsAnyAttachmentPayload
toMessageAttachmentPayload
for API transmission.Consider adding JSDoc-style documentation to describe the method's purpose and parameters, especially the
unset
parameter which may not be immediately clear to developers.
107-107
: Consistent parameter addition for location support.The addition of the optional
location: NewLocationInfo?
parameter and its correspondingnil
defaults maintain backward compatibility while enabling location functionality.Also applies to: 312-312
DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/UserAnnotationView.swift (1)
1-83
: Well-implemented map annotation view with good separation of concerns.The implementation follows iOS best practices:
- Proper reuse identifier for map annotation views
- Clean separation between avatar display and pulsing animation
- Appropriate layer management and cleanup
Sources/StreamChat/Database/DatabaseSession.swift (3)
661-664
: Well-designed protocol following existing patterns.The
LocationDatabaseSession
protocol follows the same pattern as other database session protocols in the file, providing a clean separation of concerns for location-related database operations.
107-107
: Backward-compatible parameter addition.The optional
location: NewLocationInfo?
parameter maintains backward compatibility while enabling location functionality. The defaultnil
value in the extension ensures existing code continues to work.Also applies to: 305-305
661-680
:❌ Incorrect review comment
Ensure consistency with related location data structures.
The
LocationPayload
parameter type should be consistent with other location-related types in the codebase. Verify that this integrates properly with theNewLocationInfo
type used in message creation.
🏁 Script executed:
#!/bin/bash # Description: Verify consistency between LocationPayload and NewLocationInfo types # Expected: Find type definitions and their relationships rg "(LocationPayload|NewLocationInfo)" --type swift -A 3 -B 1Length of output: 6621
🏁 Script executed:
#!/bin/bash # Print LocationPayload definition echo "---- LocationPayload ----" sed -n '1,50p' Sources/StreamChat/APIClient/Endpoints/Payloads/LocationPayloads.swift echo # Print NewLocationInfo definition echo "---- NewLocationInfo ----" rg -n "struct NewLocationInfo" -A 5 Sources/StreamChat/Workers/ChannelUpdater.swiftLength of output: 1290
🏁 Script executed:
#!/bin/bash # Print around struct MessageRequestBody or similar in MessagePayloads.swift echo "---- MessageRequestPayload ----" rg -n "struct.*Request" -n Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift -A5 -B2Length of output: 446
``
🏁 Script executed:
#!/bin/bash # Show the function where NewLocationInfo is used in ChannelUpdater rg -n "location: NewLocationInfo" -n Sources/StreamChat/Workers/ChannelUpdater.swift -A5 -B5Length of output: 536
🏁 Script executed:
#!/bin/bash # Show saveMessage implementation to inspect location handling rg -n "func saveMessage" -A30 -B5 Sources/StreamChat/Database/DTOs/MessageDTO.swiftLength of output: 7398
🏁 Script executed:
#!/bin/bash # Show createNewMessage in ChannelUpdater to inspect location mapping rg -n "func createNewMessage" -A20 -B10 Sources/StreamChat/Workers/ChannelUpdater.swiftLength of output: 2905
Ignore consistency suggestion for LocationPayload vs. NewLocationInfo
LocationPayload is the API-response model (including channelId, messageId and createdByDeviceId) used when reading/storing location attachments. NewLocationInfo is the minimal outgoing model (latitude, longitude, endAt) used when creating a new message. They serve different purposes and don’t need to match field-for-field.
Likely an incorrect or invalid review comment.
StreamChat.xcodeproj/project.pbxproj (33)
1431-1432
: Add build entries for UserAnnotation components
The new PBXBuildFile entries forUserAnnotation.swift
andUserAnnotationView.swift
correctly include these source files in the build phase, aligning with the new map annotation features.
1469-1469
: IncludeLocationSharingStatusView.swift
in build phase
TheLocationSharingStatusView.swift
file has been added to the PBXBuildFile section, ensuring the status view is compiled.
1789-1790
: Add test files for live and static location payloads
The new test targetsLiveLocationAttachmentPayload_Tests.swift
andStaticLocationAttachmentPayload_Tests.swift
are included, improving coverage for the location attachment payloads.
1817-1818
: AddLocationPayloads.swift
to build phase
TheLocationPayloads.swift
source file has been correctly added to the build, providing the new payload definitions.
4232-4233
: Add file references for UserAnnotation components
UserAnnotation.swift
andUserAnnotationView.swift
have been added to the project file references under the appropriate group.
4258-4258
: Add file reference forLocationSharingStatusView.swift
The reference forLocationSharingStatusView.swift
is included, ensuring the new view is discoverable by Xcode.
4326-4326
: Add file reference forSharedLocation.swift
TheSharedLocation.swift
file is now referenced, enabling the shared location model in the project.
4398-4398
: Add file reference forLocationDTO.swift
TheLocationDTO.swift
class is correctly referenced, supporting the Core Data mapping of location entities.
4474-4475
: Add test references for location payloads
Test filesLiveLocationAttachmentPayload_Tests.swift
andStaticLocationAttachmentPayload_Tests.swift
are now referenced in the project, aligning with the added test targets.
4497-4497
: Add file reference forLocationPayloads.swift
TheLocationPayloads.swift
is now included in the file references, which provides the new location payload definitions.
4511-4513
: Add file references for location core models
The filesLocationInfo.swift
,LocationProvider.swift
, andThrottler.swift
are correctly referenced, supporting live location updates and throttling.
5621-5621
: IncludeLocationDTO.swift
in Core Data model sources
TheLocationDTO.swift
class has been added to the managed object model targets, enabling persistence of location data.
5652-5652
: IncludeThrottler.swift
in utility sources
Throttler.swift
is now part of the utilities source list, supporting rate-limiting for live location updates.
5714-5714
: IncludeLocationProvider.swift
in demo app sources
TheLocationProvider.swift
is added to the demo app’s sources, facilitating location feed in the demo.
5788-5788
: AddLocationPayloads.swift
to the Payloads group
LocationPayloads.swift
is now part of thePayloads
group, grouping it with other payload definitions.
5943-5943
: AddLocation
group to Attachments
A newLocation
subgroup has been added underAttachments
, organizing static and live location attachments.
7320-7321
: Add test file references to Attachments group
The test files for live and static location attachments are now organized under theAttachments
group, enhancing test discoverability.
8452-8454
: Add UI components to LocationAttachment group
LocationSharingStatusView.swift
,UserAnnotationView.swift
, andUserAnnotation.swift
are now grouped underLocationAttachment
, matching the location attachment UI structure.
9132-9140
: AddLocation
group with core files
The newLocation
group containingLocationInfo.swift
andSharedLocation.swift
was created, organizing core location models.
11126-11128
: Add demo app UI for location components
Demo app sourcesLocationSharingStatusView.swift
andUserAnnotation.swift
are now included, showcasing the new live location UI.
11137-11138
: Add demo app providers and views
LocationProvider.swift
andUserAnnotationView.swift
have been added to the demo app’s sources.
11422-11422
: IncludeThrottler.swift
in demo utilities
TheThrottler.swift
utility is added to the demo’s source list for live update throttling.
11463-11463
: AddSharedLocation.swift
to thread model sources
SharedLocation.swift
is now included alongside thread-related DTOs, enabling message-level location sharing.
11615-11615
: IncludeLocationDTO.swift
in event handling sources
LocationDTO.swift
is now part of the event middleware and observer logic.
11719-11719
: IncludeLocationInfo.swift
in channel model sources
TheLocationInfo.swift
model is now part of the channel-related source files.
11787-11787
: AddLocationPayloads.swift
to endpoint payload sources
LocationPayloads.swift
is now referenced in the list of payload definitions for API endpoints.
11990-11991
: Include location payload tests in core tests
The live/static location payload tests are now part of the core test suite, increasing coverage.
12288-12288
: AddLocationDTO.swift
to playback state sources
LocationDTO.swift
is now included in the playback state modules for offline handling.
12375-12375
: IncludeThrottler.swift
in cache-and-debounce sources
Throttler.swift
is added to the collection of debounce and caching utilities.
12551-12551
: IncludeLocationInfo.swift
in unread count sources
LocationInfo.swift
is now part of the unread count-related logic, supporting contextual location badges.
12601-12601
: IncludeSharedLocation.swift
in SwiftUI components
SharedLocation.swift
is now available in SwiftUI views for current user context.
12685-12685
: AddLocationPayloads.swift
to lazy map collection sources
The payload definitions for locations are now included in the lazy map collection utilities.
15458-15459
: Bump Atlantis Swift package to 1.28.0
The remote Swift package reference foratlantis
has been updated to use up-to-next-major1.28.0
, ensuring compatibility with the latest APIs.DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swift (5)
9-17
: Well-designed protocol separation.The protocol effectively separates location viewing from stop sharing actions, providing clear responsibilities for each delegate method. The migration to
SharedLocation
aligns with the broader refactoring mentioned in the summary.
28-44
: Appropriate platform-specific presentation logic.The implementation correctly handles different device types with modal presentation on iPad and push navigation on other devices. The stop sharing functionality properly uses the channel controller to manage live location sharing state.
7-7
: UIKit import addition looks good.The UIKit import is appropriately added since the code uses
UIDevice
andUINavigationController
types.
10-16
: Protocol refactoring improves separation of concerns.The migration from a single
didTapOnLocationAttachment(_:)
method to two distinct methods (didTapOnLocation(_:)
anddidTapOnStopSharingLocation(_:)
) provides better separation of concerns and clearer intent for different user actions.
20-26
:✅ Verification successful
Verify message controller creation and lifecycle management.
The implementation creates a new
ChatMessageController
for each location tap. Consider the following:
- Memory management: Ensure the controller is properly retained while the detail view is presented
- Error handling: The controller creation could potentially fail or return an invalid controller
Let me verify how
ChatMessageController
creation is handled:
🏁 Script executed:
#!/bin/bash # Description: Check how ChatMessageController is created and if error handling is needed # Expected: Find the messageController method implementation and error handling patterns ast-grep --pattern 'func messageController($$$) -> $_' # Also check for similar usage patterns in the codebase rg -A 5 "client\.messageController"Length of output: 22137
🏁 Script executed:
#!/bin/bash # Display the LocationAttachmentViewDelegate to inspect showDetailViewController usage sed -n '1,200p' DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swiftLength of output: 1522
No action needed – ChatMessageController setup is safe
The
messageController(cid:messageId:)
initializer always returns a non‐optionalChatMessageController
, and handing it off toLocationDetailViewController
(which stores it as a property) ensures it’s retained for the lifetime of the detail view. You can remove the earlier concerns around memory management and error handling.Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift (12)
57-57
: Correct coding key mapping for location field.The coding key properly maps the
location
property to the backendshared_location
field, maintaining consistency with the API contract.
116-116
: Appropriate optional location property.The location property is correctly declared as optional since location data is not present in all messages. The mutable
var
declaration allows for potential updates to location information.
183-183
: Proper optional decoding implementation.The location decoding correctly uses
decodeIfPresent
and follows the established pattern for optional fields in the payload structure.
289-289
: Complete integration of location support in outgoing messages.The location field is properly integrated into the outgoing message request body with:
- Optional property declaration
- Default parameter in initializer
- Proper assignment in constructor
- Conditional encoding logic
The use of
LocationRequestPayload
type for outgoing data suggests good separation of concerns between incoming and outgoing payload structures.Also applies to: 310-310, 329-329, 348-348
57-57
: Location coding key correctly maps to backend field.The mapping of
location = "shared_location"
aligns with the backend API specification mentioned in the PR objectives.
116-116
: Location property addition is consistent with other optional fields.The
location: LocationPayload?
property follows the same pattern as other optional fields likepoll
anddraft
.
183-183
: Location decoding implementation is correct.The decoding logic
location = try container.decodeIfPresent(LocationPayload.self, forKey: .location)
properly handles optional location data and follows the same pattern as other optional fields.
265-265
: Location assignment in initializer is correct.The assignment
self.location = location
is consistent with other properties and properly initializes the location field.
289-289
: MessageRequestBody location property follows correct pattern.The addition of
var location: LocationRequestPayload?
to the outgoing message body is consistent with the incoming payload structure.
310-310
: MessageRequestBody initializer parameter addition is correct.The location parameter with default nil value maintains backward compatibility while enabling location support.
329-329
: Location assignment in MessageRequestBody initializer is correct.The assignment follows the same pattern as other properties in the initializer.
348-348
: Location encoding implementation is correct.The encoding logic
try container.encodeIfPresent(location, forKey: .location)
properly handles optional location data and follows the established pattern for optional fields.CHANGELOG.md (1)
7-22
: Changelog entries accurately capture new location features and formatting is correct
The “Upcoming” section lists all additions from PR #3531—partial message updates, static location support, and live location sharing APIs—and follows the existing indentation and Keep a Changelog style.Sources/StreamChat/Database/DTOs/LocationDTO.swift (1)
18-35
:deviceId
is non-optional but source may be emptyIf
payload.createdByDeviceId
can legitimately benil
(e.g. Web-socket events coming from another user without device info) the assignment to a non-optionalString
will crash.Either:
- Mark
deviceId
optional in the data model, or- Provide a safe fallback:
- locationDTO.deviceId = payload.createdByDeviceId + locationDTO.deviceId = payload.createdByDeviceId ?? ""Validate upstream assumptions to avoid runtime failures.
Sources/StreamChat/Controllers/MessageController/MessageController.swift (2)
365-374
: Passing an empty attachment array may wipe the attachment on the backend
updatePartialMessage
is called withattachments: []
.
Many REST back-ends treat an empty array as “replace current value with empty array”, effectively removing the live-location attachment. In this flow we only want to update coordinates, not strip the attachment.Prefer sending
attachments: nil
(do not touch) or use theunset
parameter explicitly when removal is intended.- attachments: [], + attachments: nil,Please align the semantics here with the server implementation.
1197-1208
: 👍 Clear, domain-specific error types addedThe new
ClientError
subclasses precisely describe the two failure scenarios around live-location handling. This increases debuggability and allows callers to react programmatically. Looks good.DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift (1)
42-46
: No error handling when stopping live sharing
messageController.stopLiveLocationSharing()
is fire-and-forget. If the operation fails (e.g. network error) the UI remains in the “stopping” state silently. Provide a completion handler and surface the error (alert, toast, etc.) so users understand what happened.Sources/StreamChat/Database/DTOs/MessageDTO.swift (1)
1638-1645
: Verify REST field name for device identifier
asRequestBody()
serialises the live-location withcreatedByDeviceId
.
Please double-check the backend contract – earlier payloads/PR description showed the field asdeviceId
. A mismatch will silently drop the value server-side.
<key>LSApplicationCategoryType</key> | ||
<string></string> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove or set proper value for LSApplicationCategoryType.
The LSApplicationCategoryType
key is set to an empty string, which is not a valid App Store category. Either remove this key entirely or set it to a proper category like "public.app-category.social-networking"
.
- <key>LSApplicationCategoryType</key>
- <string></string>
📝 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.
<key>LSApplicationCategoryType</key> | |
<string></string> |
🤖 Prompt for AI Agents
In DemoApp/Info.plist at lines 23 to 24, the LSApplicationCategoryType key is
set to an empty string, which is invalid. Fix this by either removing the
LSApplicationCategoryType key entirely or setting its value to a valid App Store
category string such as "public.app-category.social-networking".
struct LocationPayload: Decodable { | ||
let channelId: String | ||
let messageId: String | ||
let latitude: Double | ||
let longitude: Double | ||
let endAt: Date? | ||
let createdByDeviceId: String | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case channelId = "channel_cid" | ||
case messageId = "message_id" | ||
case latitude | ||
case longitude | ||
case createdByDeviceId = "created_by_device_id" | ||
case endAt = "end_at" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Clarify visibility & optionality of payload fields
- Access-level
Other payload structs insideStreamChat
arepublic
because apps need to construct them for requests. Consider matching that for consistency:
-struct LocationPayload: Decodable {
+public struct LocationPayload: Decodable {
Do the same for LocationRequestPayload
if it’s part of the public surface.
- Device identifier
Servers might omitcreated_by_device_id
when the value is unknown, so marking it optional prevents decoding failures:
- let createdByDeviceId: String
+ let createdByDeviceId: String?
(Repeat in the request struct if callers may leave it out.)
These tweaks improve API robustness while avoiding breaking changes later.
📝 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.
struct LocationPayload: Decodable { | |
let channelId: String | |
let messageId: String | |
let latitude: Double | |
let longitude: Double | |
let endAt: Date? | |
let createdByDeviceId: String | |
enum CodingKeys: String, CodingKey { | |
case channelId = "channel_cid" | |
case messageId = "message_id" | |
case latitude | |
case longitude | |
case createdByDeviceId = "created_by_device_id" | |
case endAt = "end_at" | |
} | |
} | |
public struct LocationPayload: Decodable { | |
let channelId: String | |
let messageId: String | |
let latitude: Double | |
let longitude: Double | |
let endAt: Date? | |
let createdByDeviceId: String? | |
enum CodingKeys: String, CodingKey { | |
case channelId = "channel_cid" | |
case messageId = "message_id" | |
case latitude | |
case longitude | |
case createdByDeviceId = "created_by_device_id" | |
case endAt = "end_at" | |
} | |
} |
🤖 Prompt for AI Agents
In Sources/StreamChat/APIClient/Endpoints/Payloads/LocationPayloads.swift around
lines 7 to 23, update the LocationPayload struct to be public for consistency
with other payloads used in requests. Also, change the createdByDeviceId
property to be optional to handle cases where the server omits this field,
preventing decoding errors. Apply the same visibility and optionality changes to
LocationRequestPayload if it is part of the public API surface.
self?.database.write { session in | ||
let cid: ChannelId? | ||
|
||
if let payloadCid = messagePayloadBoxed.message.cid { | ||
cid = payloadCid | ||
} else if let cidFromLocal = session.message(id: messageId)?.cid, | ||
let localCid = try? ChannelId(cid: cidFromLocal) { | ||
cid = localCid | ||
} else { | ||
cid = nil | ||
} | ||
|
||
guard let cid = cid else { | ||
completion?(.failure(ClientError.ChannelNotCreatedYet())) | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
❓ Verification inconclusive
Verify channel ID resolution logic for edge cases.
The channel ID resolution logic handles both payload-provided and locally-stored channel IDs appropriately. However, ensure this logic works correctly when messages are created in channels that exist locally but haven't been fully synchronized.
🏁 Script executed:
#!/bin/bash
# Description: Verify that partial message updates handle channel ID resolution correctly
# Expected: Find tests that validate channel ID resolution in partial updates
rg -A 10 "updatePartialMessage.*test" --type swift
Length of output: 52
Add tests for channel ID resolution in partial message updates
I wasn’t able to find any existing tests covering the CID‐fallback logic in MessageUpdater.swift
(Sources/StreamChat/Workers/MessageUpdater.swift lines 221–236). Please add unit tests for these scenarios to ensure edge cases are handled:
• Payload provides a valid cid
→ update uses the payload CID
• Payload’s cid
is nil but local DB session has a message with a valid CID → update falls back to local CID
• Both payload and local session CID are missing → update fails with ClientError.ChannelNotCreatedYet()
🤖 Prompt for AI Agents
In Sources/StreamChat/Workers/MessageUpdater.swift around lines 221 to 236, add
unit tests to cover the channel ID resolution logic in partial message updates.
Create tests for these cases: when the payload provides a valid cid, when the
payload cid is nil but the local database session has a valid cid for the
message, and when both payload and local session cids are missing, ensuring the
update fails with ClientError.ChannelNotCreatedYet(). These tests should verify
that the fallback logic behaves correctly in each scenario.
PollDatabaseSession, | ||
LocationDatabaseSession {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify implementation of LocationDatabaseSession protocol.
The protocol is properly added to the main DatabaseSession
inheritance list, but ensure that there's a concrete implementation of the saveLocation
method somewhere in the codebase.
🏁 Script executed:
#!/bin/bash
# Description: Find implementation of LocationDatabaseSession protocol methods
# Expected: Find concrete implementations of saveLocation method
rg "func saveLocation" --type swift -A 5
Length of output: 1107
🏁 Script executed:
#!/bin/bash
# Show LocationDatabaseSession protocol definition
rg -n "protocol LocationDatabaseSession" -A5 -B5 Sources/StreamChat/Database/DatabaseSession.swift
# Show any default implementation via protocol extension
rg -n "extension LocationDatabaseSession" -A5 -B5 Sources/StreamChat/Database
# List types conforming to DatabaseSession
rg -n "class .*:.*DatabaseSession" -A5 -B5 --type swift Sources/StreamChat
rg -n "struct .*:.*DatabaseSession" -A5 -B5 --type swift Sources/StreamChat
Length of output: 770
Missing implementation for LocationDatabaseSession.saveLocation
I couldn’t find any concrete saveLocation(payload:cache:)
implementation in your codebase:
- The
LocationDatabaseSession
protocol is declared inSources/StreamChat/Database/DatabaseSession.swift
, but there’s no default implementation in a protocol extension. - No class or struct conforming to
DatabaseSession
implementssaveLocation
.
Please add a saveLocation(payload: LocationPayload, cache: PreWarmedCache?) throws -> LocationDTO
implementation—either in your DatabaseSession
conformer (e.g. your CoreData/SQLite session class) or via a protocol extension, using LocationDTO.loadOrCreate(...)
under the hood.
🤖 Prompt for AI Agents
In Sources/StreamChat/Database/DatabaseSession.swift around lines 679 to 680,
the LocationDatabaseSession protocol is added to DatabaseSession but lacks a
concrete implementation of the required saveLocation(payload:cache:) method. To
fix this, implement the saveLocation method either in the class or struct
conforming to DatabaseSession (such as your CoreData or SQLite session class) or
provide a default implementation in a protocol extension. The implementation
should use LocationDTO.loadOrCreate(...) internally to save the location data
and return the LocationDTO instance.
switch result { | ||
case let .success(messages): | ||
guard let message = messages.first, | ||
let location = message.sharedLocation | ||
else { | ||
self.callback { | ||
completion?(.failure(ClientError.MessageDoesNotHaveLiveLocationAttachment())) | ||
} | ||
return | ||
} | ||
|
||
/// Stop sharing the live location message. | ||
self.messageUpdater.updatePartialMessage( | ||
messageId: message.id, | ||
attachments: [] | ||
) { result in | ||
self.callback { | ||
completion?(result.map(\.id)) | ||
} | ||
} | ||
case let .failure(error): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Unused location
constant and no end-time set when stopping sharing
let location = message.sharedLocation
is never used after theguard
, producing a compiler warning.updatePartialMessage(attachments: [])
removes attachments but doesn’t mark the live-location as ended (e.g. by settingendAt = Date()
). Clients may keep considering the share “active”.
- guard let message = messages.first,
- let location = message.sharedLocation
+ guard let message = messages.first,
+ message.sharedLocation != nil
and, if the backend requires an end_at
, pass it explicitly:
self.messageUpdater.updatePartialMessage(
messageId: message.id,
locationEndAt: Date()
) { … }
(Exact API may differ.)
🏁 Script executed:
#!/bin/bash
# Locate the definition(s) of updatePartialMessage to inspect its parameters
rg "func updatePartialMessage" -n .
# Find all occurrences of sharedLocation on Message to see its type and usage
rg "sharedLocation" -n .
Length of output: 3601
🏁 Script executed:
#!/bin/bash
# Display the signature of updatePartialMessage to see available parameters
rg -A 5 -n "func updatePartialMessage" -n Sources/StreamChat/Workers/MessageUpdater.swift
Length of output: 334
---
🏁 Script executed:
```shell
#!/bin/bash
# Find the definition of SharedLocation and inspect its properties
rg -n "struct SharedLocation" -A 15 -n Sources/StreamChat/Models/ChatMessage.swift
# Check if updatePartialMessage extraData parameter is used elsewhere for location end
rg "updatePartialMessage.*extraData" -n .
Length of output: 726
🏁 Script executed:
#!/bin/bash
# Search entire repository for SharedLocation definition
rg -n "struct SharedLocation" -n .
# If not found, search for SharedLocation as a typealias or class
rg -n "class SharedLocation" -n .
# Search for endAt property anywhere
rg -n "endAt" -n .
Length of output: 3317
Remove unused binding and mark live location as ended
The location
constant is never used and you also need to tell the backend to end the live‐location share (simply clearing attachments won’t set endAt
).
• Update the guard to drop the unused binding:
- guard let message = messages.first,
- let location = message.sharedLocation
+ guard let message = messages.first,
+ message.sharedLocation != nil
• When stopping the share, clear the attachments and include an end_at
timestamp. You can pass it via extraData
or extend updatePartialMessage
with a locationEndAt
parameter. For example:
let endDate = Date()
self.messageUpdater.updatePartialMessage(
messageId: message.id,
attachments: [],
extraData: ["location": [
"end_at": endDate
]]
) { result in
self.callback {
completion?(result.map(\.id))
}
}
Adjust the key/value structure to match your backend schema, or add a dedicated locationEndAt: Date?
parameter to updatePartialMessage
if that’s preferred.
🤖 Prompt for AI Agents
In Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
around lines 988 to 1008, remove the unused 'location' binding from the guard
statement and modify the call to updatePartialMessage to not only clear
attachments but also mark the live location share as ended by passing an
'end_at' timestamp. This can be done by adding an extraData dictionary with the
appropriate key/value (e.g., ["location": ["end_at": Date()]]) or by using a
locationEndAt parameter if available, ensuring the backend is informed that the
live location sharing has stopped.
) -> NSFetchRequest<MessageDTO> { | ||
let request = NSFetchRequest<MessageDTO>(entityName: MessageDTO.entityName) | ||
MessageDTO.applyPrefetchingState(to: request) | ||
// Hard coded limit for now. 10 live locations messages at the same should be more than enough. | ||
request.fetchLimit = 10 | ||
request.sortDescriptors = [NSSortDescriptor( | ||
keyPath: \MessageDTO.createdAt, | ||
ascending: true | ||
)] | ||
var predicates: [NSPredicate] = [ | ||
.init(format: "location.endAt < %@", Date().bridgeDate), | ||
.init(format: "user.id == %@", currentUserId) | ||
] | ||
if let channelId { | ||
predicates.append(.init(format: "channel.cid == %@", channelId.rawValue)) | ||
} | ||
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates) | ||
return request | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Predicate selects expired instead of active live-location messages
activeLiveLocationMessagesFetchRequest
intends to return currently running live-location messages, however the predicate
.init(format: "location.endAt < %@", Date().bridgeDate)
matches rows whose endAt
is earlier than “now”, i.e. already expired.
It also ignores messages with an open-ended share (endAt == nil
) and messages without a location
object.
- .init(format: "location.endAt < %@", Date().bridgeDate),
+ // Active = (no end date OR end date is in the future)
+ NSPredicate(
+ format: "location != nil AND (location.endAt == nil OR location.endAt > %@)",
+ Date().bridgeDate
+ ),
While touching the predicate, consider lifting the magic fetchLimit = 10
into a named constant or a parameter to avoid accidental hard-caps.
🤖 Prompt for AI Agents
In Sources/StreamChat/Database/DTOs/MessageDTO.swift around lines 626 to 644,
the predicate incorrectly filters for expired live-location messages by checking
if location.endAt is less than the current date, excluding active messages and
those with nil endAt or missing location. Update the predicate to select
messages where location.endAt is either nil or greater than the current date to
capture active live-location messages, and also handle cases where location may
be nil. Additionally, replace the hardcoded fetchLimit of 10 with a named
constant or a function parameter to make the limit configurable and avoid
accidental hard caps.
...eamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swift
Outdated
Show resolved
Hide resolved
draft: DraftPayload? = nil, | ||
location: LocationPayload? = nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify LocationPayload and LocationRequestPayload types exist.
The init method parameter addition looks correct, but we should verify that the LocationPayload
type is properly defined and imported.
🏁 Script executed:
#!/bin/bash
# Description: Verify LocationPayload and LocationRequestPayload types are defined
# Expected: Find the definitions of these types
ast-grep --pattern 'struct LocationPayload'
ast-grep --pattern 'struct LocationRequestPayload'
# Also check if they are imported or defined in this module
rg "LocationPayload" --type swift -A 3
Length of output: 3672
Ensure LocationPayload Is Defined and Clarify LocationRequestPayload
- ✅ LocationPayload is defined in
Sources/StreamChat/APIClient/Endpoints/Payloads/LocationPayloads.swift
- ❌ No type named
LocationRequestPayload
was found in the codebase; if you intended to introduce a dedicated request payload type, please add or import it, or remove this reference from the review.
🤖 Prompt for AI Agents
In Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift around
lines 224 to 225, verify that LocationPayload is correctly imported from
LocationPayloads.swift since it is used as a parameter type. Also, since
LocationRequestPayload does not exist in the codebase, remove any references to
LocationRequestPayload or create and import this type if it is intended to be
used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
DemoApp/StreamChat/Components/CustomAttachments/DemoComposerVC.swift (1)
64-80
:⚠️ Potential issueCritical: Thread safety issue remains unresolved.
The completion handler from
locationProvider.getCurrentLocation
may execute on a background thread, but the code performs UI updates and SDK controller calls without ensuring main thread execution.As noted in the previous review, wrap the completion block in
DispatchQueue.main.async
:private func getCurrentLocationInfo(completion: @escaping (LocationInfo?) -> Void) { locationProvider.getCurrentLocation { [weak self] result in - switch result { - case .success(let location): - let location = LocationInfo( - latitude: location.coordinate.latitude, - longitude: location.coordinate.longitude - ) - completion(location) - case .failure(let error): - if error is LocationPermissionError { - self?.showLocationPermissionAlert() - } - completion(nil) - } + DispatchQueue.main.async { + switch result { + case .success(let location): + let location = LocationInfo( + latitude: location.coordinate.latitude, + longitude: location.coordinate.longitude + ) + completion(location) + case .failure(let error): + if error is LocationPermissionError { + self?.showLocationPermissionAlert() + } + completion(nil) + } + } } }Sources/StreamChat/APIClient/Endpoints/Payloads/LocationPayloads.swift (1)
7-23
: 🛠️ Refactor suggestionAddress previous review feedback on visibility and optionality.
The previous review comment about making these structs public and
createdByDeviceId
optional is still relevant and should be addressed.Apply the suggested changes for API robustness:
-struct SharedLocationPayload: Decodable { +public struct SharedLocationPayload: Decodable { let channelId: String let messageId: String let latitude: Double let longitude: Double let endAt: Date? - let createdByDeviceId: String + let createdByDeviceId: String?This prevents decoding failures when the server omits the device ID and ensures consistency with other public payload structs in the SDK.
🧹 Nitpick comments (1)
DemoApp/StreamChat/Components/CustomAttachments/DemoComposerVC.swift (1)
49-49
: Consider making live location duration configurable.The 2-hour duration is hardcoded, which limits flexibility for different use cases. Consider making this configurable through app settings or user selection.
You could add a configuration property or present duration options to the user:
// Option 1: Configuration property private let defaultLiveLocationDuration: TimeInterval = AppConfig.shared.defaultLiveLocationDuration // Option 2: User selection private func showLiveLocationDurationPicker(completion: @escaping (TimeInterval) -> Void) { // Present action sheet with duration options }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
DemoApp/StreamChat/Components/CustomAttachments/DemoComposerVC.swift
(1 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift
(2 hunks)Sources/StreamChat/APIClient/Endpoints/LocationEndpoints.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/Payloads/LocationPayloads.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift
(9 hunks)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
(7 hunks)Sources/StreamChat/Controllers/MessageController/MessageController.swift
(7 hunks)Sources/StreamChat/Database/DTOs/LocationDTO.swift
(1 hunks)Sources/StreamChat/Database/DTOs/MessageDTO.swift
(10 hunks)Sources/StreamChat/Database/DatabaseSession.swift
(4 hunks)Sources/StreamChat/Models/ChatMessage.swift
(6 hunks)Sources/StreamChat/Models/Location/SharedLocation.swift
(1 hunks)Sources/StreamChat/Workers/MessageUpdater.swift
(28 hunks)StreamChat.xcodeproj/project.pbxproj
(39 hunks)
✅ Files skipped from review due to trivial changes (2)
- Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift
- Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift
🚧 Files skipped from review as they are similar to previous changes (11)
- Sources/StreamChat/Models/Location/SharedLocation.swift
- Sources/StreamChat/Models/ChatMessage.swift
- StreamChat.xcodeproj/project.pbxproj
- Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift
- DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewDelegate.swift
- Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
- Sources/StreamChat/Database/DatabaseSession.swift
- Sources/StreamChat/Database/DTOs/LocationDTO.swift
- Sources/StreamChat/Workers/MessageUpdater.swift
- Sources/StreamChat/Controllers/MessageController/MessageController.swift
- Sources/StreamChat/Database/DTOs/MessageDTO.swift
🧰 Additional context used
🧬 Code Graph Analysis (1)
Sources/StreamChat/APIClient/Endpoints/LocationEndpoints.swift (1)
TestTools/StreamChatTestTools/TestData/DummyData/MessageAttachmentPayload.swift (1)
liveLocation
(190-203)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Test LLC (Debug)
- GitHub Check: Metrics
- GitHub Check: Build Test App and Frameworks
🔇 Additional comments (2)
Sources/StreamChat/APIClient/Endpoints/LocationEndpoints.swift (1)
7-43
: Well-structured endpoint definitions with clear separation of concerns.The endpoint implementations follow the SDK's established patterns effectively:
- Proper HTTP method usage (PUT for mutations, GET for queries)
- Consistent authentication requirements
- Type-safe return types with appropriate payload generics
- Clear documentation explaining the relationship to message creation
The shared
.liveLocation
path with different HTTP methods is a clean RESTful approach for managing the same resource lifecycle.DemoApp/StreamChat/Components/CustomAttachments/DemoComposerVC.swift (1)
82-97
: Good implementation of location permission handling.The permission alert provides clear guidance to users and offers a direct path to Settings. The implementation handles the permission error case appropriately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
Sources/StreamChat/Workers/CurrentUserUpdater.swift (2)
232-248
: Add documentation comments for consistency.The method lacks documentation comments while other methods in the class have comprehensive documentation. Please add documentation following the existing pattern to describe the method's purpose, parameters, and completion behavior.
+ /// Loads the current user's active live location messages from the backend. + /// + /// - Parameter completion: Called when the request is finished. Returns an array of `SharedLocation` objects on success, or an error on failure. func loadActiveLiveLocations(completion: @escaping (Result<[SharedLocation], Error>) -> Void) {
232-248
: Add async version for API consistency.Most other methods in this class have corresponding async versions in the extension (lines 251-330). Consider adding an async version of
loadActiveLiveLocations
to maintain consistency with the existing API design.Add this method to the async extension:
func updateUserData( currentUserId: UserId, name: String?, imageURL: URL?, privacySettings: UserPrivacySettings?, role: UserRole?, teamsRole: [TeamId: UserRole]?, userExtraData: [String: RawJSON]?, unset: Set<String> ) async throws { try await withCheckedThrowingContinuation { continuation in updateUserData( currentUserId: currentUserId, name: name, imageURL: imageURL, privacySettings: privacySettings, role: role, teamsRole: teamsRole, userExtraData: userExtraData, unset: unset ) { error in continuation.resume(with: error) } } } + + func loadActiveLiveLocations() async throws -> [SharedLocation] { + try await withCheckedThrowingContinuation { continuation in + loadActiveLiveLocations { result in + continuation.resume(with: result) + } + } + } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
DemoApp/Screens/DemoAppTabBarController.swift
(4 hunks)Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift
(1 hunks)Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift
(2 hunks)Sources/StreamChat/APIClient/Endpoints/LocationEndpoints.swift
(1 hunks)Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
(8 hunks)Sources/StreamChat/Controllers/MessageController/MessageController.swift
(7 hunks)Sources/StreamChat/Workers/CurrentUserUpdater.swift
(1 hunks)Sources/StreamChat/Workers/MessageUpdater.swift
(28 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- Sources/StreamChat/APIClient/Endpoints/EndpointPath.swift
- Sources/StreamChat/APIClient/Endpoints/EndpointPath+OfflineRequest.swift
- DemoApp/Screens/DemoAppTabBarController.swift
- Sources/StreamChat/Controllers/MessageController/MessageController.swift
- Sources/StreamChat/Workers/MessageUpdater.swift
- Sources/StreamChat/Controllers/CurrentUserController/CurrentUserController.swift
- Sources/StreamChat/APIClient/Endpoints/LocationEndpoints.swift
⏰ Context from checks skipped due to timeout of 90000ms (5)
- GitHub Check: Test LLC (Debug)
- GitHub Check: Build LLC + UI (Xcode 15)
- GitHub Check: Automated Code Review
- GitHub Check: Build Test App and Frameworks
- GitHub Check: Metrics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
StreamChat.xcodeproj/project.pbxproj
(35 hunks)TestTools/StreamChatTestTools/Extensions/Unique/ChatMessage+Unique.swift
(1 hunks)TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatMessage_Mock.swift
(2 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift
(4 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
(3 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/MessageUpdater_Mock.swift
(5 hunks)TestTools/StreamChatTestTools/SpyPattern/Spy/DatabaseContainer_Spy.swift
(2 hunks)TestTools/StreamChatTestTools/TestData/DummyData/MessageAttachmentPayload.swift
(1 hunks)TestTools/StreamChatTestTools/TestData/DummyData/MessagePayload.swift
(2 hunks)Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift
(5 hunks)Tests/StreamChatTests/Controllers/MessageController/MessageController_Tests.swift
(1 hunks)Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift
(13 hunks)Tests/StreamChatTests/Models/ChatMessage_Tests.swift
(1 hunks)Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift
(1 hunks)
✅ Files skipped from review due to trivial changes (4)
- TestTools/StreamChatTestTools/TestData/DummyData/MessageAttachmentPayload.swift
- TestTools/StreamChatTestTools/Extensions/Unique/ChatMessage+Unique.swift
- Tests/StreamChatTests/Models/ChatMessage_Tests.swift
- TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
🚧 Files skipped from review as they are similar to previous changes (4)
- StreamChat.xcodeproj/project.pbxproj
- Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift
- Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift
- Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift
⏰ Context from checks skipped due to timeout of 90000ms (5)
- GitHub Check: Build LLC + UI (Xcode 15)
- GitHub Check: Test LLC (Debug)
- GitHub Check: Automated Code Review
- GitHub Check: Build Test App and Frameworks
- GitHub Check: Metrics
🔇 Additional comments (10)
TestTools/StreamChatTestTools/Mocks/Models + Extensions/ChatMessage_Mock.swift (1)
53-54
: LGTM! Clean addition of location sharing support to test mocks.The
sharedLocation
parameter addition follows the established pattern of other optional parameters in the mock factory, with appropriate type and default value.Also applies to: 95-96
TestTools/StreamChatTestTools/TestData/DummyData/MessagePayload.swift (1)
54-55
: LGTM! Proper addition of location sharing support to dummy data factory.The parameter mapping from
sharedLocation
tolocation
is correct and aligns with theMessagePayload
initializer signature. The type and default value are appropriate for payload-level testing.Also applies to: 97-98
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (2)
143-143
: LGTM! Correct addition of location parameter to message creation.The
location
parameter is properly typed asNewLocationInfo?
for the database layer and correctly forwarded to the underlying session.Also applies to: 167-167
267-269
: LGTM! Proper implementation of location saving delegation.The
saveLocation
method follows the established delegation pattern used throughout the mock class, correctly forwarding to the underlying session.Tests/StreamChatTests/Controllers/MessageController/MessageController_Tests.swift (3)
2576-2583
: LGTM - Well-structured test helper method.The helper method properly handles async delegate callback testing with XCTestExpectation and follows standard XCTest patterns.
2585-2647
: Comprehensive test coverage for partial message updates.The test methods properly cover the new
partialUpdateMessage
functionality with appropriate parameter validation, error propagation, and success handling. The async testing patterns and mocking approach are consistent with the existing codebase.
2649-2829
: Excellent test coverage for live location functionality.The live location tests comprehensively cover both success and error scenarios for
updateLiveLocation
andstopLiveLocationSharing
methods. The tests properly validate:
- Correct parameter passing to the message updater
- Error handling for edge cases (already stopped, no attachment)
- Proper async behavior with expectations
- Correct error types returned
The mocking strategy using
SharedLocation
objects and message observer is appropriate and follows existing patterns in the codebase.TestTools/StreamChatTestTools/SpyPattern/Spy/DatabaseContainer_Spy.swift (1)
294-294
: LGTM! Clean addition of location support to test infrastructure.The new
location
parameter is properly integrated into the message creation flow with appropriate default value and consistent naming.Also applies to: 324-324
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/MessageUpdater_Mock.swift (2)
33-38
: LGTM! Well-structured mock properties for new location features.The atomic properties follow the established pattern and properly capture all necessary parameters for testing the location-related methods.
Also applies to: 125-130
339-355
: LGTM! Mock implementations correctly follow established patterns.The new mock methods properly capture parameters and support completion handler testing, maintaining consistency with existing mock implementations.
Also applies to: 564-578
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/MessageUpdater_Mock.swift
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewInjector.swift (1)
18-18
: Apply width constraint to the correct viewThe width constraint is still being applied to
bubbleThreadFootnoteContainer
instead oflocationAttachmentView
, which means the footnote container gets stretched while the map keeps its intrinsic width.- contentView.bubbleThreadFootnoteContainer.width(mapWidth) + locationAttachmentView.width(mapWidth)
🧹 Nitpick comments (1)
DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift (1)
134-149
: Consider making animation duration configurableThe 5-second animation duration is hardcoded and might not feel natural for all use cases. The comment explains the rationale, but this could be made configurable or calculated based on update frequency.
+private let locationUpdateAnimationDuration: TimeInterval = 5.0 +private let mapCenterAnimationDuration: TimeInterval = 5.2 - UIView.animate(withDuration: 5, delay: 0, options: .allowUserInteraction) { + UIView.animate(withDuration: locationUpdateAnimationDuration, delay: 0, options: .allowUserInteraction) { existingAnnotation.coordinate = coordinate } if isAutoCenteringEnabled { - UIView.animate(withDuration: 5, delay: 0.2, options: [.curveEaseOut, .allowUserInteraction]) { + UIView.animate(withDuration: mapCenterAnimationDuration, delay: 0.2, options: [.curveEaseOut, .allowUserInteraction]) { self.mapView.setCenter(coordinate, animated: true) } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewInjector.swift
(1 hunks)DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift
(2 hunks)Sources/StreamChat/Workers/MessageUpdater.swift
(28 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- Sources/StreamChat/Workers/MessageUpdater.swift
🔇 Additional comments (4)
DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationAttachmentViewInjector.swift (2)
20-26
: Good separation of tap handlersThe implementation correctly separates location tap handling from stop sharing action, addressing the previous concern about conflating these two distinct user actions.
39-47
:✅ Verification successful
Verify shared location property availability
The code accesses
contentView.content?.sharedLocation
but ensure this property is consistently available across all message types that use this injector.
🏁 Script executed:
#!/bin/bash # Description: Verify that sharedLocation property exists and is used consistently # Expected: Find the property definition and its usage patterns # Search for sharedLocation property definition ast-grep --pattern 'var sharedLocation: $_' # Search for sharedLocation usage in message content rg -A 3 -B 3 'sharedLocation'Length of output: 35406
No action needed:
sharedLocation
is always available
ThesharedLocation
property is declared onChatMessage
(Sources/StreamChat/Models/ChatMessage.swift) as an optional and is consistently populated from payloads (MessagePayload.sharedLocation
). The injector already safely unwraps it before use—no further changes are required.DemoApp/StreamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift (2)
201-227
: Good implementation of live location updatesThe message controller delegate implementation correctly handles live location updates, including stopping animations when sharing ends and updating the banner state.
57-59
:✅ Verification successful
Verify memory management in closure
The closure captures
self
weakly, which is correct, but ensure thatmessageController.stopLiveLocationSharing()
doesn't create retain cycles or unexpected behavior when called after the view controller is deallocated.
🏁 Script executed:
#!/bin/bash # Description: Check if stopLiveLocationSharing method properly handles controller lifecycle # Expected: Find the method implementation and verify it handles weak references # Search for stopLiveLocationSharing implementation ast-grep --pattern 'func stopLiveLocationSharing() { $$$ }' # Search for potential retain cycle patterns rg -A 5 'stopLiveLocationSharing.*{'Length of output: 218
🏁 Script executed:
#!/bin/bash set -e # Find all occurrences of stopLiveLocationSharing (definition and calls) in the Swift codebase rg -n "stopLiveLocationSharing" -C 3 --glob "*.swift"Length of output: 13545
No retain cycle detected in
onStopSharingTapped
closure
The banner’s closure correctly capturesself
weakly, and callingmessageController.stopLiveLocationSharing()
doesn’t introduce any strong reference back to the view controller. No further changes are needed.
...treamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift
Show resolved
Hide resolved
...treamChat/Components/CustomAttachments/LocationAttachment/LocationDetailViewController.swift
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
899-969
: Solid implementation with room for optimization.The
startLiveLocationSharing
method is well-implemented with proper validation and conflict prevention. The async check for existing active live location messages correctly prevents duplicate live sharing.However, consider addressing the TODO comment on line 927 - using
ChatChannel.activeLiveLocations
might be a more efficient approach than querying the message repository.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
Sources/StreamChat/APIClient/Endpoints/Payloads/LocationPayloads.swift
(1 hunks)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift
(6 hunks)Sources/StreamChat/Workers/MessageUpdater.swift
(28 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- Sources/StreamChat/APIClient/Endpoints/Payloads/LocationPayloads.swift
- Sources/StreamChat/Workers/MessageUpdater.swift
🔇 Additional comments (6)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (6)
71-73
: LGTM! Clean property addition following existing patterns.The
channelMemberUpdater
property is properly documented and follows the same pattern as other updater components in the class.
233-233
: Consistent initialization pattern.The
channelMemberUpdater
initialization follows the established dependency injection pattern used by other updaters in the controller.
844-898
: Well-implemented static location method following established patterns.The
sendStaticLocation
method correctly:
- Validates channel state before proceeding
- Uses comprehensive parameter documentation
- Follows consistent error handling and callback patterns
- Sets
endAt: nil
for static locations, which is appropriateThe implementation integrates well with the existing message creation infrastructure.
2043-2043
: Good visibility improvement addressing previous feedback.Making the
ClientError
extension public is appropriate as it allows customers to handle specific error types in their applications, which aligns with the previous review feedback.
2068-2077
: Well-designed error class with meaningful context.The
ActiveLiveLocationAlreadyExists
error class is properly implemented:
- Includes the
messageId
of the conflicting message for better debugging and error handling- Provides a descriptive error message
- Follows the established pattern of other
ClientError
subclassesThis gives developers clear information about what caused the conflict.
999-999
: Minor formatting improvement.
🔗 Issue Links
Resolves https://linear.app/stream/issue/IOS-578/location-attachment
🎯 Goal
Adds support for static and live location attachments in the Low-Level Client SDK.
The UI has been implemented in the Demo App to demonstrate how to use the new location APIs.
📝 Summary
New APIs:
ChatChannelController
sendStaticLocation()
- Sends a static location message to the channel.startLiveLocationSharing()
- Starts a live location-sharing message in the channel.ChatMessageController
partialUpdateMessage()
- Updates the message partially. (It was missing from the SDK)stopLiveLocationSharing()
- Stops sharing the live location attachment if it has one.CurrentChatUserController
updateLiveLocation()
- Updates the location of all active live location messages for the current user.loadActiveLiveLocationMessages()
- Loads all active locations of the current user.CurrentChatUserControllerDelegate
didStartSharingLiveLocation()
- Notifies whenever the current user is sharing any live location.didStopSharingLiveLocation()
- Notifies whenever the current user stopped/expired all live locations.didChangeActiveLiveLocationMessages()
- Notifies whenever the active live location messages update.Throttler
ChatMessage
sharedLocation
- Returns the location if it has one, either live or static.🛠 Implementation
The SDK at the moment only handles updating the location attachments. The location tracking should be provided by the App. Something like the
LocationProvider
in the Demo App should be implemented by the customer.Creating a location attachment
In order to create a new message with a location attachment, the developer can use the
ChannelController.sendStaticLocation()
or theChannelController.startSharingLiveLocation()
.Sending location updates (Live Location)
The customer is responsible for sending new location updates to the SDK. This is done through the
CurrentChatUserController.updateLiveLocation()
method. This method will update all the current user's active location attachments. Internally, it uses aactiveLiveLocationMessagesObserver
that keeps track of the active location attachments of the current user. These changes are also available to the customer through theCurrentChatUserControllerDelegate
to make it easier for the developer to know when it should track location updates and when it can turn them off.Stopping live location attachment
To stop a live location attachment, it can be done through 2 methods:
ChatMessageController.stopLiveLocationSharing()
: Directly tries to stop a live location attachment in the given message if it has an active location attachment.Overall Data Flow
🎨 Showcase
static.mp4
live.mp4
🧪 Manual Testing Notes
Note: To simulate live location updates in the Simulator, with the Simulator selected, Go to
Features > Location > City Bicycle Ride
in the top bar of the Mac.Static
Live
Make sure to test from the current user's perspective and the other user's perspective.
☑️ Contributor Checklist
Summary by CodeRabbit
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Documentation
Tests