From 0c19c878c5bed69df3fc813a97a7cc5607df624b Mon Sep 17 00:00:00 2001 From: John Estropia Date: Thu, 17 Oct 2019 11:39:29 +0900 Subject: [PATCH] LiveList refetch --- CoreStore.xcodeproj/project.pbxproj | 2 + .../CoreStoreDemo.xcodeproj/project.pbxproj | 12 ++ .../CoreStoreDemo/Base.lproj/Main.storyboard | 131 ++++++++++---- .../CollectionViewDemoViewController.swift | 169 ++++++++++++++++++ .../ColorsDemo.swift | 8 +- .../ListObserverDemoViewController.swift | 1 + .../PaletteCollectionSectionHeaderView.swift | 17 ++ .../PaletteCollectionViewCell.swift | 18 ++ .../PaletteTableViewCell.swift | 5 +- Sources/Info.plist | 2 +- Sources/LiveList.swift | 84 ++++++++- 11 files changed, 408 insertions(+), 41 deletions(-) create mode 100644 CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/CollectionViewDemoViewController.swift create mode 100644 CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteCollectionSectionHeaderView.swift create mode 100644 CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteCollectionViewCell.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 553a2d2..8476d39 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -3123,6 +3123,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MARKETING_VERSION = 7.0.0; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -3141,6 +3142,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MARKETING_VERSION = 7.0.0; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; diff --git a/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj b/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj index 9492763..4da2f9f 100644 --- a/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj +++ b/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ B54AAD5B1AF4D26E00848AE0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B54AAD5A1AF4D26E00848AE0 /* Images.xcassets */; }; B54AAD5E1AF4D26E00848AE0 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = B54AAD5C1AF4D26E00848AE0 /* LaunchScreen.xib */; }; B560070F1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B560070E1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift */; }; + B5635D192357F9F700B80E6B /* CollectionViewDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5635D182357F9F700B80E6B /* CollectionViewDemoViewController.swift */; }; + B5635D1B2357FA4B00B80E6B /* PaletteCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5635D1A2357FA4B00B80E6B /* PaletteCollectionViewCell.swift */; }; B566E32A1B117B1F00F4F0C6 /* StackSetupDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566E3291B117B1F00F4F0C6 /* StackSetupDemoViewController.swift */; }; B566E3321B11DF3200F4F0C6 /* UserAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566E3311B11DF3200F4F0C6 /* UserAccount.swift */; }; B56964C91B20AC780075EE4A /* CustomLoggerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56964C81B20AC780075EE4A /* CustomLoggerViewController.swift */; }; @@ -38,6 +40,7 @@ B569651C1B30889A0075EE4A /* QueryingResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B569651B1B30889A0075EE4A /* QueryingResultsViewController.swift */; }; B56965291B3582D30075EE4A /* MigrationDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B56965271B3582D30075EE4A /* MigrationDemo.xcdatamodeld */; }; B5AA37EF2357D30300FFD4B9 /* ColorsDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AA37EE2357D30300FFD4B9 /* ColorsDemo.swift */; }; + B5C37EE52357FEBE0035A20D /* PaletteCollectionSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C37EE42357FEBE0035A20D /* PaletteCollectionSectionHeaderView.swift */; }; B5E599321B5240F50084BD5F /* OrganismTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E599311B5240F50084BD5F /* OrganismTableViewCell.swift */; }; B5E89AD01C5292A2003B04A9 /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5BDC9211C202429008147CD /* CoreStore.framework */; }; B5E89AD11C5292A2003B04A9 /* CoreStore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B5BDC9211C202429008147CD /* CoreStore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -85,6 +88,8 @@ B54AAD5A1AF4D26E00848AE0 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; B54AAD5D1AF4D26E00848AE0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; B560070E1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismV2ToV3MigrationPolicy.swift; sourceTree = ""; }; + B5635D182357F9F700B80E6B /* CollectionViewDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewDemoViewController.swift; sourceTree = ""; }; + B5635D1A2357FA4B00B80E6B /* PaletteCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaletteCollectionViewCell.swift; sourceTree = ""; }; B566E3291B117B1F00F4F0C6 /* StackSetupDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackSetupDemoViewController.swift; sourceTree = ""; }; B566E3311B11DF3200F4F0C6 /* UserAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserAccount.swift; sourceTree = ""; }; B56964C81B20AC780075EE4A /* CustomLoggerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomLoggerViewController.swift; sourceTree = ""; }; @@ -98,6 +103,7 @@ B56965281B3582D30075EE4A /* MigrationDemo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MigrationDemo.xcdatamodel; sourceTree = ""; }; B5AA37EE2357D30300FFD4B9 /* ColorsDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorsDemo.swift; sourceTree = ""; }; B5BDC9211C202429008147CD /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B5C37EE42357FEBE0035A20D /* PaletteCollectionSectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaletteCollectionSectionHeaderView.swift; sourceTree = ""; }; B5E599311B5240F50084BD5F /* OrganismTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OrganismTableViewCell.swift; path = "CoreStoreDemo/MIgrations Demo/OrganismTableViewCell.swift"; sourceTree = SOURCE_ROOT; }; B5EE25801B36E1B00000406B /* MigrationDemoV2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MigrationDemoV2.xcdatamodel; sourceTree = ""; }; B5EE25841B36E23C0000406B /* OrganismV1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismV1.swift; sourceTree = ""; }; @@ -138,9 +144,12 @@ B5AA37EE2357D30300FFD4B9 /* ColorsDemo.swift */, B52977D81B120B80003D50A5 /* ObserversViewController.swift */, B503FADB1AFDC71700F90881 /* ListObserverDemoViewController.swift */, + B5635D182357F9F700B80E6B /* CollectionViewDemoViewController.swift */, B503FADC1AFDC71700F90881 /* ObjectObserverDemoViewController.swift */, B503FADD1AFDC71700F90881 /* Palette.swift */, B503FADE1AFDC71700F90881 /* PaletteTableViewCell.swift */, + B5635D1A2357FA4B00B80E6B /* PaletteCollectionViewCell.swift */, + B5C37EE42357FEBE0035A20D /* PaletteCollectionSectionHeaderView.swift */, ); path = "List and Object Observers Demo"; sourceTree = ""; @@ -333,6 +342,7 @@ buildActionMask = 2147483647; files = ( B56965181B2E20CC0075EE4A /* TimeZone.swift in Sources */, + B5C37EE52357FEBE0035A20D /* PaletteCollectionSectionHeaderView.swift in Sources */, B56965291B3582D30075EE4A /* MigrationDemo.xcdatamodeld in Sources */, B5E599321B5240F50084BD5F /* OrganismTableViewCell.swift in Sources */, B5EE25851B36E23C0000406B /* OrganismV1.swift in Sources */, @@ -344,6 +354,7 @@ B503FAE01AFDC71700F90881 /* ObjectObserverDemoViewController.swift in Sources */, B52977D91B120B80003D50A5 /* ObserversViewController.swift in Sources */, B56964C91B20AC780075EE4A /* CustomLoggerViewController.swift in Sources */, + B5635D192357F9F700B80E6B /* CollectionViewDemoViewController.swift in Sources */, B566E32A1B117B1F00F4F0C6 /* StackSetupDemoViewController.swift in Sources */, B56964DA1B231BCA0075EE4A /* MaleAccount.swift in Sources */, B566E3321B11DF3200F4F0C6 /* UserAccount.swift in Sources */, @@ -355,6 +366,7 @@ B503FADF1AFDC71700F90881 /* ListObserverDemoViewController.swift in Sources */, B54AAD4F1AF4D26E00848AE0 /* AppDelegate.swift in Sources */, B50132282344E5E900FC238B /* SwiftUIContainerViewController.swift in Sources */, + B5635D1B2357FA4B00B80E6B /* PaletteCollectionViewCell.swift in Sources */, B56964D71B231AE90075EE4A /* StackSetupDemo.xcdatamodeld in Sources */, B56964DC1B231BCB0075EE4A /* FemaleAccount.swift in Sources */, B5AA37EF2357D30300FFD4B9 /* ColorsDemo.swift in Sources */, diff --git a/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard b/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard index f71eda2..71b7f7d 100644 --- a/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard +++ b/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard @@ -4,6 +4,7 @@ + @@ -623,7 +624,7 @@ - + @@ -649,7 +650,7 @@ - + @@ -713,7 +714,7 @@ - + @@ -764,14 +765,14 @@ - + - + @@ -878,8 +879,8 @@ - - + + @@ -894,36 +895,76 @@ - - + + - - - - - - - + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -1101,6 +1142,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/CollectionViewDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/CollectionViewDemoViewController.swift new file mode 100644 index 0000000..7a4175c --- /dev/null +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/CollectionViewDemoViewController.swift @@ -0,0 +1,169 @@ +// +// CollectionViewDemoViewController.swift +// CoreStoreDemo +// +// Created by John Estropia on 2019/10/17. +// Copyright © 2019 John Rommel Estropia. All rights reserved. +// + +import UIKit +import CoreStore + + +// MARK: - CollectionViewDemoViewController + +final class CollectionViewDemoViewController: UICollectionViewController { + + // MARK: UIViewController + + override func viewDidLoad() { + + super.viewDidLoad() + + let navigationItem = self.navigationItem + navigationItem.leftBarButtonItems = [ + self.editButtonItem, + UIBarButtonItem( + barButtonSystemItem: .trash, + target: self, + action: #selector(self.resetBarButtonItemTouched(_:)) + ) + ] + + let filterBarButton = UIBarButtonItem( + title: ColorsDemo.filter.rawValue, + style: .plain, + target: self, + action: #selector(self.filterBarButtonItemTouched(_:)) + ) + navigationItem.rightBarButtonItems = [ + UIBarButtonItem( + barButtonSystemItem: .add, + target: self, + action: #selector(self.addBarButtonItemTouched(_:)) + ), + UIBarButtonItem( + barButtonSystemItem: .refresh, + target: self, + action: #selector(self.shuffleBarButtonItemTouched(_:)) + ), + filterBarButton + ] + self.filterBarButton = filterBarButton + + self.dataSource = DiffableDataSource.CollectionView( + collectionView: self.collectionView, + dataStack: ColorsDemo.stack, + cellProvider: { (collectionView, indexPath, palette) in + + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PaletteCollectionViewCell", for: indexPath) as! PaletteCollectionViewCell + cell.colorView?.backgroundColor = palette.color + cell.label?.text = palette.colorText + return cell + }, + supplementaryViewProvider: { (collectionView, kind, indexPath) in + + switch kind { + + case UICollectionView.elementKindSectionHeader: + let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "PaletteCollectionSectionHeaderView", for: indexPath) as! PaletteCollectionSectionHeaderView + view.label?.text = ColorsDemo.palettes.sectionIdentifiers[indexPath.section] + return view + + default: + return nil + } + } + ) + ColorsDemo.palettes.addObserver(self) { [weak self] (liveList) in + + guard let self = self else { + + return + } + self.filterBarButton?.title = ColorsDemo.filter.rawValue + self.dataSource?.apply(liveList.snapshot, animatingDifferences: true) + } + self.dataSource?.apply(ColorsDemo.palettes.snapshot, animatingDifferences: false) + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + + super.prepare(for: segue, sender: sender) + + switch (segue.identifier, segue.destination, sender) { + + case ("ObjectObserverDemoViewController"?, let destinationViewController as ObjectObserverDemoViewController, let palette as LiveObject): + destinationViewController.setPalette(palette) + + default: + break + } + } + + + // MARK: UICollectionViewDelegate + + override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + + collectionView.deselectItem(at: indexPath, animated: true) + + self.performSegue( + withIdentifier: "ObjectObserverDemoViewController", + sender: ColorsDemo.palettes[indexPath: indexPath] + ) + } + + + // MARK: Private + + private var filterBarButton: UIBarButtonItem? + private var dataSource: DiffableDataSource.CollectionView? + + deinit { + + ColorsDemo.palettes.removeObserver(self) + } + + @IBAction private dynamic func resetBarButtonItemTouched(_ sender: AnyObject?) { + + ColorsDemo.stack.perform( + asynchronous: { (transaction) in + + try transaction.deleteAll(From()) + }, + completion: { _ in } + ) + } + + @IBAction private dynamic func filterBarButtonItemTouched(_ sender: AnyObject?) { + + ColorsDemo.filter = ColorsDemo.filter.next() + } + + @IBAction private dynamic func addBarButtonItemTouched(_ sender: AnyObject?) { + + ColorsDemo.stack.perform( + asynchronous: { (transaction) in + + let palette = transaction.create(Into()) + palette.setInitialValues(in: transaction) + }, + completion: { _ in } + ) + } + + @IBAction private dynamic func shuffleBarButtonItemTouched(_ sender: AnyObject?) { + ColorsDemo.stack.perform( + asynchronous: { (transaction) in + + for palette in try transaction.fetchAll(From()) { + + palette.hue .= Palette.randomHue() + palette.colorName .= nil + } + }, + completion: { _ in } + ) + } +} diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ColorsDemo.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ColorsDemo.swift index 7866298..d012c15 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ColorsDemo.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ColorsDemo.swift @@ -45,10 +45,10 @@ struct ColorsDemo { didSet { -// self.palettes.refetch( -// self.filter.whereClause(), -// OrderBy(.ascending(\.hue)) -// ) + self.palettes.refetch( + self.filter.whereClause(), + OrderBy(.ascending(\.hue)) + ) } } diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift index cd3d3dc..9eb2555 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift @@ -68,6 +68,7 @@ class ListObserverDemoViewController: UITableViewController { return } + self.filterBarButton?.title = ColorsDemo.filter.rawValue self.dataSource?.apply(liveList.snapshot, animatingDifferences: true) } self.dataSource?.apply(ColorsDemo.palettes.snapshot, animatingDifferences: false) diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteCollectionSectionHeaderView.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteCollectionSectionHeaderView.swift new file mode 100644 index 0000000..b2fa540 --- /dev/null +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteCollectionSectionHeaderView.swift @@ -0,0 +1,17 @@ +// +// PaletteCollectionSectionHeaderView.swift +// CoreStoreDemo +// +// Created by John Estropia on 2019/10/17. +// Copyright © 2019 John Rommel Estropia. All rights reserved. +// + +import UIKit + + +// MARK: - PaletteCollectionSectionHeaderView + +final class PaletteCollectionSectionHeaderView: UICollectionReusableView { + + @IBOutlet weak var label: UILabel? +} diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteCollectionViewCell.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteCollectionViewCell.swift new file mode 100644 index 0000000..156ccf2 --- /dev/null +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteCollectionViewCell.swift @@ -0,0 +1,18 @@ +// +// PaletteCollectionViewCell.swift +// CoreStoreDemo +// +// Created by John Estropia on 2019/10/17. +// Copyright © 2019 John Rommel Estropia. All rights reserved. +// + +import UIKit + + +// MARK: - PaletteCollectionViewCell + +final class PaletteCollectionViewCell: UICollectionViewCell { + + @IBOutlet weak var colorView: UIView? + @IBOutlet weak var label: UILabel? +} diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteTableViewCell.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteTableViewCell.swift index eeb1b1f..efdeafa 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteTableViewCell.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/PaletteTableViewCell.swift @@ -8,7 +8,10 @@ import UIKit -class PaletteTableViewCell: UITableViewCell { + +// MARK: - PaletteTableViewCell + +final class PaletteTableViewCell: UITableViewCell { @IBOutlet weak var colorView: UIView? @IBOutlet weak var label: UILabel? diff --git a/Sources/Info.plist b/Sources/Info.plist index da1d595..ca23c84 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.3.2 + $(MARKETING_VERSION) CFBundleSignature ???? CFBundleVersion diff --git a/Sources/LiveList.swift b/Sources/LiveList.swift index 905b052..fdce498 100644 --- a/Sources/LiveList.swift +++ b/Sources/LiveList.swift @@ -178,11 +178,53 @@ public final class LiveList: Hashable { self.observers.removeObject(forKey: observer) } - deinit { - self.observers.removeAllObjects() + // MARK: Public (Refetching) + + /** + Asks the `ListMonitor` to refetch its objects using the specified series of `FetchClause`s. Note that this method does not execute the fetch immediately; the actual fetching will happen after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes. + + `refetch(...)` broadcasts `listMonitorWillRefetch(...)` to its observers immediately, and then `listMonitorDidRefetch(...)` after the new fetch request completes. + + - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + - Important: Starting CoreStore 4.0, all `FetchClause`s required by the `ListMonitor` should be provided in the arguments list of `refetch(...)`. + */ + public func refetch(_ fetchClauses: FetchClause...) { + + self.refetch(fetchClauses) } + /** + Asks the `ListMonitor` to refetch its objects using the specified series of `FetchClause`s. Note that this method does not execute the fetch immediately; the actual fetching will happen after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes. + + `refetch(...)` broadcasts `listMonitorWillRefetch(...)` to its observers immediately, and then `listMonitorDidRefetch(...)` after the new fetch request completes. + + - parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. + - Important: Starting CoreStore 4.0, all `FetchClause`s required by the `ListMonitor` should be provided in the arguments list of `refetch(...)`. + */ + public func refetch(_ fetchClauses: [FetchClause]) { + + self.refetch { (fetchRequest) in + + fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest) } + } + } + + + // MARK: Public (3rd Party Utilities) + + /** + Allow external libraries to store custom data in the `ListMonitor`. App code should rarely have a need for this. + ``` + enum Static { + static var myDataKey: Void? + } + monitor.userInfo[&Static.myDataKey] = myObject + ``` + - Important: Do not use this method to store thread-sensitive data. + */ + public let userInfo = UserInfo() + // MARK: Equatable @@ -253,6 +295,44 @@ public final class LiveList: Hashable { ) } + internal func refetch(_ applyFetchClauses: @escaping (_ fetchRequest: Internals.CoreStoreFetchRequest) -> Void) { + + self.applyFetchClauses = applyFetchClauses + + DispatchQueue.main.async { [weak self] () -> Void in + + guard let `self` = self else { + + return + } + + let (newFetchedResultsController, newFetchedResultsControllerDelegate) = Self.recreateFetchedResultsController( + context: self.fetchedResultsController.managedObjectContext, + from: self.from, + sectionBy: self.sectionBy, + applyFetchClauses: self.applyFetchClauses + ) + newFetchedResultsControllerDelegate.handler = self + + do { + + try newFetchedResultsController.performFetchFromSpecifiedStores() + } + catch { + + // DataStack may have been deallocated + return + } + (self.fetchedResultsController, self.fetchedResultsControllerDelegate) = (newFetchedResultsController, newFetchedResultsControllerDelegate) + } + } + + deinit { + + self.fetchedResultsControllerDelegate.fetchedResultsController = nil + self.observers.removeAllObjects() + } + // MARK: FilePrivate