Search code examples
iosswiftswiftuiavaudioplayer

Displaying only time from a Date object in Swift/SwiftUI


I've been working with displaying audio duration with ending time. One of the good Stack Overflow developers suggested this solution.

Hence, I've added this in my view:

struct ContentView: View {
    @ObservedObject var viewModel = MyViewModel() //<- here
    var body: some View {
        VStack {
            
            Text(viewModel.endTime) //<- here
            
        }
    }
}

This is the code in my viewModel:

class MyViewModel: ObservableObject {
    @Published var endDate: Date? 
    var endTime: String{
        endDate == nil ? "":endDate!.description 
    }

    func play(){
        let path = Bundle.main.path(forResource: "song", ofType:"mp3")!
        let url = URL(fileURLWithPath: path)
        do {
            player = try AVAudioPlayer(contentsOf: url)
            
            endDate = Date() + player.duration
            if player.isPlaying{
                
                player.pause()
                
            }
            else{
                player.play()
                
            }
            isPlaying = player.isPlaying
        }catch{print("error")}

But I'm getting the wrong time (not the system time) but with the correct date. I want to display only the correct time without date.


Solution

  • To display a formatted date, you'll probably want to use DateFormatter:

    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = .medium
    dateFormatter.timeStyle = .none
     
    let date = Date(timeIntervalSinceReferenceDate: 118800)
     
    // US English Locale (en_US)
    dateFormatter.locale = Locale(identifier: "en_US")
    print(dateFormatter.string(from: date)) // Jan 2, 2001
    

    Source: https://developer.apple.com/documentation/foundation/dateformatter

    (Note that creating a DateFormatter is known to be an expensive operation so be careful about creating them too often)

    You have a couple of logic errors in your view model as well. Here's an example of how to handle this:

    struct ContentView : View {
        @ObservedObject private var vm = MyViewModel()
        
        var body: some View {
            Button(action:{
                vm.play()
            } ) {
                Text(vm.isPlaying ? "Stop" : "Play")
            }
            Text(vm.endTime)
        }
    }
    
    class MyViewModel: ObservableObject {
        @Published var endDate: Date?
        @Published var isPlaying = false
        
        var player = AVAudioPlayer()
        var dateFormatter = DateFormatter()
        
        var endTime: String{
            guard let endDate = endDate else {
                return ""
            }
            dateFormatter.dateStyle = .none
            dateFormatter.timeStyle = .medium
            return dateFormatter.string(from: endDate)
        }
    
        func play(){
            do{
                if player.isPlaying {
                    player.pause()
                } else {
                    let path = Bundle.main.path(forResource: "All Of Me Final Mix", ofType:"mp3")!
                    let url = URL(fileURLWithPath: path)
                    player = try AVAudioPlayer(contentsOf: url)
                    endDate = Date() + player.duration - player.currentTime
                    player.play()
                }
                isPlaying = player.isPlaying
            } catch {
                print("error: \(error)")
            }
        }
    }