Reputation: 244
I have a 2D generic array, with about 50 of these things:
[string, string, string, string, double, double]
The values are all in the same order, and I have a custom class:
class tramStop {
var tramDirection: String?
var stopCode: String?
var nameEnglish: String?
var nameChinese: String?
var latitude: Double?
var longitude: Double?
init(direction: String, code: String, nameEn: String, nameCn: String, lat: Double, lon: Double) {
tramDirection = direction
stopCode = code
nameEnglish = nameEn
nameChinese = nameCn
latitude = lat
longitude = lon
}
}
I would like to store the array as an array of custom objects in a file. I can't seem to figure out how to do this without creating something that appends an empty array every time. I feel like there's an easy (and proper) way to do this, but I can't figure it out.
Eventually what I would like to do, is take the device location, and find the nearest latitude and longitude object, and retrieve strings from it.
Upvotes: 0
Views: 1508
Reputation: 236360
First of all it is Swift convention to name your classes and structures starting with an uppercase letter. I recommend using a struct instead of a class. Start declaring all your properties constants and add a custom initializer that takes your array of Any type objects (IMO you should be using a dictionary as a source). Add a guard statement to make sure the array count is equal to 6 and properly initialize each of your struct properties. To allow persistence you can add a computed property to create a dictionary with its own info and another property to convert your json object to data:
struct TramStop {
let tramDirection: String
let stopCode: String
let nameEnglish: String
let nameChinese: String
let latitude: Double
let longitude: Double
init?(array: [Any]) {
guard array.count == 6 else { return nil }
tramDirection = array[0] as? String ?? ""
stopCode = array[1] as? String ?? ""
nameEnglish = array[2] as? String ?? ""
nameChinese = array[3] as? String ?? ""
latitude = array[4] as? Double ?? 0
longitude = array[5] as? Double ?? 0
}
init(_ dictionary: [String: Any]) {
tramDirection = dictionary["tramDirection"] as? String ?? ""
stopCode = dictionary["stopCode"] as? String ?? ""
nameEnglish = dictionary["nameEnglish"] as? String ?? ""
nameChinese = dictionary["nameChinese"] as? String ?? ""
latitude = dictionary["latitude"] as? Double ?? 0
longitude = dictionary["longitude"] as? Double ?? 0
}
var dictionary: [String: Any] {
return [ "tramDirection" : tramDirection,
"stopCode" : stopCode,
"nameEnglish" : nameEnglish,
"nameChinese" : nameChinese,
"latitude" : latitude,
"longitude" : longitude
]
}
var data: Data { return (try? JSONSerialization.data(withJSONObject: dictionary)) ?? Data() }
var json: String { return String(data: data, encoding: .utf8) ?? String() }
}
And create a struct to hold your tramStops array and the proper initializers (array2D and jsonData):
struct Stops {
let tramStops: [TramStop]
init(_ array2D: [[Any]]) {
tramStops = array2D.flatMap(TramStop.init)
}
init?(_ data: Data) {
guard let array = (try? JSONSerialization.jsonObject(with: data)) as? [[String: Any]]
else { return nil }
tramStops = array.map(TramStop.init)
}
var array: [[String: Any]] {
return tramStops.map{ $0.dictionary }
}
var data: Data {
return (try? JSONSerialization.data(withJSONObject: array)) ?? Data()
}
var json: String {
return String(data: data, encoding: .utf8) ?? String()
}
}
Playground testing
let array2D = [["a1", "a2", "a3", "a4", 1.0, 2.0],
["b1", "b2", "b3", "b4", 3.0, 4.0]]
let stops = Stops(array2D)
print(stops.json) // "[{"nameEnglish":"a3","latitude":1,"tramDirection":"a1","longitude":2,"stopCode":"a2","nameChinese":"a4"},{"nameEnglish":"b3","latitude":3,"tramDirection":"b1","longitude":4,"stopCode":"b2","nameChinese":"b4"}]"
let jsonURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("TramStop.json")
stops.data // 209 bytes
for tramStop in stops.tramStops {
print(tramStop.dictionary)
print(tramStop.stopCode)
print(tramStop.nameEnglish)
print(tramStop.nameChinese)
print(tramStop.latitude)
print(tramStop.longitude)
}
do {
try stops.data.write(to: jsonURL)
print("json data saved")
} catch {
print(error)
}
Reading json data from fileURL:
do {
let data = try Data(contentsOf: jsonURL)
print(String(data: data, encoding: .utf8) ?? "")
if let stops = Stops(data) {
for tramStop in stops.tramStops {
print(tramStop.tramDirection)
print(tramStop.stopCode)
print(tramStop.nameEnglish)
print(tramStop.nameChinese)
print(tramStop.latitude)
print(tramStop.longitude)
}
}
} catch {
print(error)
}
Upvotes: 1
Reputation: 383
You need to loop through the array, taking each set of 6 elements and initializing a tramStop
to put in a new tramStop
array. Or, better, initialize tramStops in the first place, without creating the interim array.
There are several ways to save to a file, depending on what you want to do with it. NSCoding might be applicable, and it's fairly easy to make your class conform to it. Check out this article.
EDIT: Here's how you can create the new tramStop array:
var newArray: [tramStop] = []
for i in stride(from: 0, to: oldArray.count, by: 6) {
newArray.append(tramStop(
direction: oldArray[i] as! String,
code: oldArray[i + 1] as! String,
nameEn: oldArray[i + 2] as! String,
nameCn: oldArray[i + 3] as! String,
lat: oldArray[i + 4] as! Double,
lon: oldArray[i + 5] as! Double
))
}
But, as I said, I'd look into how to eliminate oldArray
altogether.
Also, I didn't know you wanted a text file when I suggested NSCoding, which won't give you a text file.
Upvotes: 0