Fetch data from API every 5 seconds

Hello,

I try to fetch data from my API every 5 seconds. I did this code, but it does not work. Any ideas what I'm doing wrong?


            let urlString = "https://radio.app/api/nowplaying/radio_x"

            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.asyncAfter(deadline: .now() + 5, execute: { 
//this is the line that I've added DispatchQueue.main.asyncDispatchQueue.main.asyncAfter insted of standard 

                        self.songtitle.text = radio.nowPlaying.song.title

                        self.artist.text = radio.nowPlaying.song.artist

                    

                        

                        

                //albumcover art section

                if let artUrl = URL(string: radio.nowPlaying.song.art) {

                //I need to load an image from `artUrl`

                    let imageDatatask = session.dataTask(with: artUrl) { imageData, imageResponse, imageError in

                        if let error = error {

                            print(error)

                            return

                        }

                        guard let imageData = imageData else {

                            print("image_data is nil")

                            return

                        }

                        

                        //let albumArt = UIImage(data: imageData)

                        DispatchQueue.main.async {

                        let albumArt = UIImage(data: imageData)

                        let albumView = UIImageView(image: albumArt)

                        }

                    }

                    imageDatatask.resume()

                    }

                })

                    

                    }

                    

                 catch {

                    print("Error Parsing JSON: \(error)")

                }

            }

            dataTask.resume()

        }

    
Answered by OOPer in 688091022

Thanks for showing your code. Generally, easily triable code would help involving more readers. Very few people would dive into the external links, even though they are safe enough as GitHub.

And have you tried Paste and Match Style as suggested by Claude31? It will help preventing extra empty lines added.


Seems you use asyncAfter in the wrong place. You need to trigger fetching the next nowPlaying after the current nowPlaying is processed.

Please try something like this:

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        overrideUserInterfaceStyle = .light
        setupRemoteCommandCenter()
        requestNowPlaying()
    }
    
    private var songArtUrl: URL? = nil
    private func requestNowPlaying() {
        //Radio API endpoint title and artist labels
        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.songtitle.text = radio.nowPlaying.song.title
                    self.artist.text = radio.nowPlaying.song.artist
                    //albumcover art section
                    if let artUrl = URL(string: radio.nowPlaying.song.art),
                       artUrl != self.songArtUrl {
                        //I need to load an image from `artUrl`
                        let imageDatatask = session.dataTask(with: artUrl) { imageData, imageResponse, imageError in
                            if let imageError = imageError {
                                print(imageError)
                                return
                            }
                            guard let imageData = imageData else {
                                print("image_data is nil")
                                return
                            }
                            DispatchQueue.main.async {
                                self.songArtUrl = artUrl
                                let albumArt = UIImage(data: imageData)
                                //↓ Assuming you connected `artUrl` to the right UIImageView
                                //Generally, naming `...Url` for a property of type `UIImageView` may be confusing...
                                self.artUrl.image = albumArt
                            }
                        }
                        imageDatatask.resume()
                    }
                    let intervalToNextRequest = radio.nowPlaying.remaining ?? 5 // Re-consider if this is appropriate
                    print("intervalToNextRequest=\(intervalToNextRequest)")
                    DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(intervalToNextRequest)) {
                        print("Next request triggered...")
                        self.requestNowPlaying()
                    }
                }
            } catch {
                print("Error Parsing JSON: \(error)")
            }
        }
        dataTask.resume()
    }

Showing partial code would not help solving your issue. Please show complete code, at least the full code of the method, preferably full code of the class. By the way every 5 seconds may be too much. Your app may be called a battery eater or a packet eater.

" it does not work. " does not tell anything. What do you get ?

When you paste code please use Paste and Match Style to avoid all the extra lines that make code hard to grasp.

let urlString = "https://radio.app/api/nowplaying/radio_x"

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.asyncAfter(deadline: .now() + 5, execute: {
              //this is the line that I've added DispatchQueue.main.asyncDispatchQueue.main.asyncAfter insted of standard
              self.songtitle.text = radio.nowPlaying.song.title
              self.artist.text = radio.nowPlaying.song.artist
              
              //albumcover art section
              
              if let artUrl = URL(string: radio.nowPlaying.song.art) {
                   //I need to load an image from `artUrl`
                   let imageDatatask = session.dataTask(with: artUrl) { imageData, imageResponse, imageError in
                        
                        if let error = error {
                             print(error)
                             return
                        }
                        
                        guard let imageData = imageData else {
                             print("image_data is nil")
                             return
                        }
                        
                        //let albumArt = UIImage(data: imageData)
                        
                        DispatchQueue.main.async {
                             let albumArt = UIImage(data: imageData)
                             let albumView = UIImageView(image: albumArt)
                        }
                        
                   }
                   imageDatatask.resume()
              }
              
         })

        }

     catch {
        print("Error Parsing JSON: \(error)")
    }

}

dataTask.resume()

}

How many songs are there in a nowPlaying session ? As you want to show an album every 5 seconds, why don't you fetch them all, including image, keep in some array and then start displaying every 5 seconds. Getting the dispatch out of the datatask.

In your code, it seems you delay starting, but you do not loop over songs every 5 seconds.

Also, you create a new UIImageView

                             let albumView = UIImageView(image: albumArt)

Why ? Isn't there an IBOutlet you have to feed ?

@Claude31 thanks for the tip with the style. It is the radio stream. Now, I've got nowPlaying data only once when the app is starting. I would like to change it and fetch data when the next song is played. My API is here in static JSON: https://admin.radiopromil.online/api/nowplaying_static/radio_promil.json and here the general one: https://admin.radiopromil.online/api/nowplaying/radio_promil - this one Im using in let urlString. Album arts are URL's and I would like to fetch them as an Image and show with song title and artists, there are more than 5000 songs, and the database kepps growing, so the array is not the solution for me. @OOPer You can find full code here: https://github.com/pawelzet/promil_test/blob/main/ViewController.swift . On the top you can see IBOutlets, coneccted to two labels and UIImageView object. The app is building, but the titles in labels does not change and I cant see album art in this UIImageView. Alson in the API there is a part duration: which show how many seconds the song have. Maybe thats the way I can solve fetching new data? Adding this duration of each song parameter + 15 more seconds for the jingle (which is playing between songs and it is not shown in the API). Thank you in advance for your help.

You could show your code as Code Block.

@OOPer here is the full code

import UIKit

import AVKit

import MediaPlayer



class ViewController: UIViewController, AVAudioPlayerDelegate {

    

    var player : AVPlayer!

    var dict = NSDictionary()

    var isPlaying = false

    let playImage = UIImage(named: "play.png")

    let pauseImage = UIImage(named: "pause.png")

    

    @IBOutlet weak var artist: UILabel!

    @IBOutlet weak var songtitle: UILabel!

    @IBOutlet weak var artUrl: UIImageView!

    

    @IBAction func buttonPressed(_ sender: UIButton){

              if isPlaying {

                   player.pause()

                   sender.setImage(playImage, for: .normal)

              } else {

                   let url = "https://admin.radiopromil.online/radio/8000/radio.mp3"

                   do {

                        try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers, .allowAirPlay])

                        try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: [])

                        print("Playback OK")

                        try AVAudioSession.sharedInstance().setActive(true)

                        print("Session is Active")

                   } catch {

                        print(error)

                   }

                   

                   player = AVPlayer(url: URL(string: url)!)

                   player.volume = 1.0

                   player.rate = 1.0

                   player.play()

                sender.setImage(pauseImage, for: .normal)

              }

              

              isPlaying.toggle()

         }

    



    

    override func viewDidLoad() {

            super.viewDidLoad()

            // Do any additional setup after loading the view.

            overrideUserInterfaceStyle = .light

            setupRemoteCommandCenter()

            //Radio API endpoint title and artist labels

            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.asyncAfter(deadline: .now() + 5, execute: {

                        self.songtitle.text = radio.nowPlaying.song.title

                        self.artist.text = radio.nowPlaying.song.artist

                    

                        

                        

                //albumcover art section

                if let artUrl = URL(string: radio.nowPlaying.song.art) {

                //I need to load an image from `artUrl`

                    let imageDatatask = session.dataTask(with: artUrl) { imageData, imageResponse, imageError in

                        if let imageError = imageError {

                            print(imageError)

                            return

                        }

                        guard let imageData = imageData else {

                            print("image_data is nil")

                            return

                        }

                        

                        //let albumArt = UIImage(data: imageData)

                        DispatchQueue.main.async {

                        let albumArt = UIImage(data: imageData)

                        let albumView = UIImageView(image: albumArt)

                        }

                    }

                    imageDatatask.resume()

                    }

                })

                    

                    }

                    

                 catch {

                    print("Error Parsing JSON: \(error)")

                }

            }

            dataTask.resume()

        }

    

    

    

    

    

    

    

    func setupRemoteCommandCenter() {

        // Get the shared MPRemoteCommandCenter

        MPNowPlayingInfoCenter.default().nowPlayingInfo = [MPMediaItemPropertyTitle: "Radio Promil"]

        let commandCenter = MPRemoteCommandCenter.shared()

        commandCenter.playCommand.isEnabled = true

            commandCenter.pauseCommand.isEnabled = true

            commandCenter.playCommand.addTarget { [weak self] (event) -> MPRemoteCommandHandlerStatus in

                self?.player.play()

                return .success

            }

            commandCenter.pauseCommand.addTarget { [weak self] (event) -> MPRemoteCommandHandlerStatus in

                self?.player.pause()

                return .success

            }

        }

            

    

    

}

@OOPer here is the full code:

import UIKit

import AVKit

import MediaPlayer



class ViewController: UIViewController, AVAudioPlayerDelegate {

    

    var player : AVPlayer!

    var dict = NSDictionary()

    var isPlaying = false

    let playImage = UIImage(named: "play.png")

    let pauseImage = UIImage(named: "pause.png")

    

    @IBOutlet weak var artist: UILabel!

    @IBOutlet weak var songtitle: UILabel!

    @IBOutlet weak var artUrl: UIImageView!

    

    @IBAction func buttonPressed(_ sender: UIButton){

              if isPlaying {

                   player.pause()

                   sender.setImage(playImage, for: .normal)

              } else {

                   let url = "https://admin.radiopromil.online/radio/8000/radio.mp3"

                   do {

                        try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers, .allowAirPlay])

                        try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: [])

                        print("Playback OK")

                        try AVAudioSession.sharedInstance().setActive(true)

                        print("Session is Active")

                   } catch {

                        print(error)

                   }

                   

                   player = AVPlayer(url: URL(string: url)!)

                   player.volume = 1.0

                   player.rate = 1.0

                   player.play()

                sender.setImage(pauseImage, for: .normal)

              }

              

              isPlaying.toggle()

         }

    



    

    override func viewDidLoad() {

            super.viewDidLoad()

            // Do any additional setup after loading the view.

            overrideUserInterfaceStyle = .light

            setupRemoteCommandCenter()

            //Radio API endpoint title and artist labels

            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.asyncAfter(deadline: .now() + 5, execute: {

                        self.songtitle.text = radio.nowPlaying.song.title

                        self.artist.text = radio.nowPlaying.song.artist

                    

                        

                        

                //albumcover art section

                if let artUrl = URL(string: radio.nowPlaying.song.art) {

                //I need to load an image from `artUrl`

                    let imageDatatask = session.dataTask(with: artUrl) { imageData, imageResponse, imageError in

                        if let imageError = imageError {

                            print(imageError)

                            return

                        }

                        guard let imageData = imageData else {

                            print("image_data is nil")

                            return

                        }

                        

                        //let albumArt = UIImage(data: imageData)

                        DispatchQueue.main.async {

                        let albumArt = UIImage(data: imageData)

                        let albumView = UIImageView(image: albumArt)

                        }

                    }

                    imageDatatask.resume()

                    }

                })

                    

                    }

                    

                 catch {

                    print("Error Parsing JSON: \(error)")

                }

            }

            dataTask.resume()

        }

    

    

    

    

    

    

    

    func setupRemoteCommandCenter() {

        // Get the shared MPRemoteCommandCenter

        MPNowPlayingInfoCenter.default().nowPlayingInfo = [MPMediaItemPropertyTitle: "Radio Promil"]

        let commandCenter = MPRemoteCommandCenter.shared()

        commandCenter.playCommand.isEnabled = true

            commandCenter.pauseCommand.isEnabled = true

            commandCenter.playCommand.addTarget { [weak self] (event) -> MPRemoteCommandHandlerStatus in

                self?.player.play()

                return .success

            }

            commandCenter.pauseCommand.addTarget { [weak self] (event) -> MPRemoteCommandHandlerStatus in

                self?.player.pause()

                return .success

            }

        }

            

    

    

}
Accepted Answer

Thanks for showing your code. Generally, easily triable code would help involving more readers. Very few people would dive into the external links, even though they are safe enough as GitHub.

And have you tried Paste and Match Style as suggested by Claude31? It will help preventing extra empty lines added.


Seems you use asyncAfter in the wrong place. You need to trigger fetching the next nowPlaying after the current nowPlaying is processed.

Please try something like this:

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        overrideUserInterfaceStyle = .light
        setupRemoteCommandCenter()
        requestNowPlaying()
    }
    
    private var songArtUrl: URL? = nil
    private func requestNowPlaying() {
        //Radio API endpoint title and artist labels
        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.songtitle.text = radio.nowPlaying.song.title
                    self.artist.text = radio.nowPlaying.song.artist
                    //albumcover art section
                    if let artUrl = URL(string: radio.nowPlaying.song.art),
                       artUrl != self.songArtUrl {
                        //I need to load an image from `artUrl`
                        let imageDatatask = session.dataTask(with: artUrl) { imageData, imageResponse, imageError in
                            if let imageError = imageError {
                                print(imageError)
                                return
                            }
                            guard let imageData = imageData else {
                                print("image_data is nil")
                                return
                            }
                            DispatchQueue.main.async {
                                self.songArtUrl = artUrl
                                let albumArt = UIImage(data: imageData)
                                //↓ Assuming you connected `artUrl` to the right UIImageView
                                //Generally, naming `...Url` for a property of type `UIImageView` may be confusing...
                                self.artUrl.image = albumArt
                            }
                        }
                        imageDatatask.resume()
                    }
                    let intervalToNextRequest = radio.nowPlaying.remaining ?? 5 // Re-consider if this is appropriate
                    print("intervalToNextRequest=\(intervalToNextRequest)")
                    DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(intervalToNextRequest)) {
                        print("Next request triggered...")
                        self.requestNowPlaying()
                    }
                }
            } catch {
                print("Error Parsing JSON: \(error)")
            }
        }
        dataTask.resume()
    }
Fetch data from API every 5 seconds
 
 
Q