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

JSON Parsing

Forums > Swift

Sorry for the huge amount of code, but I went through one of Paul's tutorials where he used dataTaskPublisher. I don't understand how to translate to Example 2. Paul's example has one json record, the other has 10. Anyways, I appreciate any assistance anyone can provide.

Paul Example:

import UIKit
import SwiftUI
import Combine
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.setLiveView(ContentView())

struct User: Decodable {
    var id: UUID
    var name: String
    var age: Int

    static let `default` = User(id: UUID(), name: "Anonymous", age: 0)
}

struct ContentView: View {
    @State private var requests = Set<AnyCancellable>()

    var users = [User]()

    var body: some View {
        Button(action: {
            if let url = URL(string: "https://www.hackingwithswift.com/samples/user-24601.json") {
                self.fetch(url, defaultValue: User.default) {
                    print($0.name)
                    print($0.age)
                }
            }
        }, label: {
            Text("Fetch Data")
                .padding()
                .background(Color.red)
                .foregroundColor(.white)
                .cornerRadius(20)
        })

    }
    func fetch<T: Decodable>(_ url: URL, defaultValue: T, completion: @escaping (T) -> Void) {
        let decoder = JSONDecoder()

        URLSession.shared.dataTaskPublisher(for: url)
            .retry(1)
            .map(\.data)
            .decode(type: T.self, decoder: decoder)
            .replaceError(with: defaultValue)
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: completion)
            .store(in: &requests)
    }
}

Example 2

import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

struct Geo: Codable {
//    let lat: String
//    let lng: String
    let latitude: String
    let longitude: String

    private enum CodingKeys: String, CodingKey {
        case latitude = "lat"
        case longitude = "lng"
    }
}

struct Address: Codable {
    let street: String
    let city: String
    let zipcode: String
    let geo: Geo
}

struct User: Codable {
    let name: String
    let username: String
    let email: String
    let address: Address
}

var users = [User]()

let urlString = "https://jsonplaceholder.typicode.com/users"
if let url = URL(string: urlString) {
    if let data = try? Data(contentsOf: url) {
        // If we get here we are ok to parse json
        parse(json: data)
    }
}

func parse(json: Data) {
    let decoder = JSONDecoder()
    if let jsonUsers = try? decoder.decode([User].self, from: json) {
        users = jsonUsers
    }
}

print(users[0].name)
print(users[0].address.city)
//print(users[0].address.geo.lat)
print(users[0].address.geo.latitude)
print("#########################################")

users.forEach { (user) in
    print("Name: \(user.name)")
    print("Email: \(user.email)")
    print("City: \(user.address.city)")
    //print("Latitude: \(user.address.geo.lat) | Longitude: \(user.address.geo.lng)")
    print("Latitude: \(user.address.geo.latitude) | Longitude: \(user.address.geo.longitude)")
    print("#########################################")
}

3      

Using SwiftUI I have come up with something like this for example 2 In this example however I have created a class called 'NetworkCall' hence the ObservableObject and self.networkcall.fetch changes

ContentView:

import SwiftUI
import Combine

struct User: Decodable, Identifiable {
    struct AddressDetail: Decodable {
        var street: String
        var city: String
    }

    var id: Int
    var name: String
    var username: String
    var email: String
    var address: AddressDetail
}

struct ContentView: View {
    @ObservedObject var networkcall = NetworkCall()
    @State private var users = [User]()

    var body: some View {
        NavigationView {
            List(users) { user in
                VStack(alignment: .leading) {
                    Text(user.name)
                        .font(.title)
                    Text("Email: \(user.email)")
                    Text("Street: \(user.address.street)")
                    Text("City: \(user.address.city)")
                }
            }
            .navigationBarTitle("Users")
        }
        .onAppear {
            let url = URL(string: "https://jsonplaceholder.typicode.com/users")!

            self.networkcall.fetch(url, defaultValue: [User]()) {
                self.users = $0
            }
        }
    }
}

Networking

import Foundation
import Combine

class NetworkCall: ObservableObject {
    @Published var requests = Set<AnyCancellable>()

    func fetch<T: Decodable>(_ url: URL, defaultValue: T, completion: @escaping (T) -> Void) {

        let decoder = JSONDecoder()

        URLSession.shared.dataTaskPublisher(for: url)
            .retry(1)
            .map(\.data)
            .decode(type: T.self, decoder: decoder)
            .replaceError(with: defaultValue)
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: completion)
            .store(in: &requests)
    }
}

3      

I got the same but using Paul's format.

import Combine
import SwiftUI

struct User: Decodable {

    struct Address: Decodable {

        struct Geo: Decodable {
            var lat: String
            var lng: String
        }

        var street: String
        var suite: String
        var city: String
        var zipcode: String
        var geo: Geo
    }

    struct Company: Decodable {
        var name: String
        var catchPhrase: String
        var bs: String
    }

    var id: Int
    var name: String
    var username: String
    var email: String
    var address: Address
    var phone: String
    var website: String
    var company: Company

    static let `default`:[User] = [User(id: 0, name: "Anonymous", username: "anon", email: "anon@anon.com", address: Address(street: "street", suite: "suite", city: "city", zipcode: "zip", geo: Address.Geo(lat: "37.000", lng: "37.000")), phone: "1111-1111", website: "apple.com", company: Company(name: "Anon", catchPhrase: "Anon", bs: "anon"))]
}

struct ContentView: View {
    @State private var requests = Set<AnyCancellable>()
    @State var users = [User]()

    var body: some View {
        List {
            ForEach(users, id: \.id) { user in
                Text(user.name)
                    .font(.title)
                Text(user.address.geo.lat)
            }
        }
        Button(action: {
            let url = URL(string: "https://jsonplaceholder.typicode.com/users")!
            fetch(url, defaultValue: User.default) {
                self.users = $0
            }
        }, label: {
            Text("Fetch Data")
                .padding()
                .background(Color.red)
                .foregroundColor(.white)
                .cornerRadius(20)
        })
    }

    func fetch<T: Decodable>(_ url: URL, defaultValue: T, completion: @escaping (T) -> Void) {
        let decoder = JSONDecoder()

        URLSession.shared.dataTaskPublisher(for: url)
            .retry(1)
            .map(\.data)
            .decode(type: T.self, decoder: decoder)
            .replaceError(with: defaultValue)
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: completion)
            .store(in: &requests)
    }
}

3      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.