What does "% is unavailable: Use truncatingRemainder instead" mean?
Solution 1:
CMTimeGetSeconds()
returns a floating point number (Float64
aka
Double
). In Swift 2 you could compute the
remainder of a floating point division as
let rem = 2.5 % 1.1
print(rem) // 0.3
In Swift 3 this is done with
let rem = 2.5.truncatingRemainder(dividingBy: 1.1)
print(rem) // 0.3
Applied to your code:
let totalSeconds = CMTimeGetSeconds(self)
let hours = Int(totalSeconds / 3600)
let minutes = Int((totalSeconds.truncatingRemainder(dividingBy: 3600)) / 60)
let seconds = Int(totalSeconds.truncatingRemainder(dividingBy: 60))
However, in this particular case it is easier to convert the duration to an integer in the first place:
let totalSeconds = Int(CMTimeGetSeconds(self)) // Truncate to integer
// Or:
let totalSeconds = lrint(CMTimeGetSeconds(self)) // Round to nearest integer
Then the next lines simplify to
let hours = totalSeconds / 3600
let minutes = (totalSeconds % 3600) / 60
let seconds = totalSeconds % 60
Solution 2:
The %
modulus operator is defined only for integer types. For floating-point types, you need to be more specific about the kind of IEEE 754 division/remainder behavior you want, so you have to call a method: either remainder
or truncatingRemainder
. (If you're doing floating-point math you actually need to care about this, and lots of other stuff, or you can get unexpected / bad results.)
If you actually intend to do integer modulus, you need to convert the return value of CMTimeGetSeconds
to an integer before using %
. (Note that if you do, you'll lop off the fractional seconds... depending on where you're using CMTime
that may be important. Do you want minutes:seconds:frames, for example?)
Depending on how you want to present CMTime
values in your UI, it might be better to extract the seconds value and pass it to NSDateFormatter
or NSDateComponentsFormatter
so you get appropriate locale support.
Solution 3:
Bring back the simple modulo syntax in swift 3:
This syntax was actually suggested on Apples official swift mailing list here but for some reason they opted for a less elegant syntax.
infix operator %%/*<--infix operator is required for custom infix char combos*/
/**
* Brings back simple modulo syntax (was removed in swift 3)
* Calculates the remainder of expression1 divided by expression2
* The sign of the modulo result matches the sign of the dividend (the first number). For example, -4 % 3 and -4 % -3 both evaluate to -1
* EXAMPLE:
* print(12 %% 5) // 2
* print(4.3 %% 2.1) // 0.0999999999999996
* print(4 %% 4) // 0
* NOTE: The first print returns 2, rather than 12/5 or 2.4, because the modulo (%) operator returns only the remainder. The second trace returns 0.0999999999999996 instead of the expected 0.1 because of the limitations of floating-point accuracy in binary computing.
* NOTE: Int's can still use single %
* NOTE: there is also .remainder which supports returning negatives as oppose to truncatingRemainder (aka the old %) which returns only positive.
*/
public func %% (left:CGFloat, right:CGFloat) -> CGFloat {
return left.truncatingRemainder(dividingBy: right)
}
This simple swift 3 migration tip is part of a more comprehensive swift 3 migration guide with many insights (35k loc / 8-days of migration) http://eon.codes/blog/2017/01/12/swift-3-migration/
Solution 4:
There's no need to create a separate modulo operator for floating point numbers, unless you think it makes the code safer. You can overload the %
operator to accept floating point numbers like so:
func %<N: BinaryFloatingPoint>(lhs: N, rhs: N) -> N {
lhs.truncatingRemainder(dividingBy: rhs)
}
Usage
let a: Float80 = 10
let b: Float80 = 3
print(a % b)
You can now use %
with any two floating point numbers of the same tye.