I have this custom protocol, named DMPoint.
protocol DMPoint {
// MARK: - Properties
var x: Double { get set }
var y: Double { get set }
// MARK: - Lifecycle
init(x: Double, y: Double)
}
I create an extension of the CGPoint which implements the DMPoint. By analysing the documentation for CGPoint, CGPoint should conform to DMPoint without any further implementation.
extension CGPoint: DMPoint { }
If I write this piece of code, I get the following error: Type 'CGPoint' does not conform to protocol 'DMPoint'.
I have also used CGFloat for the data type used in DMPoint. It works for the CGPoint extension, but I also want to use this protocol for another classes that use Double instead of CGFloat, in which case those will also throw the same error. As of Swift 5.5, the compiler should be able to use to automatically convert CGFloat to Double and vice-versa.
I am using Swift 5.7.2. Is there any other way of making CGPoint conform to DMPoint other than using CGFloat instead of Double inside the DMPoint protocol?
CodePudding user response:
Is this what you need to achieve? :
protocol DMPoint {
// MARK: - Properties
var x: Double { get set }
var y: Double { get set }
// MARK: - Lifecycle
init(x: Double, y: Double)
}
extension DMPoint {
var x: Double {
get {
return x
}
set {
x = newValue
}
}
var y: Double {
get {
return y
}
set {
y = newValue
}
}
}
extension CGPoint: DMPoint { }
var point = CGPoint(x: 1.0, y: 2.0)
print(point.x) // x value is Double
print(point.y) // y value is Double
CodePudding user response:
In CoreFoundation, CGPoint uses Double (which is probably what you are seeing in the documentation), but in UIKit, for example, they are CGFloat.
Depending upon your use case, you might consider defining DMPoint to have an associatedtype, e.g.:
protocol DMPoint {
associatedtype FloatType
// MARK: - Properties
var x: FloatType { get set }
var y: FloatType { get set }
// MARK: - Lifecycle
init(x: FloatType, y: FloatType)
}
extension CGPoint: DMPoint { }
And if you need to have properties that always return Double, you can write an extension that defines distinct computed properties that return the Double values, but adding some constraint on the associated type, e.g.:
protocol DMPoint where FloatType: BinaryFloatingPoint {
associatedtype FloatType
// MARK: - Properties
var x: FloatType { get set }
var y: FloatType { get set }
// MARK: - Lifecycle
init(x: FloatType, y: FloatType)
}
extension CGPoint: DMPoint { }
And because of the magic of SE-0307, you can do things like:
let x: Double = point.x
Or, I guess you could write a protocol extension with default implementations to do this for you:
extension DMPoint {
var doubleX: Double {
get { Double(x) }
set { x = FloatType(newValue) }
}
var doubleY: Double {
get { Double(x) }
set { x = FloatType(newValue) }
}
func xy() -> (Double, Double) {
(doubleX, doubleY)
}
}
And then you could do things like:
let x = point.doubleX
Or:
let (x, y) = point.xy()
The other alternative is to make DMPoint a concrete type, rather than a protocol, and, for example, give it a cgPoint computed property to bridge to CGPoint, e.g.:
struct DMPoint: Codable {
// MARK: - Properties
var x: Double
var y: Double
// MARK: - Lifecycle
init(x: Double, y: Double) {
self.x = x
self.y = y
}
init(cgPoint: CGPoint) {
x = Double(cgPoint.x)
y = Double(cgPoint.y)
}
var cgPoint: CGPoint {
get { CGPoint(x: x, y: y) }
set { x = Double(newValue.x); y = Double(newValue.y) }
}
}
extension CGPoint {
var dmPoint: DMPoint {
DMPoint(cgPoint: self)
}
}
