Home > Software design >  CGPoint does not conform to custom protocol in Swift
CGPoint does not conform to custom protocol in Swift

Time:01-17

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