Don't destroy/recreate same items view controller during handoff

This commit is contained in:
Shadowfacts 2022-03-13 14:51:29 -04:00
parent c65b69cfbd
commit 00096e6df8
6 changed files with 108 additions and 82 deletions

View File

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
D608238D27DE729E00D7D5F9 /* ItemListType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D608238C27DE729E00D7D5F9 /* ItemListType.swift */; };
D65B18B22750469D004A9448 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65B18B12750469D004A9448 /* LoginViewController.swift */; }; D65B18B22750469D004A9448 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65B18B12750469D004A9448 /* LoginViewController.swift */; };
D65B18B4275048D9004A9448 /* ClientRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65B18B3275048D9004A9448 /* ClientRegistration.swift */; }; D65B18B4275048D9004A9448 /* ClientRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65B18B3275048D9004A9448 /* ClientRegistration.swift */; };
D65B18B627504920004A9448 /* FervorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65B18B527504920004A9448 /* FervorController.swift */; }; D65B18B627504920004A9448 /* FervorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65B18B527504920004A9448 /* FervorController.swift */; };
@ -116,6 +117,7 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
D608238C27DE729E00D7D5F9 /* ItemListType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemListType.swift; sourceTree = "<group>"; };
D65B18B12750469D004A9448 /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = "<group>"; }; D65B18B12750469D004A9448 /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = "<group>"; };
D65B18B3275048D9004A9448 /* ClientRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientRegistration.swift; sourceTree = "<group>"; }; D65B18B3275048D9004A9448 /* ClientRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientRegistration.swift; sourceTree = "<group>"; };
D65B18B527504920004A9448 /* FervorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FervorController.swift; sourceTree = "<group>"; }; D65B18B527504920004A9448 /* FervorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FervorController.swift; sourceTree = "<group>"; };
@ -324,6 +326,7 @@
D68B303527907D9200E8B3FA /* ExcerptGenerator.swift */, D68B303527907D9200E8B3FA /* ExcerptGenerator.swift */,
D68B304127932ED500E8B3FA /* UserActivities.swift */, D68B304127932ED500E8B3FA /* UserActivities.swift */,
D68408EE2794808E00E327D2 /* Preferences.swift */, D68408EE2794808E00E327D2 /* Preferences.swift */,
D608238C27DE729E00D7D5F9 /* ItemListType.swift */,
D6A8A33527766E9300CCEC72 /* CoreData */, D6A8A33527766E9300CCEC72 /* CoreData */,
D65B18AF2750468B004A9448 /* Screens */, D65B18AF2750468B004A9448 /* Screens */,
D6C687F7272CD27700874C10 /* Assets.xcassets */, D6C687F7272CD27700874C10 /* Assets.xcassets */,
@ -632,6 +635,7 @@
files = ( files = (
D6A8A33427766C2800CCEC72 /* PersistentContainer.swift in Sources */, D6A8A33427766C2800CCEC72 /* PersistentContainer.swift in Sources */,
D6E24357278B96E40005E546 /* Feed+CoreDataClass.swift in Sources */, D6E24357278B96E40005E546 /* Feed+CoreDataClass.swift in Sources */,
D608238D27DE729E00D7D5F9 /* ItemListType.swift in Sources */,
D65B18B627504920004A9448 /* FervorController.swift in Sources */, D65B18B627504920004A9448 /* FervorController.swift in Sources */,
D68B304227932ED500E8B3FA /* UserActivities.swift in Sources */, D68B304227932ED500E8B3FA /* UserActivities.swift in Sources */,
D6C687EC272CD27600874C10 /* AppDelegate.swift in Sources */, D6C687EC272CD27600874C10 /* AppDelegate.swift in Sources */,

60
Reader/ItemListType.swift Normal file
View File

@ -0,0 +1,60 @@
//
// ItemListType.swift
// Reader
//
// Created by Shadowfacts on 3/13/22.
//
import Foundation
import CoreData
enum ItemListType: Hashable {
case unread
case all
case group(Group)
case feed(Feed)
var title: String {
switch self {
case .unread:
return "Unread Articles"
case .all:
return "All Articles"
case let .group(group):
return group.title
case let .feed(feed):
return feed.title!
}
}
var idFetchRequest: NSFetchRequest<NSManagedObjectID> {
let req = NSFetchRequest<NSManagedObjectID>(entityName: "Item")
req.resultType = .managedObjectIDResultType
switch self {
case .unread:
req.predicate = NSPredicate(format: "read = NO")
case .all:
break
case .group(let group):
req.predicate = NSPredicate(format: "feed in %@", group.feeds!)
case .feed(let feed):
req.predicate = NSPredicate(format: "feed = %@", feed)
}
return req
}
var countFetchRequest: NSFetchRequest<Reader.Item>? {
let req = Reader.Item.fetchRequest()
switch self {
case .unread:
req.predicate = NSPredicate(format: "read = NO")
case .all:
return nil
case .group(let group):
req.predicate = NSPredicate(format: "read = NO AND feed in %@", group.feeds!)
case .feed(let feed):
req.predicate = NSPredicate(format: "read = NO AND feed = %@", feed)
}
return req
}
}

View File

@ -119,9 +119,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
} }
switch activity.activityType { switch activity.activityType {
case NSUserActivity.readUnreadType: case NSUserActivity.readUnreadType:
split.selectHomeItem(.unread) split.showItemList(.unread)
case NSUserActivity.readAllType: case NSUserActivity.readAllType:
split.selectHomeItem(.all) split.showItemList(.all)
case NSUserActivity.readFeedType: case NSUserActivity.readFeedType:
guard let feedID = activity.feedID() else { guard let feedID = activity.feedID() else {
break break
@ -129,7 +129,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
let req = Feed.fetchRequest() let req = Feed.fetchRequest()
req.predicate = NSPredicate(format: "id = %@", feedID) req.predicate = NSPredicate(format: "id = %@", feedID)
if let feed = try? fervorController.persistentContainer.viewContext.fetch(req).first { if let feed = try? fervorController.persistentContainer.viewContext.fetch(req).first {
split.selectHomeItem(.feed(feed)) split.showItemList(.feed(feed))
} }
case NSUserActivity.readGroupType: case NSUserActivity.readGroupType:
guard let groupID = activity.groupID() else { guard let groupID = activity.groupID() else {
@ -138,7 +138,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
let req = Group.fetchRequest() let req = Group.fetchRequest()
req.predicate = NSPredicate(format: "id = %@", groupID) req.predicate = NSPredicate(format: "id = %@", groupID)
if let group = try? fervorController.persistentContainer.viewContext.fetch(req).first { if let group = try? fervorController.persistentContainer.viewContext.fetch(req).first {
split.selectHomeItem(.group(group)) split.showItemList(.group(group))
} }
default: default:
break break

View File

@ -54,7 +54,7 @@ class AppSplitViewController: UISplitViewController {
setViewController(nav, for: .compact) setViewController(nav, for: .compact)
} }
func selectHomeItem(_ item: HomeViewController.Item) { func showItemList(_ type: ItemListType) {
let column: Column let column: Column
if traitCollection.horizontalSizeClass == .compact { if traitCollection.horizontalSizeClass == .compact {
column = .compact column = .compact
@ -63,7 +63,7 @@ class AppSplitViewController: UISplitViewController {
} }
let nav = viewController(for: column) as! UINavigationController let nav = viewController(for: column) as! UINavigationController
let home = nav.viewControllers.first! as! HomeViewController let home = nav.viewControllers.first! as! HomeViewController
home.selectItem(item) home.selectItem(type)
} }
} }

View File

@ -23,7 +23,7 @@ class HomeViewController: UIViewController {
var enableStretchyMenu = true var enableStretchyMenu = true
private var collectionView: UICollectionView! private var collectionView: UICollectionView!
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>! private var dataSource: UICollectionViewDiffableDataSource<Section, ItemListType>!
private var groupResultsController: NSFetchedResultsController<Group>! private var groupResultsController: NSFetchedResultsController<Group>!
private var feedResultsController: NSFetchedResultsController<Feed>! private var feedResultsController: NSFetchedResultsController<Feed>!
@ -76,7 +76,7 @@ class HomeViewController: UIViewController {
dataSource = createDataSource() dataSource = createDataSource()
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() var snapshot = NSDiffableDataSourceSnapshot<Section, ItemListType>()
snapshot.appendSections([.all, .groups, .feeds]) snapshot.appendSections([.all, .groups, .feeds])
snapshot.appendItems([.unread, .all], toSection: .all) snapshot.appendItems([.unread, .all], toSection: .all)
dataSource.apply(snapshot, animatingDifferences: false) dataSource.apply(snapshot, animatingDifferences: false)
@ -116,14 +116,14 @@ class HomeViewController: UIViewController {
dataSource.apply(snapshot) dataSource.apply(snapshot)
} }
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> { private func createDataSource() -> UICollectionViewDiffableDataSource<Section, ItemListType> {
let sectionHeaderCell = UICollectionView.SupplementaryRegistration<UICollectionViewListCell>(elementKind: UICollectionView.elementKindSectionHeader) { supplementaryView, elementKind, indexPath in let sectionHeaderCell = UICollectionView.SupplementaryRegistration<UICollectionViewListCell>(elementKind: UICollectionView.elementKindSectionHeader) { supplementaryView, elementKind, indexPath in
let section = self.dataSource.sectionIdentifier(for: indexPath.section)! let section = self.dataSource.sectionIdentifier(for: indexPath.section)!
var config = supplementaryView.defaultContentConfiguration() var config = supplementaryView.defaultContentConfiguration()
config.text = section.title config.text = section.title
supplementaryView.contentConfiguration = config supplementaryView.contentConfiguration = config
} }
let listCell = UICollectionView.CellRegistration<HomeCollectionViewCell, Item> { cell, indexPath, item in let listCell = UICollectionView.CellRegistration<HomeCollectionViewCell, ItemListType> { cell, indexPath, item in
var config = UIListContentConfiguration.valueCell() var config = UIListContentConfiguration.valueCell()
config.text = item.title config.text = item.title
if let req = item.countFetchRequest, if let req = item.countFetchRequest,
@ -135,7 +135,7 @@ class HomeViewController: UIViewController {
cell.accessories = [.disclosureIndicator()] cell.accessories = [.disclosureIndicator()]
} }
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in let dataSource = UICollectionViewDiffableDataSource<Section, ItemListType>(collectionView: collectionView) { collectionView, indexPath, item in
return collectionView.dequeueConfiguredReusableCell(using: listCell, for: indexPath, item: item) return collectionView.dequeueConfiguredReusableCell(using: listCell, for: indexPath, item: item)
} }
dataSource.supplementaryViewProvider = { collectionView, elementKind, indexPath in dataSource.supplementaryViewProvider = { collectionView, elementKind, indexPath in
@ -206,26 +206,26 @@ class HomeViewController: UIViewController {
} }
} }
private func itemsViewController(for item: Item) -> ItemsViewController { private func itemsViewController(for item: ItemListType) -> ItemsViewController {
let vc = ItemsViewController(fetchRequest: item.idFetchRequest, fervorController: fervorController) let vc = ItemsViewController(type: item, fervorController: fervorController)
vc.title = item.title
vc.delegate = itemsDelegate vc.delegate = itemsDelegate
switch item {
case .all:
vc.userActivity = .readAll(account: fervorController.account!)
case .unread:
vc.userActivity = .readUnread(account: fervorController.account!)
case .group(let group):
vc.userActivity = .readGroup(group, account: fervorController.account!)
case .feed(let feed):
vc.userActivity = .readFeed(feed, account: fervorController.account!)
}
return vc return vc
} }
func selectItem(_ item: Item) { func selectItem(_ item: ItemListType) {
navigationController!.popToRootViewController(animated: false) guard let navigationController = navigationController else {
navigationController!.pushViewController(itemsViewController(for: item), animated: false) return
}
if navigationController.viewControllers.count >= 2,
let second = navigationController.viewControllers[1] as? ItemsViewController,
second.type == item {
while navigationController.viewControllers.count > 2 {
navigationController.popViewController(animated: false)
}
} else {
navigationController.popToRootViewController(animated: false)
navigationController.pushViewController(itemsViewController(for: item), animated: false)
}
} }
} }
@ -247,56 +247,6 @@ extension HomeViewController {
} }
} }
} }
enum Item: Hashable {
case unread
case all
case group(Group)
case feed(Feed)
var title: String {
switch self {
case .unread:
return "Unread Articles"
case .all:
return "All Articles"
case let .group(group):
return group.title
case let .feed(feed):
return feed.title!
}
}
var idFetchRequest: NSFetchRequest<NSManagedObjectID> {
let req = NSFetchRequest<NSManagedObjectID>(entityName: "Item")
req.resultType = .managedObjectIDResultType
switch self {
case .unread:
req.predicate = NSPredicate(format: "read = NO")
case .all:
break
case .group(let group):
req.predicate = NSPredicate(format: "feed in %@", group.feeds!)
case .feed(let feed):
req.predicate = NSPredicate(format: "feed = %@", feed)
}
return req
}
var countFetchRequest: NSFetchRequest<Reader.Item>? {
let req = Reader.Item.fetchRequest()
switch self {
case .unread:
req.predicate = NSPredicate(format: "read = NO")
case .all:
return nil
case .group(let group):
req.predicate = NSPredicate(format: "read = NO AND feed in %@", group.feeds!)
case .feed(let feed):
req.predicate = NSPredicate(format: "read = NO AND feed = %@", feed)
}
return req
}
}
} }
extension HomeViewController: NSFetchedResultsControllerDelegate { extension HomeViewController: NSFetchedResultsControllerDelegate {

View File

@ -18,20 +18,32 @@ class ItemsViewController: UIViewController {
weak var delegate: ItemsViewControllerDelegate? weak var delegate: ItemsViewControllerDelegate?
let fervorController: FervorController let fervorController: FervorController
let fetchRequest: NSFetchRequest<NSManagedObjectID> let type: ItemListType
private let fetchRequest: NSFetchRequest<NSManagedObjectID>
private var collectionView: UICollectionView! private var collectionView: UICollectionView!
private var dataSource: UICollectionViewDiffableDataSource<Section, NSManagedObjectID>! private var dataSource: UICollectionViewDiffableDataSource<Section, NSManagedObjectID>!
private var batchUpdates: [() -> Void] = [] private var batchUpdates: [() -> Void] = []
init(fetchRequest: NSFetchRequest<NSManagedObjectID>, fervorController: FervorController) { init(type: ItemListType, fervorController: FervorController) {
precondition(fetchRequest.entityName == "Item")
self.fervorController = fervorController self.fervorController = fervorController
self.fetchRequest = fetchRequest self.type = type
self.fetchRequest = type.idFetchRequest
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
self.title = type.title
switch type {
case .all:
self.userActivity = .readAll(account: fervorController.account!)
case .unread:
self.userActivity = .readUnread(account: fervorController.account!)
case .group(let group):
self.userActivity = .readGroup(group, account: fervorController.account!)
case .feed(let feed):
self.userActivity = .readFeed(feed, account: fervorController.account!)
}
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {