Skip to content

Commit

Permalink
Done some stuff, gave up on diffable because maybe it doesn't work if…
Browse files Browse the repository at this point in the history
… we pass Realm objects between threads (#3)
  • Loading branch information
andreabou authored Feb 15, 2023
1 parent 99eea9f commit ad5207d
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 139 deletions.
8 changes: 4 additions & 4 deletions BLResultsController.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "BLResultsController"
s.version = "2.1.0"
s.version = "3.0.0"
s.summary = "BLResultsController is not a drop-in replacement for the `NSFetchedResultsController` to be used with Realm."
s.screenshot = "https://github.com/BellAppLab/BLResultsController/raw/main/Images/BLResultsController.png"

Expand Down Expand Up @@ -38,9 +38,9 @@ Changes to the underlying dataset are calculated on a background queue, therefor

s.swift_version = '5.0', '5.1', '5.2', '5.3'

s.ios.deployment_target = "12.0"
s.osx.deployment_target = "10.12"
s.tvos.deployment_target = "12.0"
s.ios.deployment_target = "13.0"
s.osx.deployment_target = "10.13"
s.tvos.deployment_target = "13.0"

s.module_name = 'BLResultsController'

Expand Down
94 changes: 50 additions & 44 deletions BLResultsController/Core/BLResultsController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,39 @@ import Glibc
import Darwin
#endif

private final class PThreadMutex {
func sync<R>(execute work: () throws -> R) rethrows -> R {
unbalancedLock()
defer { unbalancedUnlock() }
return try work()
}
private final class UnfairLock {
// MARK: Raw Lock
typealias Primitive = os_unfair_lock

private var unsafeMutex = pthread_mutex_t()
var primitive = os_unfair_lock()

/// Default constructs as ".Normal" or ".Recursive" on request.
init() {
var attr = pthread_mutexattr_t()
guard pthread_mutexattr_init(&attr) == 0 else {
preconditionFailure()
}
pthread_mutexattr_settype(&attr, Int32(PTHREAD_MUTEX_NORMAL))
guard pthread_mutex_init(&unsafeMutex, &attr) == 0 else {
preconditionFailure()
}
pthread_mutexattr_destroy(&attr)
@inlinable
func lock() {
os_unfair_lock_lock(&primitive)
}

deinit {
pthread_mutex_destroy(&unsafeMutex)
@inlinable
func tryLock() -> Bool {
os_unfair_lock_trylock(&primitive)
}

private func unbalancedLock() {
pthread_mutex_lock(&unsafeMutex)
@inlinable
func unlock() {
os_unfair_lock_unlock(&primitive)
}

private func unbalancedUnlock() {
pthread_mutex_unlock(&unsafeMutex)
@inlinable
func sync<R>(execute work: () throws -> R) rethrows -> R {
lock()
defer { unlock() }
return try work()
}

@inlinable
func trySync<R>(execute work: () throws -> R) rethrows -> R? {
guard tryLock() else { return nil }
defer { unlock() }
return try work()
}
}

Expand Down Expand Up @@ -172,7 +173,7 @@ public final class ResultsController<Section: ResultsControllerSection, Element:
/// The `NSPredicate`s currently in use by this `ResultsController`.
public private(set) var predicates: [NSPredicate]
/// The `SortDescriptor`s currently in use by this `ResultsController`.
public private(set) var sortDescriptors: [SortDescriptor]
public private(set) var sortDescriptors: [RealmSwift.SortDescriptor]
/// The path to the property used to calculate sections in this `ResultsController`.
public private(set) var sectionNameKeyPath: String

Expand Down Expand Up @@ -200,7 +201,7 @@ public final class ResultsController<Section: ResultsControllerSection, Element:
- `reload()`
*/
public func change(predicates: [NSPredicate]? = nil,
sortDescriptors: [SortDescriptor]? = nil,
sortDescriptors: [RealmSwift.SortDescriptor]? = nil,
sectionNameKeyPath: String? = nil) throws
{
precondition(Thread.current == Thread.main, "\(String(describing: self)) must only have its settings changed from the main thread.")
Expand Down Expand Up @@ -394,7 +395,7 @@ public final class ResultsController<Section: ResultsControllerSection, Element:
public init(realm: Realm,
predicates: [NSPredicate] = [],
sectionNameKeyPath: String,
sortDescriptors: [SortDescriptor]) throws
sortDescriptors: [RealmSwift.SortDescriptor]) throws
{
self.realm = realm
self.predicates = predicates
Expand Down Expand Up @@ -434,6 +435,12 @@ public final class ResultsController<Section: ResultsControllerSection, Element:
.int where Section.self is Int32.Type,
.int where Section.self is Int64.Type:
break
case .int where Section.self is UInt.Type,
.int where Section.self is UInt8.Type,
.int where Section.self is UInt16.Type,
.int where Section.self is UInt32.Type,
.int where Section.self is UInt64.Type:
break
case .float where Section.self is Float.Type:
break
case .double where Section.self is Double.Type:
Expand Down Expand Up @@ -533,11 +540,11 @@ public final class ResultsController<Section: ResultsControllerSection, Element:
fileprivate var backgroundRealm: BackgroundRealm!
fileprivate var realmChangeNotificationToken: NotificationToken!

fileprivate var elements: [InternalElement<Section>] = []
fileprivate var elements: [InternalElement<Section, Element>] = []
fileprivate var sectionIndexTitles: [String]?

@nonobjc
fileprivate let mutex = PThreadMutex()
fileprivate let lock = UnfairLock()

//MARK: Search
private var searchTimer: Timer? {
Expand Down Expand Up @@ -746,7 +753,7 @@ fileprivate extension ResultsController

func setup() {
DispatchQueue.global().async { [weak self] in
self?.mutex.sync { self?._setup() }
self?.lock.sync { self?._setup() }
}
}

Expand Down Expand Up @@ -784,20 +791,19 @@ fileprivate extension ResultsController

func processInitialLoad(_ results: [Element]) {
let new = generateElements(results: results)
mutex.sync {
lock.sync {
DispatchQueue.main.sync { [weak self] in
guard let strongSelf = self else { return }
strongSelf.elements = new
guard let callback = strongSelf._changeCallback else { return }
callback(.reload(controller: strongSelf))
guard let self = self else { return }
self.elements = new
self._changeCallback?(.reload(controller: self))
}
}
}

private func generateElements(results: [Element]) -> [InternalElement<Section>]
private func generateElements(results: [Element]) -> [InternalElement<Section, Element>]
{
var newElements = [InternalElement<Section>]()
var currentElement: InternalElement<Section>?
var newElements = [InternalElement<Section, Element>]()
var currentElement: InternalElement<Section, Element>?
results.enumerated().forEach { (offset, result) in
guard let section = result.value(forKeyPath: sectionNameKeyPath) as? Section else { return }
if let element = currentElement {
Expand Down Expand Up @@ -868,17 +874,17 @@ fileprivate extension ResultsController
return new.isEmpty == false && new[$0.section].items.isEmpty == false
}

mutex.sync {
lock.sync {
DispatchQueue.main.sync { [weak self] in
guard let strongSelf = self else { return }
strongSelf.elements = new
guard let self = self else { return }
self.elements = new

guard let callback = strongSelf._changeCallback else { return }
guard let callback = self._changeCallback else { return }

if sectionInsertions.isEmpty == false ||
sectionDeletions.isEmpty == false
{
callback(.sectionUpdate(controller: strongSelf,
callback(.sectionUpdate(controller: self,
insertedSections: sectionInsertions,
deletedSections: sectionDeletions))
}
Expand All @@ -887,7 +893,7 @@ fileprivate extension ResultsController
deletedIndexPaths.isEmpty == false ||
modifiedIndexPaths.isEmpty == false
{
callback(.rowUpdate(controller: strongSelf,
callback(.rowUpdate(controller: self,
insertedItems: insertionIndexPaths,
deletedItems: deletedIndexPaths,
updatedItems: modifiedIndexPaths))
Expand Down
27 changes: 16 additions & 11 deletions BLResultsController/Core/BLResultsControllerElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,18 @@ import class RealmSwift.Object
```
*/
public protocol ResultsControllerElement: Object {
var resultsControllerId: String { get }
associatedtype ResultsControllerID: Hashable
var resultsControllerId: ResultsControllerID { get }
}

public extension ResultsControllerElement where Self: Identifiable {
var resultsControllerId: ID { id }
}


struct InternalElement<Key: ResultsControllerSection>: Hashable, Equatable, Collection
struct InternalElement<Key: ResultsControllerSection, Element: ResultsControllerElement>: Hashable, Equatable, Collection
{
let items: [String]
let items: [Element.ResultsControllerID]
let key: Key

typealias Index = Int
Expand All @@ -61,23 +66,23 @@ struct InternalElement<Key: ResultsControllerSection>: Hashable, Equatable, Coll
return items.endIndex
}

subscript(i: Int) -> String {
subscript(i: Int) -> Element.ResultsControllerID {
return items[i]
}

func index(after i: Int) -> Int {
return items.index(after: i)
}

init(items: [String] = [],
init(items: [Element.ResultsControllerID] = [],
key: Key)
{
self.items = items
self.key = key
}

init<E: ResultsControllerElement>(element: E,
key: Key)
init(element: Element,
key: Key)
{
self.init(items: [element.resultsControllerId],
key: key)
Expand All @@ -87,8 +92,8 @@ struct InternalElement<Key: ResultsControllerSection>: Hashable, Equatable, Coll
hasher.combine(key.hashValue)
}

static func ==(lhs: InternalElement<Key>,
rhs: InternalElement<Key>) -> Bool
static func ==(lhs: InternalElement<Key, Element>,
rhs: InternalElement<Key, Element>) -> Bool
{
return lhs.key == rhs.key
}
Expand All @@ -97,7 +102,7 @@ struct InternalElement<Key: ResultsControllerSection>: Hashable, Equatable, Coll

extension InternalElement
{
mutating func insertElement<E: ResultsControllerElement>(_ element: E) {
mutating func insertElement(_ element: Element) {
var items = self.items
items.append(element.resultsControllerId)
self = InternalElement(items: items,
Expand All @@ -108,7 +113,7 @@ extension InternalElement

extension Array
{
func indexPathsOfElements<E: ResultsControllerSection>(indices: [Int]) -> [IndexPath] where Element == InternalElement<E>
func indexPathsOfElements<S: ResultsControllerSection, E: ResultsControllerElement>(indices: [Int]) -> [IndexPath] where Element == InternalElement<S, E>
{
guard indices.isEmpty == false else { return [] }
let enumerated = self.enumerated()
Expand Down
13 changes: 8 additions & 5 deletions BLResultsController/Core/BLResultsControllerSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,16 @@
*/

import Foundation
import RealmSwift


/**
A `ResultsControllerSection` is a property of your `Realm.Object` subclass that can be used to calculate sections in a `ResultsController`.

Currently, the `ResultsController` only supports the following types as `Section`s:
- `Bool`
- `Int`
- `Int8`
- `Int16`
- `Int32`
- `Int64`
- `Int` (`Int8`, `Int16`, `Int32`, `Int64`)
- `UInt` (`UInt8`, `UInt16`, `UInt32`, `UInt64`)
- `Double`
- `Float`
- `String`
Expand All @@ -50,6 +48,11 @@ extension Int8: ResultsControllerSection {}
extension Int16: ResultsControllerSection {}
extension Int32: ResultsControllerSection {}
extension Int64: ResultsControllerSection {}
extension UInt: ResultsControllerSection {}
extension UInt8: ResultsControllerSection {}
extension UInt16: ResultsControllerSection {}
extension UInt32: ResultsControllerSection {}
extension UInt64: ResultsControllerSection {}
extension Double: ResultsControllerSection {}
extension Float: ResultsControllerSection {}
extension String: ResultsControllerSection {}
Expand Down
Loading

0 comments on commit ad5207d

Please sign in to comment.