Skip to content

Commit

Permalink
core data working
Browse files Browse the repository at this point in the history
  • Loading branch information
MrAdamBoyd committed May 31, 2016
1 parent ba5d3c8 commit aef7a7e
Show file tree
Hide file tree
Showing 9 changed files with 688 additions and 1,098 deletions.
1,487 changes: 515 additions & 972 deletions DownTube/DownTube.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Binary file not shown.
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "0"
version = "2.0">
</Bucket>
69 changes: 1 addition & 68 deletions DownTube/DownTube/AppDelegate.swift
Expand Up @@ -21,10 +21,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
splitViewController.delegate = self

let masterNavigationController = splitViewController.viewControllers[0] as! UINavigationController
let controller = masterNavigationController.topViewController as! MasterViewController
controller.managedObjectContext = self.managedObjectContext
return true
}

Expand All @@ -49,7 +45,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
CoreDataController.sharedController.saveContext()
}

// MARK: - Split view
Expand All @@ -63,68 +59,5 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
}
return false
}
// MARK: - Core Data stack

lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "com.adam.DownTube" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()

lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle.mainBundle().URLForResource("DownTube", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason

dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}

return coordinator
}()

lazy var managedObjectContext: NSManagedObjectContext = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()

// MARK: - Core Data Saving support

func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}

}

106 changes: 106 additions & 0 deletions DownTube/DownTube/CoreDataController.swift
@@ -0,0 +1,106 @@
//
// CoreDataController.swift
// DownTube
//
// Created by Adam Boyd on 2016-05-30.
// Copyright © 2016 Adam. All rights reserved.
//

import Foundation
import CoreData

class CoreDataController {
static let sharedController = CoreDataController()

//MARK: - Core Data Stack

var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}

let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
let entity = NSEntityDescription.entityForName("Video", inManagedObjectContext: self.managedObjectContext)
fetchRequest.entity = entity

// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20

//Order: most recent first
let sortDescriptor = NSSortDescriptor(key: "created", ascending: false)

fetchRequest.sortDescriptors = [sortDescriptor]

// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: "Master")
_fetchedResultsController = aFetchedResultsController

do {
try _fetchedResultsController!.performFetch()
} catch {
print("Could not save to Core Data")
}

return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil

lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "com.adam.SlackTeam" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()

lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle.mainBundle().URLForResource("DownTube", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason

dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)

NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort() //Abort here if something goes really wrong
}

return coordinator
}()

lazy var managedObjectContext: NSManagedObjectContext = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()

// MARK: - Core Data Saving support

func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
@@ -1,9 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="Test1.xcdatamodel" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1" systemVersion="11A491" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="Event">
<attribute name="timeStamp" optional="YES" attributeType="Date"/>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10174" systemVersion="15F34" minimumToolsVersion="Automatic">
<entity name="Video" representedClassName=".Video" syncable="YES">
<attribute name="created" optional="YES" attributeType="Date" syncable="YES"/>
<attribute name="quality" optional="YES" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="uploader" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<elements>
<element name="Event" positionX="261" positionY="189" width="128" height="60"/>
<element name="Video" positionX="270" positionY="207" width="128" height="105"/>
</elements>
</model>
69 changes: 15 additions & 54 deletions DownTube/DownTube/MasterViewController.swift
Expand Up @@ -12,8 +12,6 @@ import CoreData
class MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate {

var detailViewController: DetailViewController? = nil
var managedObjectContext: NSManagedObjectContext? = nil


override func viewDidLoad() {
super.viewDidLoad()
Expand All @@ -26,6 +24,8 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
let controllers = split.viewControllers
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}

CoreDataController.sharedController.fetchedResultsController.delegate = self
}

override func viewWillAppear(animated: Bool) {
Expand All @@ -39,13 +39,13 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
}

func insertNewObject(sender: AnyObject) {
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
let context = CoreDataController.sharedController.fetchedResultsController.managedObjectContext
let entity = CoreDataController.sharedController.fetchedResultsController.fetchRequest.entity!
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context)

// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
newManagedObject.setValue(NSDate(), forKey: "timeStamp")
newManagedObject.setValue(NSDate(), forKey: "created")

// Save the context.
do {
Expand All @@ -63,7 +63,7 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath)
let object = CoreDataController.sharedController.fetchedResultsController.objectAtIndexPath(indexPath)
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
Expand All @@ -75,18 +75,18 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
// MARK: - Table View

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.fetchedResultsController.sections?.count ?? 0
return CoreDataController.sharedController.fetchedResultsController.sections?.count ?? 0
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = self.fetchedResultsController.sections![section]
let sectionInfo = CoreDataController.sharedController.fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
self.configureCell(cell, withObject: object)
let object = CoreDataController.sharedController.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
self.configureCell(cell, withVideo: object as! Video)
return cell
}

Expand All @@ -97,8 +97,8 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
let context = self.fetchedResultsController.managedObjectContext
context.deleteObject(self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject)
let context = CoreDataController.sharedController.fetchedResultsController.managedObjectContext
context.deleteObject(CoreDataController.sharedController.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject)

do {
try context.save()
Expand All @@ -111,49 +111,10 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
}
}

func configureCell(cell: UITableViewCell, withObject object: NSManagedObject) {
cell.textLabel!.text = object.valueForKey("timeStamp")!.description
func configureCell(cell: UITableViewCell, withVideo video: Video) {
cell.textLabel!.text = video.created?.description
}

// MARK: - Fetched results controller

var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}

let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: self.managedObjectContext!)
fetchRequest.entity = entity

// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20

// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false)

fetchRequest.sortDescriptors = [sortDescriptor]

// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master")
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController

do {
try _fetchedResultsController!.performFetch()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//print("Unresolved error \(error), \(error.userInfo)")
abort()
}

return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil

func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.tableView.beginUpdates()
}
Expand All @@ -176,7 +137,7 @@ class MasterViewController: UITableViewController, NSFetchedResultsControllerDel
case .Delete:
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
case .Update:
self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, withObject: anObject as! NSManagedObject)
self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, withVideo: anObject as! Video)
case .Move:
tableView.moveRowAtIndexPath(indexPath!, toIndexPath: newIndexPath!)
}
Expand Down
22 changes: 22 additions & 0 deletions DownTube/DownTube/Video+CoreDataProperties.swift
@@ -0,0 +1,22 @@
//
// Video+CoreDataProperties.swift
//
//
// Created by Adam Boyd on 2016-05-30.
//
//
// Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
// to delete and recreate this implementation file for your updated model.
//

import Foundation
import CoreData

extension Video {

@NSManaged var title: String?
@NSManaged var quality: NSNumber?
@NSManaged var uploader: String?
@NSManaged var created: NSDate?

}

0 comments on commit aef7a7e

Please sign in to comment.