从用户默认值加载数据时,我得到一个空的 CLLocationCoordinates 数组
Posted
技术标签:
【中文标题】从用户默认值加载数据时,我得到一个空的 CLLocationCoordinates 数组【英文标题】:I get an empty CLLocationCoordinates array when loading data from user defaults 【发布时间】:2018-12-04 14:05:13 【问题描述】:我正在尝试将来自我的应用程序跟踪部分的 CCLocationCoordinates 数组与作为键的跟踪路线的名称配对存储到 UserDefaults,以便以后能够调用它以在函数中使用它。 问题是当我调用该函数时,我得到索引超出范围错误。我检查了一下,数组是空的。 由于我是用户默认设置的新手,因此我尝试查看其他类似的帖子,但它们都是关于 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()
在扩展中你可以看到使用数组的函数。
在数组打印之后,我得到了索引超出范围错误。 一如既往地感谢社区。p>
【问题讨论】:
【参考方案1】:在尝试了提供的各种解决方案后,我决定重写整个内容。 因此,在找到一篇关于如何将我的数组编码/解码为字符串的帖子后,我决定这是要走的路。它不应该在系统上很重,因为它是一个被保存的字符串。请让我知道您对此解决方案的看法。 感谢@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
【讨论】:
【参考方案2】:与其使用重量级的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)
【讨论】:
感谢您的解决方案。不过我想我还是太新手,无法完全理解它,但我会更改代码,因为我没有处理任何可能的错误。【参考方案3】:你的问题是你把它保存为数据并尝试直接读取而不存档,你可以试试
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)
【讨论】:
我不确定如何实施您的解决方案。我用你的代码替换了我的代码的哪一部分?func storeCoordinates()
没有正确归档?然后是func loadCoordinates()
?以上是关于从用户默认值加载数据时,我得到一个空的 CLLocationCoordinates 数组的主要内容,如果未能解决你的问题,请参考以下文章