0

Let's say I have a JSON with this data:

[ 
  { "id": "a",
    "x": 1,
    "y": 1  },

  { "id": "b",
    "x": 7,
    "y": 12 },
  .
  .
  .
  {etc.}
]

In Swift, I can load that into a struct:

struct jsonDataStruct: Codable, Identifiable {
    let id: String
    let x: Int
    let y: Int
}

But what if, I wanted to calculate the distance between x and y, for example? Normally, I've written a struct with an initializer, like this:

struct coordinates: Identifiable {
    
    let id: String
    let x: Int
    let y: Int
    let length: Int?

    init(id: String, x: Int, y: Int) {
        self.x = x
        self.y = y
        self.distance = sqrt(x * x + y * y)

        // do other calculations or processing
    }
    
}

And when I create an object:

let dudeWheresMyCar = coordinates(id: "my car", x: 12, y: 18)
print("it's \(dudeWheresMyCar.distance) meters away")

the distance was automatically calculated when the coordinates object was created.

How would I do that with the struct jsonDataStruct above? I've written an initializer for it, yet it doesn't seem to be called when data is loaded from a JSON:

Bundle-Decodable.swift:

import Foundation

extension Bundle {
    func decode(_ file: String) -> [jsonDataStruct] {
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle.")
        }
        
        guard let data = try? Data(contentsOf: url) else {
            fatalError("Failed to load \(file) from bundle.")
        }
        
        let decoder = JSONDecoder()
        
        guard let loaded = try? decoder.decode([jsonDataStruct].self, from: data) else {
            fatalError("Failed to decode \(file) from bundle.")
        }
        
        return loaded
    }
}

ContentView.Swift:

import SwiftUI

struct ContentView: View {
    
    let testJsonData = Bundle.main.decode("testData.json")
    
    var body: some View {
        Text("\(testJsonData.count)")
            .padding()
    }
}

I could simply compute the distance or any other value as needed during runtime, but having it pre-computed means it's ready to be used, and wouldn't have to write additional code.

I'm sure this basic question has been answered before, but being an amateur coder I'm most likely missing it because I probably don't know what to look for, or how to ask the question.

Thank you!

2
  • 3
    First, the property distance (or length - you have both) is better expressed as a computed property, var distance: Double { sqrt(x * x + y * y) } - this way you don't need to decode it at all. Alternatively, you'd need to write a custom init(from: Decoder) and compute distance there. Also, it's strongly recommended to follow the Swift convention of Capitalizing type names: struct JsonDataStruct { }, struct Coordinates { } Commented Jan 8, 2021 at 23:24
  • Thank you very much for your advice! There's still a lot left for me to learn Commented Jan 9, 2021 at 16:21

2 Answers 2

1

If you really wanted to keep it in the initializer you could override this initializer, which gets called during decoding:

init(from decoder: Decoder)

That said, why not just make this an instance method on the struct itself? There's no reason that you need to make this computation at initialization time. Something like this:

struct coordinates: Codable {
    let id: String
    let x: Int
    let y: Int
    let length: Int?

    func distance() -> Int {
        return sqrt(x * x + y * y)
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much! I considered making it a function, but what I was hoping to do is have the value pre-calculated, especially if it will be computationally expensive to do on the fly
BTW, init(from decoder: Decoder) helped! it pointed me in the correct direction and I was able to find examples. Thank you!
0

Try! this

struct coordinates: Codable {
    let id: String?
    let x: Int?
    let y: Int?

    var distance:Int {
        let X = x ?? 0
        let Y = y ?? 0
        return Int(sqrt(Double(X * X + Y * Y)))
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.