ATProtoKit icon indicating copy to clipboard operation
ATProtoKit copied to clipboard

[Bug]: Encoding/Decoding array of AppBskyLexicon.Feed.FeedViewPostDefinition not working

Open ShihabM opened this issue 7 months ago • 11 comments

Summary

When caching feeds, they don't correctly save the Unknown types so decoding doesn't show the text content of the post.

Reproduction Steps

  • Save/cache the array of AppBskyLexicon.Feed.FeedViewPostDefinition
  • Fetch and decode from the saved location
  • Record data has wrong type and is therefore missing

Expected Results

Correct saving and decoding of Unknown types.

Actual Results

Missing or wrong types.

What operating systems did you experience this bug?

iOS/iPadOS

Operating System Version

iOS 18

ATProtoKit Version

0.62.5

Additional Context

No response

ShihabM avatar May 20 '25 09:05 ShihabM

This is a bit confusing. Could you show me the code you used in order to accomplish this? What method did you use to output AppBskyLexicon.Feed.FeedViewPostDefinition? How are you caching the data?

MasterJ93 avatar May 21 '25 03:05 MasterJ93

Using the following to output the data: try await atProto.getTimeline(limit: 100).feed, and caching using the Disk library which saves to the device's Documents folder, and retrieved from there too (whilst decoding back to an array of AppBskyLexicon.Feed.FeedViewPostDefinition). Hope this helps!

ShihabM avatar May 21 '25 08:05 ShihabM

Hey, just wanted to ask whether there was any updates/progress on this issue? Would love to cache the timelines feed data correctly.

ShihabM avatar May 27 '25 11:05 ShihabM

I'm struggling to replicate this issue. I do have some questions if you don't mind me asking:

  1. Are you using a third-party library to cache the data, using an Apple API, or using your own code?
  1. Do you mind sending me a GitHub Gist (private) of the code in question and send the link to me via a DM so I can potentially replicate it?
  2. Do you have an output of the JSON?

The answers should help me to pinpoint the problem better.

MasterJ93 avatar May 27 '25 15:05 MasterJ93

I'm using the Disk library for caching data (but the same happens with Apple's own APIs).

Here's the gist: https://gist.github.com/ShihabM/ef994ebbe22e0fab4a9b402ba023a23b

ShihabM avatar May 28 '25 12:05 ShihabM

Thanks. I have a better understanding of it, but I haven't had time to test this until now. Please bare with me a little while longer.

MasterJ93 avatar May 30 '25 03:05 MasterJ93

Thank you for the heads up. Wondering if there's any new updates on this?

ShihabM avatar Jun 05 '25 13:06 ShihabM

Yes, and I'm still looking into it; I'm just having to juggle many other tasks at the same time. I'll get back to you shortly.

MasterJ93 avatar Jun 08 '25 05:06 MasterJ93

Thank you for waiting.

I've tested your gist and it does indeed not show anything. However, I made some tweaks with the code. Here is what it looks like:

import UIKit
import ATProtoKit
import Disk

class ViewController: UIViewController {

    var allPosts: [AppBskyLexicon.Feed.FeedViewPostDefinition] = []
    var getTimelineResult: [AppBskyLexicon.Feed.FeedViewPostDefinition] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        Task {
            do {
                try await loadFromDisk()
            } catch {
                throw error
            }
        }

    }

    func saveToDisk() async {
        do {
            try Disk.save(getTimelineResult, to: .documents, as: "allPosts.json")
        } catch {
            print("error saving to Disk: \(error)")
        }
    }

    func loadFromDisk() async throws {
        let config = ATProtocolConfiguration()

        try await config.authenticate(with: "[handle]", password: "[password]")

        let atProtoKit = await ATProtoKit(sessionConfiguration: config)
        getTimelineResult = try await atProtoKit.getTimeline(limit: 3).feed

        print("allPosts: \(allPosts)")
        await saveToDisk()

        allPosts = try Disk.retrieve("allPosts.json", from: .documents, as: [AppBskyLexicon.Feed.FeedViewPostDefinition].self)


        print(
            "First post text:\(allPosts.first?.post.record.getRecord(ofType: AppBskyLexicon.Feed.PostRecord.self)?.text ?? "No data.")\n\nSecond post text: \(allPosts[1].post.record.getRecord(ofType: AppBskyLexicon.Feed.PostRecord.self)?.text ?? "No data.")"
        )
    }
}

This is similar to your gist except that I'm calling the saveToDisk method, I've checked that allPosts is empty before the Disk Swift package is saving to and retrieving from the cache, and I've printed the first two post text. This works as intended without the need to change ATProtoKit directly.

From your GitHub Gist, it appears that you forgot to call the saveToDisk function. Because of this, the cache was never created in the first place.

Also, in the "Actual Results" section, you mentioned "[...] wrong types." I've tested this with various method calls and I haven't been able to find an instance where the wrong type appears.

Please check your code to see if you forgot to save the cache.

MasterJ93 avatar Jun 08 '25 08:06 MasterJ93

Thank you for investigating. When I retrieve immediately, it seems to work. But when I retrieve in viewDidLoad after a fresh launch, it doesn't. The type of the record is ATProtoKit.UnknownType.unknown([]), which I don't think is handled in ATRecordProtocol.swift:

public func getRecord<Record: ATRecordProtocol>(ofType type: Record.Type) -> Record? {
   guard case .record(let record as Record) = self else {
      return nil
   }

   return record
}

And so when attempting to use getRecord, it's empty.

ShihabM avatar Jun 09 '25 11:06 ShihabM

I don't follow. Do you mind sending a private gist of what you're doing that causes this issue?

MasterJ93 avatar Jun 09 '25 22:06 MasterJ93