Я получаю пустой массив CLLocationCoordinates при загрузке данных из пользовательских настроек по умолчанию - PullRequest
0 голосов
/ 04 декабря 2018

Я пытаюсь сохранить в UserDefaults массив CCLocationCoordinates из отслеживающей части моего приложения в сочетании с именем отслеживаемого маршрута в качестве ключа, чтобы иметь возможность впоследствии вызвать его для использования в функции.Проблема в том, что, когда я вызываю эту функцию, я получаю индекс из-за ошибки диапазона.Я проверил и массив пуст.Поскольку я новичок в пользовательских настройках по умолчанию, я попытался увидеть другие подобные сообщения, но они все о NSUserDefaults и не нашли решения.

Вот код для функций для хранения и вызова массива:

func stopTracking2() {

        self.trackingIsActive = false
        self.trackigButton.backgroundColor = UIColor.yellow
        locationManager.stopUpdatingLocation()
        let stopRoutePosition = RouteAnnotation(title: "Route Stop", coordinate: (locationManager.location?.coordinate)!, imageName: "Route Stop")
        self.actualRouteInUseAnnotations.append(stopRoutePosition)



        print(actualRouteInUseCoordinatesArray)
        print(actualRouteInUseAnnotations)
        drawRoutePolyline()      // draw line to show route
//        checkAlerts2()           // check if there is any notified problem on our route and marks it with a blue circle, now called at programmed checking

        saveRouteToUserDefaults()
        postRouteToAnalitics() // store route anonymously to FIrebase

    }

    func saveRouteToUserDefaults() {

        // save actualRouteInUseCoordinatesArray : change for function
//        userDefaults.set(actualRouteInUseCoordinatesArray, forKey: "\(String(describing: userRoute))")

        storeCoordinates(actualRouteInUseCoordinatesArray)

    }

    // Store an array of CLLocationCoordinate2D
    func storeCoordinates(_ coordinates: [CLLocationCoordinate2D]) {
        let locations = coordinates.map { coordinate -> CLLocation in
            return CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
        }
        let archived = NSKeyedArchiver.archivedData(withRootObject: locations)
        userDefaults.set(archived, forKey: "\(String(describing: userRoute))")

        userDefaults.synchronize()
    }

    func loadRouteFromUserDefaults() {
        // gets entry from userRouteArray stored in userDefaults and append them into actualRouteInUseCoordinatesArray
        actualRouteInUseCoordinatesArray.removeAll()
        actualRouteInUseCoordinatesArray = userDefaults.object(forKey: "\(String(describing: userRoute))") as? [CLLocationCoordinate2D] ?? [CLLocationCoordinate2D]() // here we get the right set of coordinates for the route we are about to do the check on

        // load route coordinates from UserDefaults


//        actualRouteInUseCoordinatesArray = loadCoordinates()! //error found nil

    }


    // Return an array of CLLocationCoordinate2D
    func loadCoordinates() -> [CLLocationCoordinate2D]? {
        guard let archived = userDefaults.object(forKey: "\(String(describing: userRoute))") as? Data,
            let locations = NSKeyedUnarchiver.unarchiveObject(with: archived) as? [CLLocation] else {
                return nil
        }

        let coordinates = locations.map { location -> CLLocationCoordinate2D in
            return location.coordinate
        }

        return coordinates
    }

}



extension NewMapViewController {

    // ALERTS :

    func checkAlerts2() {

        loadRouteFromUserDefaults()        //load route coordinates to check in
        // CHECK IF ANY OBSTACLE IS OUN OUR ROUTE BY COMPARING DISTANCES

        while trackingCoordinatesArrayPosition != ( (actualRouteInUseCoordinatesArray.count) - 1) {
            print("checking is started")
            print(actualRouteInUseCoordinatesArray)
            let trackingLatitude = actualRouteInUseCoordinatesArray[trackingCoordinatesArrayPosition].latitude
            let trackingLongitude = actualRouteInUseCoordinatesArray[trackingCoordinatesArrayPosition].longitude
            let alertLatitude = alertNotificationCoordinatesArray[alertNotificationCoordinatesArrayPosition].latitude
            let alertLongitude = alertNotificationCoordinatesArray[alertNotificationCoordinatesArrayPosition].longitude

            let coordinateFrom = CLLocation(latitude: trackingLatitude, longitude: trackingLongitude)
            let coordinateTo = CLLocation(latitude: alertLatitude, longitude: alertLongitude)
            let coordinatesDistanceInMeters = coordinateFrom.distance(from: coordinateTo)

            // CHECK SENSITIVITY: sets the distance in meters for an alert to be considered an obstacle
            if coordinatesDistanceInMeters <= 10 {

                print( "found problem")
                routeObstacle.append(alertNotificationCoordinatesArray[alertNotificationCoordinatesArrayPosition]) // populate obstacles array
                trackingCoordinatesArrayPosition = ( trackingCoordinatesArrayPosition + 1)

            }
            else if alertNotificationCoordinatesArrayPosition < ((alertNotificationCoordinatesArray.count) - 1) {

                alertNotificationCoordinatesArrayPosition = alertNotificationCoordinatesArrayPosition + 1
            }
            else if  alertNotificationCoordinatesArrayPosition == (alertNotificationCoordinatesArray.count - 1) {

                trackingCoordinatesArrayPosition = ( trackingCoordinatesArrayPosition + 1)
                alertNotificationCoordinatesArrayPosition = 0
            }

        }

        findObstacles()

        NewMapViewController.checkCounter = 0


        displayObstacles()


    }

В расширении вы можете увидеть функцию, которая использует массив.

Сразу после печати массива я получаю индекс из-за ошибки диапазона.Как обычно, спасибо сообществу.

Ответы [ 3 ]

0 голосов
/ 05 декабря 2018

Попробовав различные решения, я решил переписать все это.Поэтому, найдя пост о том, как кодировать / декодировать мой массив в строку, я решил, что это путь.Это не должно быть тяжелым в системе, поскольку это строка, которая будет сохранена.Пожалуйста, дайте мне знать, что вы думаете об этом решении.Спасибо @Sh_Khan за указание на то, что это была проблема с расшифровкой, а @Moritz за указание на то, что я выполняю плохую практику.

Итак, код:

func storeRoute() {
    // first we code the CLLocationCoordinate2D array to string



    // second we store string into userDefaults

    userDefaults.set(encodeCoordinates(coords: actualRouteInUseCoordinatesArray), forKey: "\(String(describing: NewMapViewController.userRoute))")
}

func loadRoute() {

    //first se load string from user defaults
    let route = userDefaults.string(forKey: "\(String(describing: NewMapViewController.userRoute))")
    print("loaded route is \(route!))")

    //second we decode it into CLLocationCoordinate2D array
    actualRouteInUseCoordinatesArray = decodeCoordinates(encodedString: route!)
    print("decoded route array is \(actualRouteInUseCoordinatesArray))")

}

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
}
0 голосов
/ 05 декабря 2018

Вместо того, чтобы использовать тяжелый objectiv-c-ish NSKeyed(Un)Archiver и делать обход через CLLocation Я рекомендую расширить CLLocationCoordinate2D, чтобы принять Codable

extension CLLocationCoordinate2D : Codable {
    public init(from decoder: Decoder) throws {
        var arrayContainer = try decoder.unkeyedContainer()
        if arrayContainer.count == 2 {
            let lat = try arrayContainer.decode(CLLocationDegrees.self)
            let lng = try arrayContainer.decode(CLLocationDegrees.self)
            self.init(latitude: lat, longitude: lng)
        } else {
            throw DecodingError.dataCorruptedError(in: arrayContainer, debugDescription: "Coordinate array must contain two items")
        }
    }

    public func encode(to encoder: Encoder) throws {
        var arrayContainer = encoder.unkeyedContainer()
        try arrayContainer.encode(contentsOf: [latitude, longitude])
    }
}

и замените методы загрузки и сохранения данных на

func storeCoordinates(_ coordinates: [CLLocationCoordinate2D]) throws {
    let data = try JSONEncoder().encode(coordinates)
    UserDefaults.standard.set(data, forKey: String(describing: userRoute))
}

func loadCoordinates() -> [CLLocationCoordinate2D] {
    guard let data = UserDefaults.standard.data(forKey: String(describing: userRoute)) else { return [] }
    do {
        return try JSONDecoder().decode([CLLocationCoordinate2D].self, from: data)
    } catch {
        print(error)
        return []
    }
}

storeCoordinates throws он передает потенциальную ошибку кодирования

Загрузите данные с помощью

actualRouteInUseCoordinatesArray = loadCoordinates()

и сохраните его

do {
    try storeCoordinates(actualRouteInUseCoordinatesArray)
} catch { print(error) }
0 голосов
/ 04 декабря 2018

Ваша проблема в том, что вы сохраняете это как данные и пытаетесь читать напрямую без разархивирования. Вы можете попробовать

let locations =  [CLLocation(latitude: 123, longitude: 344),CLLocation(latitude: 123, longitude: 344),CLLocation(latitude: 123, longitude: 344)]

do {

    let archived = try NSKeyedArchiver.archivedData(withRootObject: locations, requiringSecureCoding: true)
    UserDefaults.standard.set(archived, forKey:"myKey")
    // read savely 
    if let data = UserDefaults.standard.data(forKey: "myKey") {
        let saved =  try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! [CLLocation]
        print(saved)
    }
}
catch {

    print(error)
}
...