0

I am developing an iOS app using Swift 3 and the GRDB SQLite library. The following code fragment causes a compile error of "value of type '[Row]' has no member 'next'"

let rows = try Row.fetchAll(db, sql, arguments:arguments)
while let row = try rows.next() {             <--- line with error
      ...
}

As far as I can tell I am following the example in the docs correctly. Can anyone tell me what I am doing wrong?

Mc.Stever
  • 782
  • 6
  • 16

1 Answers1

1

The fetchAll method returns a regular Swift array, that you iterate like all other arrays:

let rows = try Row.fetchAll(db, sql, arguments:arguments) // [Row]
for row in rows {
    // use row
}

The next method belongs to cursors, that you get with the fetchCursor method. Cursors are not arrays because they don't load all database results in one step. Instead, cursors are iterated row after row:

let rows = try Row.fetchCursor(db, sql, arguments:arguments) // DatabaseCursor<Row>
while let row = try rows.next() {
    // use row
}

You see that both arrays and cursors can iterate database results. How do you choose one or the other? Look at the differences:

  • Arrays contain copies of database values and may be consumed on any thread.
  • Arrays can can take a lot of memory, if the number of fetched results is high.
  • Arrays can be iterated many times.
  • Cursors iterate over database results in a lazy fashion, and don't consume much memory.
  • Cursors are faster because they go straight to SQLite and don't copy database values unless necessary.
  • Cursors can not be used on any thread.
  • Cursors can be iterated only one time.

Compare:

// On the main thread:
let (rowArray, rowCursor) = try dbQueue.inDatabase { db -> ([Row], DatabaseCursor<Row>) in
    let rowArray = try Row.fetchAll(db, "SELECT ...")
    let rowCursor = try Row.fetchCursor(db, "SELECT ...")

    // OK
    for row in rowArray { ... }
    while let row = try rowCursor.next() { ... }

    // Second iteration
    for row in rowArray { ... }                  // the same rows
    while let row = try rowCursor.next() { ... } // no result

    return (rowArray, rowCursor)
}

// OK: arrays can be consumed on any thread
for row in rowArray { ... }
DispatchQueue.global(.default).async {
    for row in rowArray { ... }
}

// DON'T DO THAT, NEVER
while let row = try rowCursor.next() { ... }
DispatchQueue.global(.default).async {
    while let row = try rowCursor.next() { ... }
}

If you don't see, or don't care about the difference, use arrays. If you care about memory and performance, use cursors when appropriate.

Link to the documentation: https://github.com/groue/GRDB.swift/blob/master/README.md#fetching-methods

Gwendal Roué
  • 3,949
  • 15
  • 34