Learn Courses My Dashboard

How can I go to the next songCell in SwiftUI

I have a text field that shows you what song is next Text("Next up is \(song.name)")
And it works but it’s just that it shows the name of the song that you’re currently in not the next one. As you can see in this image

How can I make it show the next song instead of the one it’s in?

That depends on what code you have to display the name of the song. Essentially you need to add 1 to the current array index value so that it will pick out the next song name.

(edit) That needs to be done with care so that you do not have an “index out of range” error.

I don’t know what you mean could you explain it in further detail please?

I tried to do this but it doesn’t work

        ZStack {

                        Blur(style: .dark)
                        .frame(width: 300, height: 30)
                        .cornerRadius(50)
                    
                    Text("Next up is \(song.name)")
                    song = album.songs[currentIndex + 1]
                        .foregroundColor(.white)
                }

@Air_Walks

No you can’t have logic inside a View like that.

I needed to see what code you have so that I can make sense of what needs to be done in order to achieve what you want. You’ve given me enough clues in the last post to come up with something that may work for you.

I’m assuming that you have a currentIndex that is keeping track of what song you are playing and the code extracts the song to play from album.songs[currentindex]

With that in mind, what you will have to do is create a function that returns a String that you can use in the Text element.

For example:

    func nextSong() -> String {
        if currentIndex < album.songs.count - 1 {
            // return the next song name
            return album.songs[currentIndex + 1].name
        } else {
            // return the first song name
            return album.songs[0].name
        }
    }

So your Text element can now be:

Text("Next up is \(nextSong())")

let me know if that works for you.

(edit) The function should be added before or after the body property in the same View that your Text element is in.

I done that and it works but only for the next one (so only for 2) , since there are 3 if I try to go to the 3rd one the app crashes and says ‘fatal error: Index out of range’

Hmmm, create a test project and replace ContentView with this basic code and you can see that it works fine.

struct ContentView: View {
    let songs = ["Song1", "Song2", "Song3", "Song4"]
    @State private var currentIndex = 0

    var body: some View {
        VStack(spacing: 30) {
            Text("Current song is: \(songs[currentIndex])")

            Text("Next up is: \(nextSong())")

            Button {
                if currentIndex < songs.count - 1 {
                    currentIndex += 1
                } else {
                    currentIndex = 0
                }
            } label: {
                Text("Next")
            }

        }
    }

    func nextSong() -> String {
        if currentIndex < songs.count - 1 {
            // return the next song name
            return songs[currentIndex + 1]
        } else {
            // return the first song name
            return songs[0]
        }
    }
}

You may be incrementing currentIndex beyond the array index range. You need to check the index value the same way that I am doing above.

Hopefully you can adapt the code to suit your project.

It shows this as an error now.

Is songs an array of type [Song] or something?

Is this what you need?

    func nextSong() -> String {
        if currentIndex < album.songs.count - 1 {
            // return the next song name
            return album.songs[currentIndex + 1].name
        } else {
            // return the first song name
            return album.songs[0].name
        }
    }

I tried that but now It doesn’t change it only works the first time then it’s the same for all
That image is good it works

But after that for this one it shouldn’t shwo this

It’s very hard to know what is going on without having access to your code. I’m only able to make wild guesses.

Sorry about that here is the code for the PlayerView

//
//  PlayerView.swift
//  MusicStreaming App
//
//  Created by Pavi Singh on 23/9/2022.
//

import Foundation
import SwiftUI
import Firebase
import AVFoundation

struct PlayerView : View {
    @State var album : Album
    @State var song : Song
    @State var player = AVPlayer()
    @State var isPlaying : Bool = false
    @State private var currentIndex = 0
    
    var body: some View {
        ZStack {
            Image(song.songImage)
                .resizable()
                .ignoresSafeArea(.all)
            Blur(style: .dark)
                .ignoresSafeArea(.all)
            VStack {
                Spacer()
                songImage(album: album, song: song, isWithText: false)
                Text(song.name)
                    .font(.title)
                    .fontWeight(.light)
                    .foregroundColor(.white)
                Spacer()
                
                ZStack {

                        Blur(style: .dark)
                        .frame(width: 300, height: 30)
                        .cornerRadius(50)
                    
                    Text("Next up is \(nextSong())")
                        .foregroundColor(.white)
                }
                
                ZStack {
                    Color.white.cornerRadius(20).shadow(radius: 10)
                        
                        HStack {
                                Button {
                                    self.previous()
                                } label: {
                                    Image(systemName: "arrow.left.circle").resizable()
                                }.frame(width: 50, height: 50, alignment: .center).foregroundColor(Color.black).opacity(0.2)
                                
                                Button {
                                    self.playPause()
                                } label: {
                                    Image(systemName: isPlaying ? "pause.circle.fill" : "play.circle.fill").resizable()
                                }.frame(width: 70, height: 70, alignment: .center).padding()
                                
                                Button {
                                    self.next()
                                } label: {
                                    Image(systemName: "arrow.right.circle").resizable()
                                }.frame(width: 50, height: 50, alignment: .center).foregroundColor(Color.black).opacity(0.2)
       
                        }
                }
                .edgesIgnoringSafeArea(.bottom)
                .frame(height: 200, alignment: .center)
            }

        }
    }
    
    func nextSong() -> String {
           if currentIndex < album.songs.count - 1 {
               // return the next song name
               return album.songs[currentIndex + 1].name
           } else {
               // return the first song name
               return album.songs[0].name
           }
       }
    
    func playSong() {
        let storage = Storage.storage().reference(forURL: self.song.file)
        storage.downloadURL { (url, error) in
            if error != nil {
                print(error)
            } else {
                
                do {
                    try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
                } catch {
                    // report for an error
                }
                print(url?.absoluteString)
                player = AVPlayer(playerItem: AVPlayerItem(url: url!))
                player.play()
            }
        }
    }
    
    
    func playPause() {
        self.isPlaying.toggle()
        if isPlaying == false {
            player.pause()
        } else {
            player.play()
        }
    }
    
    
    func next() {
        if let currentIndex = album.songs.firstIndex(of: song) {
            if currentIndex == album.songs.count - 1 {
                
            } else {
                player.pause()
                song = album.songs[currentIndex + 1]
                self.playSong()
            }
        }
    }
    
    func previous() {
        if let currentIndex = album.songs.firstIndex(of: song) {
            if currentIndex == 0 {
                
            } else {
                player.pause()
                song = album.songs[currentIndex - 1]
                self.playSong()
            }
        }
    }
    
    
}

@Air_Walks

Unfortunately that does not help a heap since there are a lot of references that can’t be resolved that are external to the PlayerView.

I’m guessing that PlayerView is being called from a parent View so do you pass in an Album that you are trying to play or what exactly are you doing?

It would also appear that your Song struct is different to what I was helping you with back on July 7 (not that I should be surprised by that).

What would help then?

And yes my Song struct is different because I decided to completely change the app and make it with Firebase.

And it’s better than last time because it actually works this time, I’m just adding some nice quality of life improvements and stuff that I can learn from.

Yeah that’s understandable that you have completely changed the App and added Firebase.

What would help?
The struct definitions for Album and Song which would enable me to check the logic associated with advancing the song to the next one. Though the issue there is having to create some dummy data to work with since I don’t have access to the underlying data you are relying on.

@Air_Walks

The alternative is having access to your codebase either from GItHub (if you have it on there) or compress the project into a zip file and post a share link from either Dropbox or Google Drive here in a reply or you can private message me with the share link.

@Air_Walks

Can you change the share link type to “anyone with the Link” and, if you prefer, post that share link in a private message to me rather than here so that I am the only one who has access.