[Bug]: Refreshing FeedViewPostDefinition doesn't work
Summary
Can't refresh FeedViewPostDefinition via the provided method
Reproduction Steps
Calling this function doesn't work.
Expected Results
Updated feed view to be returned.
Actual Results
Getting error: "could not resolve feed did"
What operating systems did you experience this bug?
iOS/iPadOS
Operating System Version
iOS 18
ATProtoKit Version
0.26.6
Additional Context
No response
I understand a little bit of what's going on with respect to that. I'll have to investigate this to make sure I'm able to replicate this bug, but it may take a bit to do so. Please bare with me here.
I've only now realized that this issue is completely separate from #146...
This specific bug shouldn't be too difficult to solve.
I said that this wouldn't be too difficult to solve, but it does seem that it is, indeed, difficult to solve.
Unless I've misunderstood, this refreshing a post from one of the feeds, not the feed itself. I could have sworn I tested all of the methods, but perhaps I tested one of them twice, thinking that this was tested as well.
I'll keep researching, but doing this may not entirely be possible to do, given how the lexicon works.
After trying various ways, I couldn't resolve this issue. The problem is that it needs to know where in the array to update the post so it can remove the old version and replace it with a new version. I can't seem to find a way to do this in an elegant sort of way. The only way to do this is to ask for an index number.
So something like this:
AppBskyLexicon.Feed.FeedViewPostDefinition.refresh(with: session, from: array, at: index)
Is this alright with you? If not, then I'm not sure how else to resolve this issue.
Thank you for the investigations into this. Yes the above method would work for my use case.
I've dug deeper into this now.
I've looked into the TypeScript code from the official implementation. The "could not resolve feed did" error that's being outputted is because my code is, indeed, giving out the incorrect DID (however, the error code is misleading: it's saying "did" when in actuality, it's asking for the AT URI of the feed).
The current way that the code is written, as well as the proposed solution I've made, makes it impossible to get the AT URI of the feed. So, in order to solve this problem, I need to additionally add a parameter field for the AT URI.
AppBskyLexicon.Feed.FeedViewPostDefinition.refresh(with: session, from: &array, at: index, feedURI: uri)
But then the other problem is that there's the issue of what happens if that post is over the index of 100. If the index is at a very high number (let's say 1,000), then that means 10 of the same API calls, which means you could (potentially) get rate limited. Therefore, I think that the appropriate thing for me to do is to limit this to index 100. If it goes above that limit, then an error will be thrown to state the post is too far past the original index.
Things might change, but this is the solution I have right now.
I've completed it. The final method looks like this:
public func refresh(
with atProtoKitConfiguration: ATProtoKit,
from array: [AppBskyLexicon.Feed.FeedViewPostDefinition],
at index: Int,
feedURI: String
) async throws -> [AppBskyLexicon.Feed.FeedViewPostDefinition] {
let post = try await atProtoKitConfiguration.getFeed(by: feedURI).feed[index]
if index > 99 {
throw FeedViewPostDefinitionError.indexTooHigh(index: index)
}
var newArray = array
newArray[index] = post
return newArray
}
I'm currently testing it, but it will be out in the next update. However, this is a very inelegant solution and I'm unsure if this will be even useful enough for you. But this is the best I can do, given how Bluesky gives out the output of the feed generator's posts.
Thank you for your efforts and for putting this together. I appreciate it. My specific use case was updating posts after liking them or removing likes. Curious to hear whether there's a more obvious way of doing this?
Ah, I understand now..
You could wrap PostViewDefinition in a class and conform it with ObservableObject (since structs and enums can't use ObservableObject), then initialize it with the post view object itself. So something akin to this (this is not 1:1, but you can imagine it would look something like this):
final class PostModel: ObservableObject, Identifiable {
// Reference to the immutable API struct
private(set) var post: AppBskyLexicon.Feed.PostViewDefinition
public let id: UUID
public let displayName: String
public let date: Date
public let text: String
public let repostCount: Int
@Published public private(set) var likeCount: Int
@Published public private(set) var isLiked: Bool
public init(post: AppBskyLexicon.Feed.PostViewDefinition) throws {
self.post = post
self.id = post.id
self.displayName = post.author.displayName
self.date = post.date
self.text = post.record.getRecord(ofType: AppBskyLexicon.Feed.PostRecord.self)?.text
self.repostCount = post.repostCount
self.likeCount = post.likeCount
self.isLiked = post.viewer.likeURI != nil ? true : false
}
}
And then you can use the .refresh() method in AppBskyLexicon.Feed.PostViewDefinition and add that in an update() method. You can also have a like() and unlike() method:
- The
like()method will create the like record, then increment the number and switch theisLikedproperty totrue(if successful and if no like record exists; you can just check ifisLikedistruebefore creating the record). - The
unlike()method would check ifisLikedistrue, and if it does, delete the like record (likeURIwould be the one to use), then decrement the number and switch theisLikedproperty tofalse.
Note this is for SwiftUI: things are different with UIKit and AppKit, but I don't exactly remember the layout for that.
As for why I'm not making this as a built-in feature, well, it's because it's a lot of work for me to do this and maintain. 😅 It doesn't mean I won't do it: it just means that the bandwidth I have currently won't allow me to do something like this, and I need to seriously think of the common use cases (whether in SwiftUI, AppKit, or UIKit). Perhaps in the future, I will, but for now, this is what I can do to help you at this time.
Hopefully this helps and I apologize if I'm going all over the place: I just want to answer the questions and potential follow up questions that I thought you might have. Still, please let me know if you're confused about anything.