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)
}
}