Skip to content

Conversation

@laevandus
Copy link
Contributor

@laevandus laevandus commented Nov 13, 2025

🔗 Issue Links

Fixes: IOS-1236

🎯 Goal

Keep FeedData.ownCapabilities up to date when handling web-socket events

📝 Summary

Additional context: SDK has StateLayerEventPublisher which publishes events from web-socket events and from API calls (StateLayerEvent is an enum).

  • Locally cache own capabilities per feed
    • Explicitly saving capabilities from HTTP requests without local events
      • This is implemented by Feed, FeedList, Activity, ActivityList, BookmarkList, FollowList
    • Extracting capabilities from local events (local event is sent after HTTP response for propagating data, these events contain own fields because HTTP response data is used)
    • Extracting capabilities from local events required adding middleware support to StateLayerEventPublisher
  • Update all the models when we detect that cached capabilities for feed(s) changed (there is a new local feedOwnCapabilitiesUpdated for propagating this data)
    • This is observed by Feed, FeedList, Activity, ActivityList, BookmarkList, FollowList
  • Automatically fetch capabilities if we don't have local data on web-socket events (avoids the case where activity added event comes in and Activity.currentFeed.ownCapabilities is nil).

🛠 Implementation

Since timeline feeds contain activities from different feeds, we need to read activity.current_feed.own_capabilities to decide what interactions are allowed for a given activity. Backend can't send own_capabilities in WS events, activity.current_feed.own_capabilities will be empty on feeds.activity.added events (and other).

Agreement was that, although local caching and updating is not ideal, there is not a viable way to workaround it on the backend side.

🎨 Showcase

🧪 Manual Testing Notes

☑️ Contributor Checklist

  • I have signed the Stream CLA (required)
  • This change should be manually QAed
  • Changelog is updated with client-facing changes
  • Changelog is updated with new localization keys
  • New code is covered by unit tests
  • Documentation has been updated in the docs-content repo

* Add middleware support to StateLayerEventPublisher
* Add own capabilities middleware which enriches incoming WS events and extracts and stores own capabilities from local events
* State layer explicitly saves own capabilities from get or query methods which do not post local events
@laevandus laevandus requested a review from a team as a code owner November 13, 2025 11:09
@laevandus laevandus marked this pull request as draft November 13, 2025 11:09

let user: UserData
let ownCapabilities: [FeedOwnCapability]
let ownCapabilities: Set<FeedOwnCapability>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For O(1) lookups

Comment on lines 23 to 24
/// A delay in seconds until feed own capabilities are fetched for feeds delivered through web-socket and there is no cached data available.
var automaticFeedOwnCapabilitiesFetchDelay: Int = 5
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentionally internal for now. Used by tests

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels a bit unnecessary imo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'll take it away and we can add that if it becomes a problem. It should be rare that it gets triggered often (only thinking about the timeline feed case where new activities can point at many other feeds). But event then, most probably the state is already cached.

Comment on lines +64 to +66
func saveCapabilities(in feedData: FeedData?) -> [FeedId: Set<FeedOwnCapability>]? {
saveCapabilities(in: [feedData].compactMap { $0 })
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convenience mostly for ActivityData.currentFeed (optional)

@laevandus laevandus added the ✅ Feature An issue or PR related to a feature label Nov 13, 2025
@github-actions
Copy link

1 Message
📖 Skipping Danger since the Pull Request is classed as Draft/Work In Progress

Generated by 🚫 Danger

@laevandus laevandus marked this pull request as ready for review November 13, 2025 12:51
@laevandus laevandus marked this pull request as draft November 14, 2025 07:56
@github-actions
Copy link

Public Interface

 public struct ActivityData: Identifiable, Equatable, Sendable  
-   public let currentFeed: FeedData?
+   public private var currentFeed: FeedData?

 public struct FollowData: Equatable, Sendable  
-   public let sourceFeed: FeedData
+   public private var sourceFeed: FeedData
-   public let targetFeed: FeedData
+   public private var targetFeed: FeedData

 public struct BookmarkData: Equatable, Sendable  
-   public let activity: ActivityData
+   public private var activity: ActivityData

 public struct FeedData: Identifiable, Equatable, Sendable  
-   public let ownCapabilities: [FeedOwnCapability]?
+   public private var ownCapabilities: Set<FeedOwnCapability>?
-   public let ownFollows: [FollowData]?
+   public private var ownFollows: [FollowData]?
-   public let ownMembership: FeedMemberData?
+   public private var ownMembership: FeedMemberData?

@Stream-SDK-Bot
Copy link
Collaborator

SDK Size

title develop branch diff status
StreamFeeds 7.07 MB 7.13 MB +68 KB 🟢

@sonarqubecloud
Copy link

@laevandus laevandus marked this pull request as ready for review November 14, 2025 09:27
@Stream-SDK-Bot
Copy link
Collaborator

StreamFeeds XCSize

Object Diff (bytes)
OwnCapabilitiesStateLayerEventMiddleware.o +14415
OwnCapabilitiesRepository.o +13324
ActivityMarkData.o +9311
FeedState.o +6580
FeedsRepository.o +4346
Show 21 more objects
Object Diff (bytes)
ActivityListState.o +3116
StateLayerEventPublisher.o +2670
FollowList.o +2249
FollowListState.o +2146
BookmarkList.o +2133
BookmarkListState.o +2088
Array+Extensions.o +1814
FeedListState.o +1694
ActivityList.o +1401
FeedList.o +896
Feed.o +785
ActivityData.o +740
FeedData.o +688
Activity.o +679
StreamAttachments.o -564
FeedsClient.o +440
NotificationStatusData.o -436
DefaultAPI.o +338
PollListState.o +200
ActivityState.o -188
ActivityReactionList.o +152

case bodyType(Decodable.Type)
}

struct APIResponse {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have tests where the order of requests is not serial, therefore a bit more sophisticated mocking is required. E.g. events can trigger capabilities fetch.

@laevandus laevandus merged commit ff79f8b into develop Nov 14, 2025
6 checks passed
@laevandus laevandus deleted the feature/update-feed-own-capabilities branch November 14, 2025 12:10
@Stream-SDK-Bot Stream-SDK-Bot mentioned this pull request Nov 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✅ Feature An issue or PR related to a feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants