Search code examples
swiftavplayer

AVPlayer - Fatal error: Double value cannot be converted to Int because it is either infinite or NaN


I have this function:

func getSeconds()->Int{
        
        return Int(Double(self.value) * (self.player.currentItem?.duration.seconds)!)
    }

It sometimes crashes:

Fatal error: Double value cannot be converted to Int because it is either infinite or NaN

Of course I've seen this:

Swift 3:fatal error: Double value cannot be converted to Int because it is either infinite or NaN

But how EXACTLY do I have to use this?

What is this?:

guard !(totalSeconds.isNaN || totalSeconds.isInfinite) else {
    return "illegal value"
}

Where do I return totalSeconds if everything is fine?

EDIT:

I'm doing it like this now:

func getSeconds()->Int{
    
    let totalSeconds = self.player.currentItem?.duration.seconds
    
    if(totalSeconds != nil){
        guard !(totalSeconds!.isNaN || totalSeconds!.isInfinite) else {
            return 0 // or do some error handling
        }
        
        return Int(Double(self.value) * (self.player.currentItem?.duration.seconds)!)
    }else{
        return 0
    }
}

Does it make sense?


Solution

  • As mentioned in the answer to the question you linked to, the code is there to verify that the value is a valid number.

    Your edit is in the correct direction but it could use some improvements to the handling of optionals. Something like the following might be better:

    func getSeconds() -> Int {   
        guard let totalSeconds = self.player.currentItem?.duration.seconds,
            !totalSeconds.isNaN && !totalSeconds.isInfinite else { return 0 }
        
        return Int(Double(self.value) * totalSeconds)
    }
    

    The guard first gets the value of self.player.currentItem?.duration.seconds but only if there is a non-optional value. If self.player.currentItem is currently nil then there won't be a duration.

    Once the guard gets a value in seconds, it makes sure the value is neither NaN nor infinite.

    So the guard's else will be reached if there is no current item to get the duration for, or if the duration isn't a finite number.

    If you have a good number, the code can move on the calculation and return a final value.

    Note there is no use of force-unwraps in the code. You should avoid those to eliminate likely crashes. Learning proper optional handling in Swift is critical.

    As stated in a comment to the answer you referenced, the whole !(totalSeconds.isNaN || totalSeconds.isInfinite) can be written as totalSeconds.isFinite. Doing that in your function would change the guard to be:

    guard let totalSeconds = self.player.currentItem?.duration.seconds,
        totalSeconds.isFinite else { return 0 }
    

    This is a bit easier to read and more to the point of the needed check in this case.