Skip to content

Commit

Permalink
support for fine-grained notifications in Realm.
Browse files Browse the repository at this point in the history
  • Loading branch information
DenTelezhkin committed May 22, 2016
1 parent 0302610 commit 93be14f
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 35 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Change Log
All notable changes to this project will be documented in this file.

## Master

### Changed

* Support for fine-grained notifications in Realm
* Update to Realm 0.103.1 and higher.

## [2.5.1](https://github.com/DenHeadless/DTModelStorage/releases/tag/2.5.1)

## Changed
Expand Down
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "realm/realm-cocoa" ~> 0.102
github "realm/realm-cocoa" ~> 0.103
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "realm/realm-cocoa" "v0.102.0"
github "realm/realm-cocoa" "v0.103.1"
2 changes: 1 addition & 1 deletion Carthage/Checkouts/Nimble
Submodule Nimble updated 61 files
+1 −1 .travis.yml
+3 −0 CONTRIBUTING.md
+3 −1 Nimble.podspec
+146 −102 Nimble.xcodeproj/project.pbxproj
+20 −3 README.md
+0 −0 Sources/Nimble/Adapters/NonObjectiveC/ExceptionCapture.swift
+0 −9 Sources/Nimble/Adapters/ObjectiveC/CurrentTestCaseTracker.h
+0 −0 Sources/Nimble/Adapters/ObjectiveC/DSL.h
+0 −0 Sources/Nimble/Adapters/ObjectiveC/DSL.m
+0 −0 Sources/Nimble/Adapters/ObjectiveC/NMBExceptionCapture.h
+0 −0 Sources/Nimble/Adapters/ObjectiveC/NMBExceptionCapture.m
+0 −0 Sources/Nimble/Adapters/ObjectiveC/NMBExpectation.swift
+0 −0 Sources/Nimble/Adapters/ObjectiveC/NMBObjCMatcher.swift
+18 −0 Sources/Nimble/Adapters/ObjectiveC/NMBStringify.h
+6 −0 Sources/Nimble/Adapters/ObjectiveC/NMBStringify.m
+78 −0 Sources/Nimble/Adapters/ObjectiveC/XCTestObservationCenter+Register.m
+6 −6 Sources/Nimble/DSL+Wait.swift
+7 −7 Sources/Nimble/DSL.swift
+0 −0 Sources/Nimble/Matchers/AsyncMatcherWrapper.swift
+1 −5 Sources/Nimble/Matchers/BeCloseTo.swift
+1 −1 Sources/Nimble/Matchers/BeEmpty.swift
+6 −12 Sources/Nimble/Matchers/BeLogical.swift
+2 −2 Sources/Nimble/Matchers/HaveCount.swift
+26 −0 Sources/Nimble/Matchers/MatchError.swift
+0 −26 Sources/Nimble/Matchers/MatcherFunc.swift
+5 −19 Sources/Nimble/Matchers/MatcherProtocols.swift
+0 −4 Sources/Nimble/Matchers/SatisfyAnyOf.swift
+0 −128 Sources/Nimble/Matchers/ThrowError.swift
+1 −0 Sources/Nimble/Nimble.h
+2 −2 Sources/Nimble/Utils/Async.swift
+133 −0 Sources/Nimble/Utils/Errors.swift
+129 −38 Sources/Nimble/Utils/Stringers.swift
+3 −3 Tests/Nimble/AsynchronousTest.swift
+3 −3 Tests/Nimble/Helpers/utils.swift
+12 −17 Tests/Nimble/Matchers/BeCloseToTest.swift
+17 −0 Tests/Nimble/Matchers/BeEmptyTest.swift
+1 −7 Tests/Nimble/Matchers/BeGreaterThanTest.swift
+1 −7 Tests/Nimble/Matchers/BeLessThanTest.swift
+4 −4 Tests/Nimble/Matchers/ContainTest.swift
+4 −4 Tests/Nimble/Matchers/HaveCountTest.swift
+78 −0 Tests/Nimble/Matchers/MatchErrorTest.swift
+1 −1 Tests/Nimble/Matchers/PostNotificationTest.swift
+1 −1 Tests/Nimble/Matchers/SatisfyAnyOfTest.swift
+6 −6 Tests/Nimble/objc/ObjCAllPassTest.m
+4 −4 Tests/Nimble/objc/ObjCBeCloseToTest.m
+10 −2 Tests/Nimble/objc/ObjCBeEmptyTest.m
+2 −2 Tests/Nimble/objc/ObjCBeFalsyTest.m
+4 −4 Tests/Nimble/objc/ObjCBeGreaterThanOrEqualToTest.m
+4 −4 Tests/Nimble/objc/ObjCBeGreaterThanTest.m
+4 −4 Tests/Nimble/objc/ObjCBeLessThanOrEqualToTest.m
+4 −4 Tests/Nimble/objc/ObjCBeLessThanTest.m
+1 −1 Tests/Nimble/objc/ObjCBeNilTest.m
+1 −1 Tests/Nimble/objc/ObjCBeTrueTest.m
+2 −2 Tests/Nimble/objc/ObjCBeTruthyTest.m
+6 −6 Tests/Nimble/objc/ObjCContainTest.m
+2 −2 Tests/Nimble/objc/ObjCEqualTest.m
+17 −2 Tests/Nimble/objc/ObjCHaveCount.m
+3 −3 Tests/Nimble/objc/ObjCSatisfyAnyOfTest.m
+6 −6 Tests/Nimble/objc/ObjCUserDescriptionTest.m
+31 −0 Tests/Nimble/objc/ObjcStringersTest.m
+1 −1 circle.yml
2 changes: 1 addition & 1 deletion Carthage/Checkouts/realm-cocoa
12 changes: 1 addition & 11 deletions Source/Realm/RealmSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,8 @@ protocol ItemAtIndexPathRetrievable {
func itemAtIndexPath(path: NSIndexPath) -> Any?
}

protocol RealmRetrievable {
var realm: Realm? { get }
}

/// Class, representing a single section of Realm Results<T>.
public class RealmSection<T:Object> : SupplementaryAccessible, Section, ItemAtIndexPathRetrievable, RealmRetrievable {
public class RealmSection<T:Object> : SupplementaryAccessible, Section, ItemAtIndexPathRetrievable {

/// Results object
public var results : Results<T>
Expand Down Expand Up @@ -71,10 +67,4 @@ public class RealmSection<T:Object> : SupplementaryAccessible, Section, ItemAtIn
func itemAtIndexPath(path: NSIndexPath) -> Any? {
return results[path.item]
}

// MARK: - RealmRetrievable

var realm: Realm? {
return results.realm
}
}
50 changes: 31 additions & 19 deletions Source/Realm/RealmStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,12 @@ public class RealmStorage : BaseStorage, StorageProtocol, SupplementaryStoragePr
/// Array of `RealmSection` objects
public var sections = [Section]()

/// NotificationToken, that allows to unsubscribe from Realm notifications.
var notificationToken : NotificationToken?

public override init() {
super.init()
notificationToken = try? Realm().addNotificationBlock({ [weak self] notification, realm in
self?.handleRealmNotification(notification, realm: realm)
})
}

func handleRealmNotification(notification: Notification, realm: Realm) {
for section in sections {
if (section as? RealmRetrievable)?.realm == realm {
delegate?.storageNeedsReloading()
return
}
}
}
public var notificationTokens: [Int:NotificationToken] = [:]

deinit {
notificationToken?.stop()
notificationTokens.values.forEach { token in
token.stop()
}
}

/// Retrieve `RealmSection` at index
Expand All @@ -72,6 +57,32 @@ public class RealmStorage : BaseStorage, StorageProtocol, SupplementaryStoragePr
let section = RealmSection(results: results)
sections.append(section)
delegate?.storageNeedsReloading()
let sectionIndex = sections.count - 1
notificationTokens[sectionIndex] = results.addNotificationBlock { [weak self] change in
self?.handleChange(change, inSection:sectionIndex)
}
}

internal func handleChange<T>(change: RealmCollectionChange<T>, inSection: Int)
{
if case RealmCollectionChange.Initial(_) = change {
delegate?.storageNeedsReloading()
return
}
guard case let RealmCollectionChange.Update(_, deletions, insertions, modifications) = change else {
return
}
startUpdate()
deletions.forEach{ [weak self] in
self?.currentUpdate?.deletedRowIndexPaths.insert(NSIndexPath(forItem: $0, inSection: inSection))
}
insertions.forEach{ [weak self] in
self?.currentUpdate?.insertedRowIndexPaths.insert(NSIndexPath(forItem: $0, inSection: inSection))
}
modifications.forEach{ [weak self] in
self?.currentUpdate?.updatedRowIndexPaths.insert(NSIndexPath(forItem: $0, inSection: inSection))
}
finishUpdate()
}

/// Delete sections at indexes. Delegate will be automatically notified of changes
Expand All @@ -83,6 +94,7 @@ public class RealmStorage : BaseStorage, StorageProtocol, SupplementaryStoragePr
var i = sections.lastIndex
while i != NSNotFound {
self.sections.removeAtIndex(i)
notificationTokens[i]?.stop()
self.currentUpdate?.deletedSectionIndexes.insert(i)
i = sections.indexLessThanIndex(i)
}
Expand Down
72 changes: 71 additions & 1 deletion Tests/Realm/RealmStorageTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ import XCTest
import Nimble
import RealmSwift

func delay(delay:Double, _ closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}

class RealmStorageTestCase: XCTestCase {

let realm = try! Realm()
Expand Down Expand Up @@ -54,6 +63,67 @@ class RealmStorageTestCase: XCTestCase {
expect(storageObserver.storageNeedsReloadingFlag) == true
}

// func testInsertNotificationIsHandled() {
// let updateObserver = StorageUpdatesObserver()
// storage.delegate = updateObserver
// let results = realm.objects(Dog)
// storage.addSectionWithResults(results)
//
// addDogNamed("Rex")
//
// expect((self.storage.itemAtIndexPath(indexPath(0, 0)) as? Dog)?.name) == "Rex"
//
// try! realm.write {
// let dog = Dog()
// dog.name = "Rexxar"
// realm.add(dog)
// }
// expect(updateObserver.update?.insertedRowIndexPaths).toEventually(equal(Set([indexPath(1, 0), indexPath(0, 0)])))
// }

// MARK: - TODO make these tests run somehow, right now they are both failing, however when run apart from entire test case - they are successful.

// func testDeleteNotificationIsHandled() {
// let updateObserver = StorageUpdatesObserver()
// storage.delegate = updateObserver
// let results = realm.objects(Dog)
// storage.addSectionWithResults(results)
//
// var dog: Dog!
// try! realm.write {
// dog = Dog()
// dog.name = "Rexxar"
// realm.add(dog)
// }
//
// delay(0) {
// try! self.realm.write {
// self.realm.delete(dog)
// }
// }
// expect(updateObserver.update?.deletedRowIndexPaths).toEventually(equal(Set([indexPath(0, 0)])))
// }
//
// func testUpdateNotificationIsHandled() {
// let updateObserver = StorageUpdatesObserver()
// storage.delegate = updateObserver
// let results = realm.objects(Dog)
// storage.addSectionWithResults(results)
//
// var dog: Dog!
// try! realm.write {
// dog = Dog()
// dog.name = "Rexxar"
// realm.add(dog)
// }
// delay(0) {
// try! self.realm.write {
// dog.name = "Rex"
// }
// }
// expect(updateObserver.update?.updatedRowIndexPaths).toEventually(equal(Set([indexPath(0, 0)])))
// }

func testStorageHasSingleSection() {
addDogNamed("Rex")

Expand Down Expand Up @@ -131,4 +201,4 @@ class RealmStorageTestCase: XCTestCase {

expect(self.storage.footerModelForSectionIndex(0) as? Int) == 2
}
}
}

0 comments on commit 93be14f

Please sign in to comment.