On Xcode 14, Apple added Hashable conformance to CMTime
and CMTimeRange
to iOS16 only. I'm trying to make it Hashable for all iOS versions, because we have many Hashable structs that contain CMTime
, and they depend on CMTime
to be Hashable as well.
Until now, we had an extension on CMTime
that make it conform to Hashable, but on Xcode 14 this extension causes compilation error with this description:
Protocol 'Hashable' requires 'hashValue' to be available in iOS 14.0.0 and newer
If I implement hashValue
like this:
public var hashValue: Int {
var hasher = Hasher()
hash(into: &hasher)
return hasher.finalize()
}
It compile and works, but I'm not sure if it's safe because hashValue
is deprecated so I'm not sure I understand why it's needed (only hash(into:)
should be implemented for Hashable conformance these days).
Can anyone shed some light about whether this solution is safe, or have any other solution?
Another idea that I tried:
I added this extension on CMTime
:
extension CMTime {
struct Hashed: Hashable {
private let time: CMTime
init(time: CMTime) {
self.time = time
}
public func hash(into hasher: inout Hasher) {
// pre iOS16 hash computation goes here.
}
}
func asHashable() -> Hashed {
return Hashed(time: self)
}
}
Then I changed all Hashable structs that contain CMTime
from this:
struct Foo: Hashable {
let time: CMTime
let string: String
}
to this:
struct Foo: Hashable {
let time: CMTime
let string: String
func hash(into hasher: inout Hasher) {
if #available(iOS 16, *) {
hasher.combine(self.time)
} else {
hasher.combine(self.time.asHashable())
}
hasher.combine(self.string)
}
}
I'm not a fan of this since it will make a LOT of changes across the code
CodePudding user response:
EDIT 2:
The code below works with iOS 16 simulator, but crashes with iOS 15 and lower. Odd, since it compiles.
My suggestion is to implement an extension and make it available only for iOS 15 and lower:
@available(iOS, obsoleted: 16)
extension CMTime: Hashable {
public var hashValue: Int {
var hasher = Hasher()
hash(into: &hasher)
return hasher.finalize()
}
public func hash(into hasher: inout Hasher) {
hasher.combine(value)
hasher.combine(timescale)
// more if needed
}
}
As for why hashValue
is still needed, I agree it's not suppose to be there. Might be an XCode false alarm, the error is really confusing.
Notice Apple doesn't say when it was deprecated, and also it was deprecated as a requirement, it's still there behind the scenes, I'm guessing: https://developer.apple.com/documentation/swift/hashable/hashvalue
EDIT 1:
CMTime
already conforms to Hashable
. Tested with iOS 13, 14, 15 and 16. I don't think you need your extension.
import Foundation
import AVFoundation
struct Foo: Hashable {
let aaa: String
let bbb: Int
let time: CMTime
}
func testMyFoo() {
let foo1 = Foo(
aaa: "yo",
bbb: 123,
time: CMTime(
value: 100,
timescale: 1
)
)
let foo2 = Foo(
aaa: "yo",
bbb: 123,
time: CMTime(
value: 100,
timescale: 1
)
)
var myFoos = Set<Foo>()
myFoos.insert(foo1)
myFoos.insert(foo2)
print(myFoos)
// you will only have 1 foo in the Set, because they are the same.
// if you change the CMTime values in foo1, you will have 2 items in the Set.
}