ShapeEdit/DocumentBrowser/RecentModelObjectsManager.swift
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
This is the Recents Manager and handles saving the recents list as it changes as well as notifies the delegate when recents are deleted / modified in a way that requires the UI to be refreshed. |
*/ |
import Foundation |
/** |
The delegate protocol implemented by the object that receives our results. |
We pass the updated list of results as well as a set of animations. |
*/ |
protocol RecentModelObjectsManagerDelegate: class { |
func recentsManagerResultsDidChange(results: [RecentModelObject], animations: [DocumentBrowserAnimation]) |
} |
/** |
The `RecentModelObjectsManager` manages our list of recents. It receives |
notifications from the recents as a RecentModelObjectDelegate and computes |
animations from the notifications which is submits to it's delegate. |
*/ |
class RecentModelObjectsManager: RecentModelObjectDelegate { |
// MARK: - Properties |
var recentModelObjects = [RecentModelObject]() |
static let maxRecentModelObjectCount = 3 |
static let recentsKey = "recents" |
private let workerQueue: NSOperationQueue = { |
let coordinationQueue = NSOperationQueue() |
coordinationQueue.name = "com.example.apple-samplecode.ShapeEdit.recentobjectsmanager.workerQueue" |
coordinationQueue.maxConcurrentOperationCount = 1 |
return coordinationQueue |
}() |
weak var delegate: RecentModelObjectsManagerDelegate? { |
didSet { |
/* |
If we already have results, we send them to the delegate as an |
initial update. |
*/ |
delegate?.recentsManagerResultsDidChange(recentModelObjects, animations: [.Reload]) |
} |
} |
// MARK: - Initialization |
init() { |
loadRecents() |
} |
deinit { |
// Be sure we are no longer listening for file presenter notifications. |
for recent in recentModelObjects { |
NSFileCoordinator.removeFilePresenter(recent) |
} |
} |
// MARK: - Recent Saving / Loading |
private func loadRecents() { |
workerQueue.addOperationWithBlock { |
let defaults = NSUserDefaults.standardUserDefaults() |
guard let loadedRecentData = defaults.objectForKey(RecentModelObjectsManager.recentsKey) as? [NSData] else { |
return |
} |
let loadedRecents = loadedRecentData.flatMap { recentModelObjectData in |
return NSKeyedUnarchiver.unarchiveObjectWithData(recentModelObjectData) as? RecentModelObject |
} |
// Remove any existing recents we may have already stored in memory. |
for recent in self.recentModelObjects { |
NSFileCoordinator.removeFilePresenter(recent) |
} |
/* |
Add all newly loaded recents to the recents set and register for |
`NSFilePresenter` notifications on all of them. |
*/ |
for recent in loadedRecents { |
recent.delegate = self |
NSFileCoordinator.addFilePresenter(recent) |
} |
self.recentModelObjects = loadedRecents |
// Check if the bookmark data is stale and resave the recents if it is. |
for recent in loadedRecents { |
if recent.bookmarkDataNeedsSave { |
self.saveRecents() |
} |
} |
NSOperationQueue.mainQueue().addOperationWithBlock { |
// Notify our delegate that the initial recents were loaded. |
self.delegate?.recentsManagerResultsDidChange(self.recentModelObjects, animations: [.Reload]) |
} |
} |
} |
private func saveRecents() { |
let recentModels = recentModelObjects.map { recentModelObject in |
return NSKeyedArchiver.archivedDataWithRootObject(recentModelObject) |
} |
NSUserDefaults.standardUserDefaults().setObject(recentModels, forKey: RecentModelObjectsManager.recentsKey) |
} |
// MARK: - Recent List Management |
private func removeRecentModelObject(recent: RecentModelObject) { |
// Remove the file presenter so we stop getting notifications on the removed recent. |
NSFileCoordinator.removeFilePresenter(recent) |
/* |
Remove the recent from the array and save the recents array to disk |
so they will reflect the correct state when the app is relaunched. |
*/ |
guard let index = recentModelObjects.indexOf(recent) else { return } |
recentModelObjects.removeAtIndex(index) |
saveRecents() |
} |
func addURLToRecents(URL: NSURL) { |
workerQueue.addOperationWithBlock { |
// Add the recent to the recents manager. |
guard let recent = RecentModelObject(URL: URL) else { return } |
var animations = [DocumentBrowserAnimation]() |
if let index = self.recentModelObjects.indexOf(recent) { |
self.recentModelObjects.removeAtIndex(index) |
if index != 0 { |
animations += [.Move(fromIndex: index, toIndex: 0)] |
} |
} |
else { |
recent.delegate = self |
NSFileCoordinator.addFilePresenter(recent) |
animations += [.Add(index: 0)] |
} |
self.recentModelObjects.insert(recent, atIndex: 0) |
// Prune down the recent documents if there are too many. |
while self.recentModelObjects.count > RecentModelObjectsManager.maxRecentModelObjectCount { |
self.removeRecentModelObject(self.recentModelObjects.last!) |
animations += [.Delete(index: self.recentModelObjects.count - 1)] |
} |
NSOperationQueue.mainQueue().addOperationWithBlock { |
self.delegate?.recentsManagerResultsDidChange(self.recentModelObjects, animations: animations) |
} |
self.saveRecents() |
} |
} |
// MARK: - RecentModelObjectDelegate |
func recentWasDeleted(recent: RecentModelObject) { |
self.workerQueue.addOperationWithBlock { |
guard let index = self.recentModelObjects.indexOf(recent) else { return } |
self.removeRecentModelObject(recent) |
NSOperationQueue.mainQueue().addOperationWithBlock { |
self.delegate?.recentsManagerResultsDidChange(self.recentModelObjects, animations: [ |
.Delete(index: index) |
]) |
} |
} |
} |
func recentNeedsReload(recent: RecentModelObject) { |
self.workerQueue.addOperationWithBlock { |
guard let index = self.recentModelObjects.indexOf(recent) else { return } |
NSOperationQueue.mainQueue().addOperationWithBlock { |
self.delegate?.recentsManagerResultsDidChange(self.recentModelObjects, animations: [ |
.Update(index: index) |
]) |
} |
} |
} |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-13