JSON parsing from a static JSON file into the label

Hello,

First of all, I dont know if I'm asking for too much.

I'm new in parsing API data in Swift. What I'm trying to do is to show in my two labels and UIImageView (or maybe there is other way to show album cover from external url) data from nowplaying api on my internet radio station.

Here how it is gonna look like in Main.storyboard:

So far I've tried couple of solutions, but I've stuck on parsing step. Here is my code:

override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view.

        overrideUserInterfaceStyle = .light

        setupRemoteCommandCenter()
//Radio API endpoint

        let urlString = "https://admin.radiopromil.online/api/nowplaying_static/radio_promil.json"

        let url = URL(string: urlString)

        

        guard url != nil else {

            return

        }

        let session = URLSession.shared

        

        let dataTask = session.dataTask(with: url!) { (data, response, error) in

            if error == nil && data != nil {

                let decoder = JSONDecoder()

                do {

                                            let radio = try decoder.decode(radioAPI.self, from: data!)

                                            print(radio)

                                            DispatchQueue.main.async {

                                                self.title.text = ????

                                                self.artist.text = ????

                                           }

                                }

                                        catch {

                                            print("Error Parsing JSON")

                                }

                        }

                        }

                        dataTask.resume()

            

        }

            }

So far I removed everything from radioAPI.swift. I

've got two API endpoint:

This one is static JSON https://admin.radiopromil.online/api/nowplaying_static/radio_promil.json

This one is standard API: https://admin.radiopromil.online/api/nowplaying/radio_promil

To sum up: I would like to parse data from API (now_playing: artist, title and cover art url) into two labels and UIImageView.

Data in API is changing when next song is playing.

Thank you in advance for your help.

What is your json format ?

If it is:

{
    "image": "some url",
    "title": "Masters of war",
    "artist": "Bob Dylan"
}

then, define a struct:

struct RadioAPI: Codable {    // Would be better named RadioAPI
    let image : URL
    let title     : String
    let artist   : String

    enum CodingKeys : String, CodingKey {
        case image = "image"
        case title    = "title"
        case artist  = "artist"
    }
}

CodingKeys are not necessary here, but it is a good practice.

And call:

 let radio = try? decoder.decode(radioAPI.self, from: data!)

 DispatchQueue.main.async {
     self.title.text = radio!.title
     self.artist.text = radio!.artist
 }

Note: I do not know how image is sent by JSON, I assumed a URL but may be something else.

Accepted Answer

First of all, it looks like you are using UIKit, not SwiftUI. Putting the right tags would help you getting the right solution sooner.


This one is standard API: https://admin.radiopromil.online/api/nowplaying/radio_promil

Seems the result of the API is a little bit long and complex:

The first thing you need to do is defining the right structs for the API result, but it seems to be difficult to do it manually.

There are some sites which generates Swift structs from JSON automatically. You should better search with json to swift and can easily find one.

From one of them, I could get the followings from the API result:

import Foundation

// MARK: - RadioAPI
struct RadioAPI: Codable {
    let station: Station
    let listeners: Listeners
    let live: Live
    let nowPlaying: NowPlaying
    let playingNext: PlayingNext
    let songHistory: [NowPlaying]
    let isOnline: Bool
    let cache: String

    enum CodingKeys: String, CodingKey {
        case station, listeners, live
        case nowPlaying = "now_playing"
        case playingNext = "playing_next"
        case songHistory = "song_history"
        case isOnline = "is_online"
        case cache
    }
}

// MARK: - Listeners
struct Listeners: Codable {
    let total, unique, current: Int
}

// MARK: - Live
struct Live: Codable {
    let isLive: Bool
    let streamerName: String
    //let broadcastStart: JSONNull? // Modified from generated code

    enum CodingKeys: String, CodingKey {
        case isLive = "is_live"
        case streamerName = "streamer_name"
        //case broadcastStart = "broadcast_start"
    }
}

// MARK: - NowPlaying
struct NowPlaying: Codable {
    let elapsed, remaining: Int?
    let shID, playedAt, duration: Int
    let playlist, streamer: String
    let isRequest: Bool
    let song: Song

    enum CodingKeys: String, CodingKey {
        case elapsed, remaining
        case shID = "sh_id"
        case playedAt = "played_at"
        case duration, playlist, streamer
        case isRequest = "is_request"
        case song
    }
}

// MARK: - Song
struct Song: Codable {
    let id, text, artist, title: String
    let album, genre, lyrics: String
    let art: String
    //let customFields: [JSONAny] //Modified

    enum CodingKeys: String, CodingKey {
        case id, text, artist, title, album, genre, lyrics, art
        //case customFields = "custom_fields" //Modified
    }
}

// MARK: - PlayingNext
struct PlayingNext: Codable {
    let cuedAt, duration: Int
    let playlist: String
    let isRequest: Bool
    let song: Song

    enum CodingKeys: String, CodingKey {
        case cuedAt = "cued_at"
        case duration, playlist
        case isRequest = "is_request"
        case song
    }
}

// MARK: - Station
struct Station: Codable {
    let id: Int
    let name, shortcode, stationDescription, frontend: String
    let backend: String
    let listenURL: String
    let url: String
    let publicPlayerURL: String
    let playlistPlsURL: String
    let playlistM3UURL: String
    let isPublic: Bool
    let mounts: [Mount]
    //let remotes: [JSONAny] //Modified

    enum CodingKeys: String, CodingKey {
        case id, name, shortcode
        case stationDescription = "description"
        case frontend, backend
        case listenURL = "listen_url"
        case url
        case publicPlayerURL = "public_player_url"
        case playlistPlsURL = "playlist_pls_url"
        case playlistM3UURL = "playlist_m3u_url"
        case isPublic = "is_public"
        case mounts//, remotes //Modified
    }
}

// MARK: - Mount
struct Mount: Codable {
    let path: String
    let isDefault: Bool
    let id: Int
    let name: String
    let url: String
    let bitrate: Int
    let format: String
    let listeners: Listeners

    enum CodingKeys: String, CodingKey {
        case path
        case isDefault = "is_default"
        case id, name, url, bitrate, format, listeners
    }
}

(From https://app.quicktype.io, some parts commented out to ignore unspecified results.)

You can use the structs like this:

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        overrideUserInterfaceStyle = .light
        setupRemoteCommandCenter()
        //Radio API endpoint
        let urlString = "https://admin.radiopromil.online/api/nowplaying/radio_promil"
        let url = URL(string: urlString)!
        let session = URLSession.shared
        let dataTask = session.dataTask(with: url) { data, response, error in
            if let error = error {
                print(error)
                return
            }
            guard let data = data else {
                print("data is nil")
                return
            }
            let decoder = JSONDecoder()
            do {
                let radio = try decoder.decode(RadioAPI.self, from: data)
                print(radio)
                DispatchQueue.main.async {
                    self.titleLabel.text = radio.nowPlaying.song.title
                    self.artistLabel.text = radio.nowPlaying.song.artist
                    if let artUrl = URL(string: radio.nowPlaying.song.art) {
                        //TODO: You need to load an image from `artUrl`,
                        //but that's another issue...
                    }
                }
            } catch {
                print("Error Parsing JSON: \(error)")
            }
        }
        dataTask.resume()
    }
JSON parsing from a static JSON file into the label
 
 
Q