Search code examples
swiftswiftdata

How to use `CLLocationCoordinate2D` and `Color` data types in Swift Data?


I am converting from a [struct] to Swift Data. Two of my elements don't seem to work in Swift Data.

How do I make types Color and CLLocationCoordinate2D conform to PersistentModel and RelationshipCollection?

I am guessing this is either very easy or impossible. I don't know which, and I don't know how. Any help/pointers will be greatly appreciated‼️

Here's my code:

import Foundation
import SwiftData
import CoreLocation
import CoreMotion
import SwiftUI


@Model
final class Item {
   var timestamp: Date
   var daCount: Int
   var gpsTimestamp: Date
   var location: CLLocationCoordinate2D
   var latitude: Double
   var longitude: Double
   var myColor: Color
   
   init(timestamp: Date, daCount: Int, gpsTimestamp: Date, location: CLLocationCoordinate2D, latitude: Double, longitude: Double, myColor: Color) {
      self.timestamp = timestamp
      self.daCount = daCount
      self.gpsTimestamp = gpsTimestamp
      self.location = location
      self.latitude = latitude
      self.longitude = longitude
      self.myColor = myColor
   }
   
}

When I compile I get the following six errors:

No exact matches in call to instance method 'setValue'

  • In expansion of macro '_PersistedProperty' here
  • Candidate requires that 'Color' conform to 'PersistentModel' (requirement specified as 'Value' : 'PersistentModel')
  • Candidate requires that 'Color' conform to 'RelationshipCollection' (requirement specified as 'Value' : 'RelationshipCollection')

No exact matches in call to instance method 'setValue'

  • In expansion of macro '_PersistedProperty' here
  • Candidate requires that 'CLLocationCoordinate2D' conform to 'PersistentModel' (requirement specified as 'Value' : 'PersistentModel')
  • Candidate requires that 'CLLocationCoordinate2D' conform to 'RelationshipCollection' (requirement specified as 'Value' : 'RelationshipCollection')

No exact matches in call to instance method 'getValue'

  • In expansion of macro '_PersistedProperty' here
  • Candidate requires that 'Color' conform to 'PersistentModel' (requirement specified as 'Value' : 'PersistentModel')
  • Candidate requires that 'Color' conform to 'RelationshipCollection' (requirement specified as 'Value' : 'RelationshipCollection')

No exact matches in call to instance method 'getValue'

  • In expansion of macro '_PersistedProperty' here
  • Candidate requires that 'CLLocationCoordinate2D' conform to 'PersistentModel' (requirement specified as 'Value' : 'PersistentModel')
  • Candidate requires that 'CLLocationCoordinate2D' conform to 'RelationshipCollection' (requirement specified as 'Value' : 'RelationshipCollection')

No exact matches in call to instance method 'setValue'

  • In expansion of macro '_PersistedProperty' here
  • Candidate requires that 'Color' conform to 'PersistentModel' (requirement specified as 'Value' : 'PersistentModel')
  • Candidate requires that 'Color' conform to 'RelationshipCollection' (requirement specified as 'Value' : 'RelationshipCollection')

No exact matches in call to instance method 'setValue'

  • In expansion of macro '_PersistedProperty' here
  • Candidate requires that 'CLLocationCoordinate2D' conform to 'PersistentModel' (requirement specified as 'Value' : 'PersistentModel')
  • Candidate requires that 'CLLocationCoordinate2D' conform to 'RelationshipCollection' (requirement specified as 'Value' : 'RelationshipCollection')

Solution

  • One straightforward solution is to create custom types that conform to Codable that holds the underlying values

    struct Coordinate2D: Codable {
        let latitude: Double
        let longitude: Double
    
        init(latitude: Double, longitude: Double) {
            self.latitude = latitude
            self.longitude = longitude
        }
    }
    
    struct RGBValues: Codable {
        let red: Double
        let green: Double
        let blue: Double
        let opacity: Double
    
        init(red: Double, green: Double, blue: Double, opacity: Double = 1.0) {
            self.red = red
            self.green = green
            self.blue = blue
            self.opacity = opacity
        }
    }
    

    They can then be used in the model directly as property types

    @Model
    final class Place {
        var name: String
        var location: Coordinate2D
        var color: RGBValues
    
        init(name: String, location: Coordinate2D, color: RGBValues) {
            self.name = name
            self.location = location
            self.color = color
        }
    }
    

    You can then for convenience add ways to map between your custom types and the built in types

    extension Coordinate2D {
        init(_ location: CLLocationCoordinate2D) {
            self.latitude = location.latitude
            self.longitude = location.longitude
        }
    
        var location: CLLocationCoordinate2D {
            CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
        }
    }
    
    extension RGBValues {
        init(_ color: Color) {
            let resolvedColor = color.resolve(in: .init())
            red = Double(resolvedColor.red)
            green = Double(resolvedColor.green)
            blue = Double(resolvedColor.blue)
            opacity = Double(resolvedColor.opacity)
        }
        var color: Color {
            Color(red: red, green: green, blue: blue, opacity: opacity)
        }
    }