patternswiftMinor
Sqlite.swift -- Selecting all rows and returning them in an array of tuples
Viewed 0 times
swiftrowsallarraysqlitereturningselectingandthemtuples
Problem
Link to SQLite wrapper
Grabbing all the rows, and returning them as a tuple for later use.
I've read the docs but I cannot think of a more efficient way to return all the rows to a TableView
EDIT: GIST
func queryAll() -> [(String, String, String)] {
do {
let sql = try DB?.prepare("SELECT city, zip , temp FROM weather")
var arr : [(city : String, zip : String, temp : String)] = []
for row in sql! {
arr += [(city: row[0] as! String, zip: row[1] as! String, temp: row[2] as! String)]
}
return arr;
}
catch {
print("\(error)")
}
return [("","","")]
}Grabbing all the rows, and returning them as a tuple for later use.
I've read the docs but I cannot think of a more efficient way to return all the rows to a TableView
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : CustomTableViewController = self.tableView.dequeueReusableCell(withIdentifier: "Cell")! as! CustomTableViewController
let DB : WeatherDataStorage = WeatherDataStorage()
var arr = [(city : String, zip : String, temp : String)]()
arr = DB.queryAll()
cell.cityLabel.text = arr[i].city
cell.zipLabel.text = arr[i].zip
cell.temperatureLabel.text = arr[i].temp
i += 1
return cell
}EDIT: GIST
Solution
-
Don't use tuples like this. The purpose of tuples is to make simple, short lived collections of values. For a use case like this, it is preferable to use a
-
Don't return non-sensical default values as a way of error handling.
-
Narrow your
-
Avoid force casting if you can. Handle your errors sensibly.
-
Currently, you manually create an empty array, and repeatedly add to it in a for loop. This is discouraged for several reasons:
-
-
It's a lot of boiler-plate code.
-
You can incur a lot of reallocation overhead, because you didn't call
Using
-
Improve your naming.
-
Don't use the pattern of:
-
Change your
-
"weather" is an awkward name for a table. Try something like "WeatherReading"
Here's how I would write this code:
Don't use tuples like this. The purpose of tuples is to make simple, short lived collections of values. For a use case like this, it is preferable to use a
struct or a class to contain your results. There are many disadvantages.-
Don't return non-sensical default values as a way of error handling.
"" isn't a city, and "" isn't a valid zip code, so why try to claim them as such? This is what optionals are for, use them.-
Narrow your
do block to contain only the function calls that throw.-
Avoid force casting if you can. Handle your errors sensibly.
-
Currently, you manually create an empty array, and repeatedly add to it in a for loop. This is discouraged for several reasons:
-
arr must be mutable.-
It's a lot of boiler-plate code.
-
You can incur a lot of reallocation overhead, because you didn't call
reserveCapacity on your array.Using
map is preferable instead.-
Improve your naming.
sql is a result set, it's not an sql code snippet, nor is it an sql connection. Name it so.-
Don't use the pattern of:
array += [newElement]. This technique causes an unnecessary array allocation. Instead, just use array.append(newElement).-
Change your
DB variable to not be an Optional. If there is something like a connection issue that causes DB to be nil, this should be handled at the time that you attempt to open the connection. Leaving it as an Optional, and using optional chaining DB?.doSomething() is a terrible idea. It makes the nil case fail silently, leading to very annoying to catch bugs.-
"weather" is an awkward name for a table. Try something like "WeatherReading"
Here's how I would write this code:
struct WeatherReading {
let city: String
let zipCode: String
let temperature: String
}
func queryAllWeatherReadings() -> [WeatherReading]? {
guard let queryResults = try? DB.prepare("SELECT city, zip, temperature FROM WeatherReading") else {
debugPrint(error)
return nil
}
let weatherReadings = queryResults.map { row in
guard let city = row[0] as? String,
let zipCode = row[1] as? String,
let temperature = row[2] as? String else {
fatalError("There was a type error in the received result set: \(row)")
}
return WeatherReading(city: city, zipCode: zipCode, temperature: temperature)
}
return weatherReadings
}Code Snippets
struct WeatherReading {
let city: String
let zipCode: String
let temperature: String
}
func queryAllWeatherReadings() -> [WeatherReading]? {
guard let queryResults = try? DB.prepare("SELECT city, zip, temperature FROM WeatherReading") else {
debugPrint(error)
return nil
}
let weatherReadings = queryResults.map { row in
guard let city = row[0] as? String,
let zipCode = row[1] as? String,
let temperature = row[2] as? String else {
fatalError("There was a type error in the received result set: \(row)")
}
return WeatherReading(city: city, zipCode: zipCode, temperature: temperature)
}
return weatherReadings
}Context
StackExchange Code Review Q#148963, answer score: 8
Revisions (0)
No revisions yet.