Add the ability to import and export saved posts.#25509
Conversation
Generated by 🚫 Danger |
|
| App Name | WordPress | |
| Configuration | Release-Alpha | |
| Build Number | 32217 | |
| Version | PR #25509 | |
| Bundle ID | org.wordpress.alpha | |
| Commit | ee074ab | |
| Installation URL | 5opkeru09qck0 |
|
| App Name | Jetpack | |
| Configuration | Release-Alpha | |
| Build Number | 32217 | |
| Version | PR #25509 | |
| Bundle ID | com.jetpack.alpha | |
| Commit | ee074ab | |
| Installation URL | 1hbhk892707s0 |
d2b6618 to
8209a08
Compare
3b1a46f to
06c723d
Compare
🤖 Build Failure AnalysisThis build has failures. Claude has analyzed them - check the build annotations for details. |
06c723d to
6f4c403
Compare
Lets the VM be unit-tested without ContextManager.shared, and marks the alert message strings @published so SwiftUI re-reads them when the isShowing* flag flips.
Adds three Tracks events: - reader_saved_posts_settings_shown when the screen appears - reader_saved_posts_exported on a successful export - reader_saved_posts_imported with imported/skipped/failed counts
| let appName = (Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String) ?? "WordPress" | ||
| let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String |
There was a problem hiding this comment.
These can be force-unwrapped – we'd never ship a release that doesn't have them.
Realistically we should have a helper that does it for you 🤷
| import WordPressData | ||
|
|
||
| /// Handles exporting and importing saved Reader posts as JSON files. | ||
| struct ReaderSavedPostsExporter { |
There was a problem hiding this comment.
The implementation does not quite follow the patterns used widely in the app. I have added a doc in #25551. I wonder if you can prompt AI to rewrite to follow the updated best practices?
There was a problem hiding this comment.
Sounds good! Once that's merged I can have it do another pass.
There was a problem hiding this comment.
If we're taking another look at this, I'd really like to see any serialization and parsing use Codable – String:Any is too loose about the format (which means we don't get compile-time detection of problems, we get runtime ones)
|
Instead of arbitrary JSON, what do you think about using the standard RSS? |
Replaces [String: Any] envelopes with typed Envelope and ExportedPost structs so the JSON format is validated at compile time rather than at runtime.
| coreDataStack: CoreDataStack, | ||
| progress: @escaping (Int, Int) -> Void, | ||
| completion: @escaping (ImportResult) -> Void | ||
| ) { |
There was a problem hiding this comment.
I think making this function async would simplify the implementation:
- You can take
Progress(which SwiftUI supports) as a parameter, and update itscompletedUnitandtotalUnitin this function to surface importing progress. - You can use TaskGroup to import posts in parallel.
- It'd be impossible to forget calling the completion block.
- The implementation uses
coreDataStack.mainContext, which means the function must be called from the main thread. You can enforce that by declaring the function@MainActor. However, I don't think this is necessary, because you should be able to usecoreDataStack.performAndSaveto save Core Data after a post is imported.
|
|
||
| func exportSavedPosts() { | ||
| do { | ||
| guard let fileURL = try exporter.export(context: coreDataStack.mainContext) else { |
There was a problem hiding this comment.
It's probably best to export in a background thread.
| featuredImageURL: (featuredImage?.isEmpty ?? true) ? nil : featuredImage, | ||
| siteID: siteID > 0 ? siteID : nil, | ||
| postID: postID > 0 ? postID : nil, | ||
| isFeed: post.isExternal |
There was a problem hiding this comment.
I think you'll need to include the feed id and feed item id in the JSON, when isExternal is true? When it's true, I think the siteId and postId would be nil.


Summary
{AppName}-Saved-Posts-{date}.jsonfile containing post metadata (URL, title, author, site, date, summary, tags, featured image, siteID, postID) along with the app version that exported it.ReaderPostServicepipeline to create fully-populated Core Data objects. Posts are fetched sequentially to avoid race conditions. Deduplication usespermaLink— posts whose URL is already saved are skipped, so importing the same file multiple times or merging lists from different devices is safe.Test plan
🤖 Generated with Claude Code