UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Writing a JSON file and reading its content in a home screen Widget. What am I missing here?

Forums > SwiftUI

Hello!

As the title suggests i'm creating a JSON file in my app and then i'd like the home screen Widget to read that file to populate the widget. It seems like the file is being written OK because I write the file and then for debugging I read it and print it in the console.

I write to the JSON like this:

struct WidgetMetric: Codable {
    var JSON_slug: String
    var JSON_currency: String
    var JSON_TotalRevenue: Double
    var JSON_MissedRevenue: Int
    var JSON_RevenueProjection: Double
    var JSON_revenuePercent: Double
    var JSON_revenueDirection: Int
    var JSON_VisitValueProjection: Double
    var JSON_VisitValuePercent: Double
    var JSON_VisitValueDirection: Int
    var JSON_TransValueProjection: Double
    var JSON_TransValuePercent: Double
    var JSON_TransValueDirection: Int
    var JSON_DurationMonths: Int
}

func WidgetJSONGeneration(slug: String, currency: String, TotalRevenue: Double, MissedRevenue: Int, RevenueGrowth: Double, revenuePercent: Double, revenueDirection: Int, VisitValue: Double, VisitValuePercent: Double, VisitValueDirection: Int, TransValue: Double, TransValuePercent: Double, TransValueDirection: Int, DurationMonths: Int) async -> String {

    let widgetMetrics = [
        WidgetMetric(
            JSON_slug: slug,
            JSON_currency: currency,
            JSON_TotalRevenue: TotalRevenue,
            JSON_MissedRevenue: MissedRevenue,
            JSON_RevenueProjection: RevenueGrowth,
            JSON_revenuePercent: revenuePercent,
            JSON_revenueDirection: revenueDirection,
            JSON_VisitValueProjection: VisitValue,
            JSON_VisitValuePercent: VisitValuePercent,
            JSON_VisitValueDirection: VisitValueDirection,
            JSON_TransValueProjection: TransValue,
            JSON_TransValuePercent: TransValuePercent,
            JSON_TransValueDirection: TransValueDirection,
            JSON_DurationMonths: DurationMonths
        )
    ]

    // Encode the array as JSON data
    let encoder = JSONEncoder()
    encoder.outputFormatting = .prettyPrinted
    let jsonData = try! encoder.encode(widgetMetrics)

    // Write the JSON data to a file
    let documentsUrl = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
    let fileUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("mobData.json")
    try! jsonData.write(to: fileUrl)

    print("mob: WidgetJSONGeneration. JSON file URL:")
    print(fileUrl)

    return("JSONGenerated")
}

And to test I read it like this. the JSON displays OK.

func readJSON() -> [WidgetMetric]? {
        print("")
        print("mob. ConnectToMob: Generated JSON:")
        print("")
        let fileUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("mobData.json")
        do {
            let data = try Data(contentsOf: fileUrl)
            let decoder = JSONDecoder()
            let widgetMetrics = try decoder.decode([WidgetMetric].self, from: data)
            return widgetMetrics
        } catch {
            print("Error reading JSON file: \(error.localizedDescription)")
            return nil
        }
    }

Then in my home screen Widget I basically use the same code to read the JSON as follows

func readJSON() -> [WidgetMetric]? {
    print("")
    print("mob. Mob_Widget: JSON found:")
    print("")
    let fileUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("mobData.json")
    do {
        let data = try Data(contentsOf: fileUrl)
        let decoder = JSONDecoder()
        let widgetMetrics = try decoder.decode([WidgetMetric].self, from: data)

        // Refresh the widget content
        //WidgetCenter.shared.reloadAllTimelines()

        return widgetMetrics
    } catch {
        print("Error reading JSON file: \(error.localizedDescription)")
        print("JSON file URL: \(fileUrl)")
        return nil
    }

But when the widget tries to read the file I get the error:

Impossible to open the file because it does not exist

Thats because the fileUrl in the app and the widget are different. If I look at the fileUrl when the JSON is created in the main app it's:

file:///Users/jas/Library/Developer/CoreSimulator/Devices/AB0B8C29-139F-493A-AEE3-0C744D331F03/data/Containers/Data/Application/EB432EE6-2983-46FF-995B-63F9F79877D1/Documents/mobData.json

and the fileUrl in the home screen Widget is:

file:///Users/jas/Library/Developer/CoreSimulator/Devices/AB0B8C29-139F-493A-AEE3-0C744D331F03/data/Documents/mobData.json

So in summary, that's a long way of asking why is the fileUrl in the app and the widget different?

Thanks for any advice! :)

1      

Solved: In order to share data like a JSON between an app and an app extension like a home screen Widget you need to setup a shared container using App Groups.

In my case doing this resulted in the same URL being created in the app and accessed in the home screen Widget.

1      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.