iosswiftfirebasefirebase-realtime-databasecllocation

Convert String of CLLocationCoordinate2D(s) into array


Updating previous question for better explanation.

var breadcrumbs: [CLLocationCoordinate2D] = []
var path: [CLLocationCoordinate2D] = []

This is called every 10 seconds to append a CLLocationCoordinate2D to the array.

func addBreadcrumb(){
    let speed = (locationmanager.location?.speed)!
    let speedRounded = speed.roundTo(places: 4)
    let crumbCoordinate = locationmanager.location?.coordinate

    breadcrumbs.append(crumbCoordinate!)
    tripSpeeds.append(speedRounded)
}

Once the user is done with their trip they tap a button and the following function is called. This gets the data in to firebase. I am saving as Strings as I get an error if done otherwise.

func submitTripPath(){
    let tripID: String? = tripUID
    let tripPath = String(describing: breadcrumbs) //This may be the problem
    let speeds = String(describing: tripSpeeds)

    var ref: FIRDatabaseReference!
    ref = FIRDatabase.database().reference()
    let tripRef = ref.child("TripPaths").child(tripID!)
    let tripDictionary = ["routePath" : tripPath, "routeSpeed" : speeds] as [String : Any]
    tripRef.updateChildValues(tripDictionary) { (err, ref) in
        if err != nil {
            print(err!)
            return
        }
    }
}

In another screen I successfully pull out the String of Coordinates in a Firebase Database Reference.

let routePath = dict["routePath"] as! String //This may also be an issue
//output looks like this
"[__C.CLLocationCoordinate2D(latitude: 37.337728550000001, longitude: -122.02796406), __C.CLLocationCoordinate2D(latitude: 37.337716899999997, longitude: -122.02835139), __C.CLLocationCoordinate2D(latitude: 37.337694319999997, longitude: -122.0287719)]"

I want to be able to use this as an array from CLLocationCoordinate2D to draw a polyline using the following. I am having trouble converting this string to a usable array of CLLocationCoordinate2D.

    if (path.count > 1) {
        let sourceIndex = path.count - 1
        let destinationIndex = path.count - 2

        let c1 = path[sourceIndex]
        let c2 = path[destinationIndex]

        var a = [c1, c2]
        let polyline = MKPolyline(coordinates: &a, count: a.count)
        mapContainerView.add(polyline)
    }

If you have a suggestion on betting saving the original array, pulling it from Firebase, or converting the string please let me know. If you need other code for reference, please let me know.


Solution

  • So I think your biggest problem is going to be that you are using String(describing:). This is going to add those weird class names that you are seeing. You are better off coming up with your own encoding method for lat/long and then reverse encoding it to get back your location data. So something like the following would be a bette approach.

    func submitTripPath(){
        let tripID: String? = tripUID
        let tripPath = encodeCoordinates(coords: breadcrumbs)
        let speeds = String(describing: tripSpeeds)
    
        var ref: FIRDatabaseReference!
        ref = FIRDatabase.database().reference()
        let tripRef = ref.child("TripPaths").child(tripID!)
        let tripDictionary = ["routePath" : tripPath, "routeSpeed" : speeds] as [String : Any]
        tripRef.updateChildValues(tripDictionary) { (err, ref) in
            if err != nil {
                print(err!)
                return
            }
        }
    }
    
    func encodeCoordinates(coords: [CLLocationCoordinate2D]) -> String {
        let flattenedCoords: [String] = coords.map { coord -> String in "\(coord.latitude):\(coord.longitude)" }
        let encodedString: String = flattenedCoords.joined(separator: ",")
        return encodedString
    }
    
    func decodeCoordinates(encodedString: String) -> [CLLocationCoordinate2D] {
        let flattenedCoords: [String] = encodedString.components(separatedBy: ",")
        let coords: [CLLocationCoordinate2D] = flattenedCoords.map { coord -> CLLocationCoordinate2D in
            let split = coord.components(separatedBy: ":")
            if split.count == 2 {
                let latitude: Double = Double(split[0]) ?? 0
                let longitude: Double = Double(split[1]) ?? 0
                return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
            } else {
                return CLLocationCoordinate2D()
            }
        }
        return coords
    }
    

    I just used commas and colons for my encoding but you can easily use something else.