 }
 
 
+// MARK: - FetchedRecordsObserver
+
+/// FetchedRecordsController adopts TransactionObserverType so that it can
+/// monitor changes to its fetched records.
+private final class FetchedRecordsObserver<Record: RowConvertible> : TransactionObserverType {
+    weak var controller: FetchedRecordsController<Record>?  // If nil, self is invalidated.
+    let observedTables: Set<String>
+    let isSameRecord: (Record, Record) -> Bool
+    var needsComputeChanges: Bool
+    var items: [Item<Record>]
+    let queue: dispatch_queue_t // protects items
+    
+    init(controller: FetchedRecordsController<Record>, initialItems: [Item<Record>], observedTables: Set<String>, isSameRecord: (Record, Record) -> Bool) {
+        self.controller = controller
+        self.items = initialItems
+        self.observedTables = observedTables
+        self.isSameRecord = isSameRecord
+        self.needsComputeChanges = false
+        self.queue = dispatch_queue_create("GRDB.FetchedRecordsObserver", DISPATCH_QUEUE_SERIAL)
+    }
+    
+    func invalidate() {
+        controller = nil
+    }
+    
+    /// Part of the TransactionObserverType protocol
+    func databaseDidChangeWithEvent(event: DatabaseEvent) {
+        if observedTables.contains(event.tableName) {
+            needsComputeChanges = true
+        }
+    }
+    
+    /// Part of the TransactionObserverType protocol
+    func databaseWillCommit() throws { }
+    
+    /// Part of the TransactionObserverType protocol
+    func databaseDidRollback(db: Database) {
+        needsComputeChanges = false
+    }
+    
+    /// Part of the TransactionObserverType protocol
+    func databaseDidCommit(db: Database) {
+        // The databaseDidCommit callback is called in the database writer
+        // dispatch queue, which is serialized: it is guaranteed to process the
+        // last database transaction.
+        
+        // Were observed tables modified?
+        guard needsComputeChanges else { return }
+        needsComputeChanges = false
+        
+        checkForChangesInDatabase(db)
+    }
+    
+    // Precondition: this method must be called from the database writer's
+    // serialized dispatch queue.
+    func checkForChangesInDatabase(db: Database) {
+        // Invalidated?
+        guard let controller = self.controller else { return }
+        
+        // Fetch items.
+        //
+        // This method is called from the database writer's serialized queue, so
+        // that we can fetch items before other writes have the opportunity to
+        // modify the database.
+        //
+        // However, we don't have to block the writer queue for all the duration
+        // of the fetch. We just need to block the writer queue until we can
+        // perform a fetch in isolation. This is the role of the readFromWrite
+        // method (see below).
+        //
+        // However, our fetch will last for an unknown duration. And since we
+        // release the writer queue early, the next database modification will
+        // triggers this callback while our fetch is, maybe, still running. This
+        // next callback will also perform its own fetch, that will maybe end
+        // before our own fetch.
+        //
+        // We have to make sure that our fetch is processed *before* the next
+        // fetch: let's immediately dispatch the processing task in our
+        // serialized FIFO queue, but have it wait for our fetch to complete,
+        // with a semaphore:
+        let semaphore = dispatch_semaphore_create(0)
+        var fetchedItems: [Item<Record>]! = nil
+        
+        controller.databaseWriter.readFromWrite { db in
+            let statement = try! controller.source.selectStatement(db)
+            fetchedItems = Item<Record>.fetchAll(statement)
+            
+            // Fetch is complete:
+            dispatch_semaphore_signal(semaphore)
+        }
+        
+        
+        // Process the fetched items
+        
+        dispatch_async(queue) {
+            // Wait for the fetch to complete:
+            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
+            assert(fetchedItems != nil)
+            
+            // Invalidated?
+            guard let controller = self.controller else { return }
+            
+            // Changes?
+            #if os(iOS)
+                let tableViewChanges: [TableViewChange<Record>]?
+                if controller.tableViewEventCallback != nil {
+                    // Compute table view changes
+                    let changes = self.computeTableViewChanges(from: self.items, to: fetchedItems)
+                    guard !changes.isEmpty else {
+                        return
+                    }
+                    tableViewChanges = changes
+                } else {
+                    // Look for a row difference
+                    guard fetchedItems.count != self.items.count || zip(fetchedItems, self.items).any({ (fetchedItem, item) in fetchedItem.row != item.row }) else {
+                        return
+                    }
+                    tableViewChanges = nil
+                }
+            #else
+                // Look for a row difference
+                guard fetchedItems.count != self.items.count || zip(fetchedItems, self.items).any({ (fetchedItem, item) in fetchedItem.row != item.row }) else {
+                    return
+                }
+            #endif
+            
+            // Ready for next check
+            self.items = fetchedItems
+            
+            dispatch_async(controller.queue) {
+                // Invalidated?
+                guard let controller = self.controller else { return }
+                
+                if controller.hasChangesCallbacks {
+                    controller.willChangeCallback?(controller)
+                    controller.fetchedItems = fetchedItems
+                    #if os(iOS)
+                        if let tableViewEventCallback = controller.tableViewEventCallback, let tableViewChanges = tableViewChanges {
+                            for change in tableViewChanges {
+                                tableViewEventCallback(controller: controller, record: change.record, event: change.event)
+                            }
+                        }
+                    #endif
+                    controller.didChangeCallback?(controller)
+                } else {
+                    controller.stopTrackingChanges()
+                }
+            }
+        }
+    }
+}
+
+
 // MARK: UITableView Support
 
 #if os(iOS)
-    extension FetchedRecordsObserver {
+    extension FetchedRecordsController {
         
         // MARK: - Accessing Records
         

            // We only support a single section
            return [FetchedRecordsSectionInfo(controller: self)]
        }
        
        
        // MARK: - Not Public
        
        private func computeTableViewChanges(from s: [Item<Record>], to t: [Item<Record>]) -> [TableViewChange<Record>] {
            let m = s.count

        }
        
        /// The array of records in the section.
        public var records: [Record] {
            // We only support a single section
            return controller.fetchedItems!.map { $0.record }
        }
    }
#endif


// MARK: - DatabaseSource
