I have UICollectionView that linked to CoreData entity via NSFetchedResultsController.
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
if type == .insert {
blockOperations.append(BlockOperation(block: {
self.MessagesCollectionView?.insertItems(at: [newIndexPath!])
}))
}
if type == .update {
blockOperations.append(BlockOperation(block: {
self.MessagesCollectionView?.reloadItems(at: [newIndexPath!])
}))
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let count = fetchedResultsControler.sections?[0].numberOfObjects
return count!
}
UICollection updates properly when i am inserting unique data into entity. But when i insert data that conflicts with unique constraint - new record in entity does not inserts (its ok), but UICollection didChange method gets '.insert' type of operation and inserts empty row!!! How to figure out this constraint issue in didChange method?
I can check existent of record in DB layer class before inserting, but in this case constraints are useless...
Here is the NSFetchResultController code:
lazy var fetchedResultsControler in UICollectionView: NSFetchedResultsController<ChatMessages> = {
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ChatMessages")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "message_created", ascending : true)]
fetchRequest.includesPendingChanges = false
let frc = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil )
frc.delegate = self
return frc as! NSFetchedResultsController<ChatMessages>
}()
Here is the core data insert code:
let newValue = NSEntityDescription.insertNewObject(forEntityName: "ChatMessages", into: context) as NSManagedObject
newValue.setValue(text, forKey: "text")
newValue.setValue(message_id, forKey:"message_id")
let parentContext = delegate.persistentContainer.viewContext
parentContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
context.parent = parentContext
context.performAndWait {
do
{
dump(value)
try context.save()
print("Obj prepared for saving:")
print(value)
parentContext.performAndWait {
do {
try parentContext.save()
} catch {
print("Failture to save context: \(error)")
parentContext.rollback()
}
}
}
catch
{
print("Object exists...")
context.rollback()
}
}
Related
My application has two tab bars. The first one presents a list of games added on view controller and save them on the core data database. Switching on the second tab/view reads from the database and presents it inside a table view. I implemented the NSFetchedResultsControllerDelegate with a fetch method. When I add the first item to the context on the first tab and switch to second tab, FRC delegate methods (controllerWillChangeContent(_:), controller(_:didChange:at:for:newIndexPath:), controllerDidChangeContent(_:)) are not getting called and the table view is empty while I can see arrayOfGamesCount = 1. But when I add a second item, I can see all FRC delegate methods are getting call when I switch to second tab bar. And TableView display one rows while arrayOfGamesCount = 2
The first tab bar have 2 view controllers.(AddGameViewController and WelcomeViewController) AddGameVC is used to grab data from textfields and send it to welcomeVC.
import UIKit
import CoreData
class WelcomeViewController: UIViewController,SendGameDataDelegate, UIAdaptivePresentationControllerDelegate {
var games : [Game]? = []
var gamesMo: [GameMo]? = []
var gamed: GameMo?
var game : Game?
func ShouldSendGame(game: Game) {
self.game = game
print("\(game)")
games?.append(game)
}
#IBAction func endWLButton(_ sender: UIButton) {
saveDataToCoreData()
print("number of games from gamesMoCount is \(gamesMo?.count ?? 0)")
games?.removeAll()
reloadCollectionViewData()
}
func saveDataToCoreData (){
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
gamed = GameMo(context: appDelegate.persistentContainer.viewContext)
if games != nil {
for game in games! {
gamed?.goal = Int32(game.goal ?? 0 )
gamed?.rivalGoal = Int32(game.rivalGoal ?? 0)
gamed?.shot = Int32(game.shots ?? 0)
gamed?.rivalShot = Int32(game.rivalGoal ?? 0)
gamed?.rivalCorners = Int32(game.rivalsCorner ?? 0)
gamed?.corners = Int32(game.corners ?? 0)
gamesMo?.append(gamed!)
}
print("Saving data to context ....")
appDelegate.saveContext()
}
}
}
}
extension WelcomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return games?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let gameIndex = games?[indexPath.row] {
let userGameScore = gameIndex.goal
let rivalGameScore = gameIndex.rivalGoal
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FormCell", for: indexPath) as? FormCollectionViewCell {
cell.setCell(userScores: userGameScore!, rivalScores: rivalGameScore! )
return cell
}
}
return UICollectionViewCell ()
}
}
The second tab bar have only one VC: AllWLeagueController used to display items from the the database.
import UIKit
import CoreData
class AllWLeagueController : UITableViewController {
var fetchRequestController : NSFetchedResultsController<GameMo>!
var arrayOfGamesModel : [[GameMo]]? = []
var gameMo: GameMo?
var gamesMo: [GameMo] = []
override func viewDidLoad() {
validation(object: arrayOfGamesModel)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchRequest()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("arrayOfGamesModelcount est \(arrayOfGamesModel?.count ?? 0)")
return arrayOfGamesModel?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let weekL = arrayOfGamesModel?[indexPath.row] {
if let cell = tableView.dequeueReusableCell(withIdentifier: "WL") as? AllWLeaguesTableViewCell {
let winCounts = WLManager.winCountMethod(from: weekL)
let lossCounts = WLManager.lossCountMethod(from:weekL)
cell.setOulet(win: winCounts, loss: lossCounts, rankName: rankString)
cellLayer(with: cell)
return cell
}
}
}
extension AllWLeagueController: NSFetchedResultsControllerDelegate {
func fetchRequest () {
let fetchRequest = NSFetchRequest<GameMo>(entityName: "Game")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "win", ascending: true)]
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate){
let context = appDelegate.persistentContainer.viewContext
// fetch result controller
fetchRequestController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchRequestController.delegate = self
do{
try fetchRequestController.performFetch()
if let fetchedObjects = fetchRequestController.fetchedObjects {
gamesMo = fetchedObjects
print("Fetech Request Activated")
print(gamesMo)
}
}catch{
fatalError("Failed to fetch entities: \(error)")
}
}
}
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("TableView beginupdates")
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
if let newIndexPath = newIndexPath {
print("insert")
tableView.insertRows(at: [newIndexPath], with: .fade)
}
case .delete:
if let indexPath = indexPath {
print("delete")
tableView.deleteRows(at: [indexPath], with: .fade)
}
case .update:
if let indexPath = indexPath {
print("update")
tableView.reloadRows(at: [indexPath], with: .fade)
}
default:
tableView.reloadData()
}
if let fetchedObjects = controller.fetchedObjects {
gamesMo = fetchedObjects as! [GameMo]
print("we are about to append arrayOfGamesModel")
arrayOfGamesModel?.append(gamesMo)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("TableView endupdates")
tableView.endUpdates()
}
}
You are making a fatal mistake. In saveDataToCoreData only one instance is created and then it's being overwritten with the game data in each iteration of the array. So your array gamesMo may contain multiple items but it's always the same instance and only one instance is saved into the context.
Replace saveDataToCoreData with
func saveDataToCoreData (){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
guard let games = games else { return }
for game in games {
let newGame = GameMo(context: appDelegate.persistentContainer.viewContext)
newGame.goal = Int32(game.goal ?? 0 )
newGame.rivalGoal = Int32(game.rivalGoal ?? 0)
newGame.shot = Int32(game.shots ?? 0)
newGame.rivalShot = Int32(game.rivalGoal ?? 0)
newGame.rivalCorners = Int32(game.rivalsCorner ?? 0)
newGame.corners = Int32(game.corners ?? 0)
gamesMo.append(newGame)
}
print("Saving data to context ....")
appDelegate.saveContext()
}
Another bad practice is to create new fetch results controllers in viewWillAppear. It's highly recommended to create one controller as lazy instantiated property – as well as the managed object context – for example
lazy var context : NSManagedObjectContext = {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}()
lazy var fetchRequestController : NSFetchedResultsController<GameMo> = {
let fetchRequest = NSFetchRequest<GameMo>(entityName: "Game")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "win", ascending: true)]
// fetch result controller
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
frc.delegate = self
do {
try frc.performFetch()
if let fetchedObjects = frc.fetchedObjects {
self.gamesMo = fetchedObjects
print("Fetech Request Activated")
print(gamesMo)
}
} catch{
fatalError("Failed to fetch entities: \(error)")
}
return frc
}()
Force unwrapping AppDelegate is perfectly fine. Your app won't even launch if AppDelegate was missing.
I recommend also to use less ambiguous variable names. games, gamesMo, gamed and game look very similar and can cause confusion.
I've been playing with Core Data for the past 18 hours or so. I'm fetching data with NSFetchedResultsController and shows data with UITableView. Adding a new record and deleting the selected record aren't my problems.
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// MARK: - Instance variables
private let persistentContainer = NSPersistentContainer(name: "Profiles") // core data model file (.xcdatamodeld)
var managedObjectContext: NSManagedObjectContext?
// MARK: - IBOutlets
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// loading persistentContainer //
persistentContainer.loadPersistentStores { (persistentStoreDescription, error) in
if let error = error {
print("Unable to Load Persistent Store")
} else {
do {
try self.fetchedResultsController.performFetch()
} catch {
let fetchError = error as NSError
print("\(fetchError), \(fetchError.localizedDescription)")
}
}
}
// notifications //
NotificationCenter.default.addObserver(self, selector: #selector(profileDidUpdate), name: NSNotification.Name(rawValue: "HomeViewControllerPictureDidSelect"), object: nil)
}
// MARK: - fetchedResultsController(controller with the entity)
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<Person> = {
// Create Fetch Request with Entity
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
// Configure Fetch Request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "lastName", ascending: true)]
// Create Fetched Results Controller
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
// Configure Fetched Results Controller
fetchedResultsController.delegate = self
return fetchedResultsController
}()
// MARK: - fetchedResultsController
// MARK: - Notifications
#objc func profileDidUpdate(notification: NSNotification) {
let profile = notification.object as! Profile
let context = persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Person", in: context)
let newPerson = NSManagedObject(entity: entity!, insertInto: context)
newPerson.setValue(profile.uuid, forKey: "uuid") // uuid is used to make each record unique
newPerson.setValue(profile.firstName, forKey: "firstName")
newPerson.setValue(profile.lastName, forKey: "lastName")
newPerson.setValue(profile.age, forKey: "age")
newPerson.setValue(profile.pictData, forKey: "pictData")
do {
try context.save()
print("saved...")
} catch {
print("failed saving")
}
}
// MARK: - Notifications
}
extension HomeViewController: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch (type) {
case .insert:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break;
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break;
default:
print("...")
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
}
}
Shown above, I create a new record from another view controller, which sends an object of a model (Profile) to the current view controller (HomeViewController). I don't have to reload the table thanks to NSFetchedResultsController.
The entity has several attributes (age, firstName, lastName, pictData, uuid). And I want to change the selected record in the list with two attributes: firstName and lastName. The uuid attribute is used to identify a specific record.
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBAction func editTapped(_ sender: UIButton) {
guard let indexPath = tableView.indexPathForSelectedRow else {
return
}
let selectedRow = indexPath.row
if selectedRow >= 0 {
editRecord(index: selectedRow)
}
}
func editRecord(index: Int) {
let indexPath = IndexPath(row: index, section: 0)
let person = fetchedResultsController.object(at: indexPath)
let uuid = person.uuid!
let context = self.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
fetchRequest.predicate = NSPredicate(format: "uuid == %#", uuid)
do {
let result = try context.fetch(fetchRequest)
if (result.count > 0) {
let managedObject = result[0] as! NSManagedObject
managedObject.setValue("Donald", forKey: "firstName")
managedObject.setValue("Washington", forKey: "lastName")
try context.save()
print("Changes saved...")
}
} catch {
print("Failed")
}
}
}
Now, if I click on the edit button, the app won't update the list immediately. If I restart the app, I see changes. So how can I update the table with NSFetchedResultsController when I make changes to the selected record? Thanks.
Since you're using the NSFetchedResultsControllerDelegate, you need to handle (for your particular use case), the following cases for the NSFetchedResultsChangeType in your didChange method:
insert
delete
update
Your function should look something like this:
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch (type) {
case .insert:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break;
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break;
case .update:
tableView.reloadRows(at: [indexPath], with: .automatic)
break;
default:
print("...")
}
}
Need a hint, give up after spending several hours struggling with NSFetchedResultsController.
The error message is:
CoreData: error: NSFetchedResultsController: no object at index
2147483647 in section at index 0
...but I don't even know who is firing the error. The last piece of my code is saveContext(), the next breakpoint is inside didChange.
class ViewController : UITableViewController, NSFetchedResultsControllerDelegate
private lazy var channelController: NSFetchedResultsController<ZChannel> = {
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
let request: NSFetchRequest<ZChannel> = ZChannel.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "kit", ascending: true), NSSortDescriptor(key: "name", ascending: true)]
let retval: NSFetchedResultsController<ZChannel> = NSFetchedResultsController(fetchRequest: request,
managedObjectContext: appDelegate.persistentContainer.viewContext,
sectionNameKeyPath: "kit",
cacheName: nil)
retval.delegate = self
return retval
}()
public init() {
super.init(style: .grouped)
self.tableView.register(ChannelTableCell.self, forCellReuseIdentifier: "ChannelTableCell")
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.persistentContainer.viewContext.perform {
do {
try self.channelController.performFetch()
} catch {
let e = error as NSError
fatalError("[CoreData] Unresolved fetch error \(e), \(e.userInfo)")
}
}
}
public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch(type) {
case .insert:
self.tableView?.insertRows(at: [newIndexPath!], with: .bottom)
break;
case .update:
self.tableView?.reloadRows(at: [indexPath!], with: .bottom)
break
default:
break
}
}
public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
switch (type) {
case .insert:
self.tableView?.insertSections(IndexSet(integer: sectionIndex), with: UITableViewRowAnimation.bottom)
break
case .delete:
self.tableView?.deleteSections(IndexSet(integer: sectionIndex), with: UITableViewRowAnimation.bottom)
break
default:
break
}
}
From the other thread I insert new object:
self.persistentContainer.viewContext.performAndWait
{
// ...
let channel: ZChannel = NSEntityDescription.insertNewObject(forEntityName: "ZChannel", into: self.persistentContainer.viewContext) as! ZChannel
// ...
self.saveContext()
}
Some issues:
2147483647 is NSNotFound. If you are using something like indexOfObject and assuming it is in the array and then using that index that would be cause of the crash.
You are using indexPath for row update when you should use newIndexPath. The reason is that indexPath is the index before any inserts or delete and newIndexPath is the index after the inserts and deletes.
There is no reason to call self.channelController.performFetch() inside persistentContainer.viewContext.perform you are already on the main thread
I am using NSFetchedResultsController to populate my TableView. I have an entity "HubProfile" with attributes: "Name" & "HubID"
Issue: NSFetchedResultsController is coming nil. The weird part is that when I print fetchedResultsController in viewDidLoad & cellForRowIndexPath method -- it gives a value. But in the numberOfRowsInSection method, the fetchedResultsController is nil and the app crashes.
Also the data is already saved in CoreData. I have seen it in SQLite Browser -- so there is data to load
Can't seem to figure out why.
Below is my code:
class StudentsController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate
{
let managedContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultsController: NSFetchedResultsController!
override func viewDidLoad() {
super.viewDidLoad()
//FETCH REQUESTS
let fetchRequest = NSFetchRequest(entityName: "HubProfile")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.predicate = NSPredicate(format: "hubID = %#", hubID!)
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
do
{
try! fetchedResultsController.performFetch()
}
print(fetchedResultsController) //THIS IS NOT NIL
}
//TABLEVIEWDATASOURCE PROTOCOL:
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(fetchedResultsController) // THIS IS NIL
let sectionInfo = fetchedResultsController.sections![section] as NSFetchedResultsSectionInfo
return sectionInfo.numberOfObjects
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! StudentsCell!
print(fetchedResultsController) //THIS is NOT Nil
let hubProfile = fetchedResultsController.objectAtIndexPath(indexPath) as! HubProfile
cell.nameLabel?.text = hubProfile.name
return cell
}}
The tableView may have loaded before the fetchResultsController has had a chance to load up. So reload the table as soon as you performFetch() as so:
do
{
try! fetchedResultsController.performFetch()
tableView.reloadData()
}
print(fetchedResultsController) //THIS IS NOT NIL
}
Then in your tableView function check if the fetchedResultsController is nil, if it is then give it some other value (in this example, 0) and when the table reloads after the fetch is complete, the function will take the items from your fetchResultsController (when it is != nil):
override func numberOfSections(in tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
My app is quite simple: a TableViewController displays the content stored in Core Data. An add button pushes a second view modally which allows me to save stuff into Core Data. I am using NSFetchedResultsController to populate the main VC. If I save stuff to Core Data and present the main view controller again, my tableView doesn't refresh its data. I think it is because the delegate methods are not called. Any idea why?
import UIKit
import CoreData
class MainTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var managedContext: NSManagedObjectContext!
var fetchedResultsController: NSFetchedResultsController!
lazy var coreDataStack = CoreDataStack()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.setToolbarItems(toolBar().cons, animated: true)
self.navigationController?.setToolbarHidden(false, animated: true)
let fetchRequest = NSFetchRequest(entityName: "CDPodcast")
let sort = NSSortDescriptor(key: "artist", ascending: true)
fetchRequest.sortDescriptors = [sort]
managedContext = coreDataStack.context
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
var error: NSError? = nil
if (!fetchedResultsController.performFetch(&error)) {
println("Error: \(error?.localizedDescription)")
}
}
None of the below are called
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
println("change section")
}
func controllerWillChangeContent(controller: NSFetchedResultsController) {
tableView.reloadData()
println("begin updates")
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.reloadData()
println("end updates")
}
Edit: More code
func saveToCoreData(name: String, artist: String, summary: String, feedURL: String, artworkURL: String, date: NSDate, episodes: [MWFeedItem]) {
self.managedContext = coreDataStack.context
let podcastEntity = NSEntityDescription.entityForName("CDPodcast", inManagedObjectContext: self.managedContext)
currentPodcast = CDPodcast(entity: podcastEntity!, insertIntoManagedObjectContext: self.managedContext)
currentPodcast.name = name
currentPodcast.artist = artist
currentPodcast.summary = summary
currentPodcast.feedURL = feedURL
currentPodcast.artworkURL = artworkURL
currentPodcast.date = date
let episodesEntity = NSEntityDescription.entityForName("CDEpisode", inManagedObjectContext: self.managedContext)
for var i = 0; i < episodes.count; i++ {
let episodesToSave = CDEpisode(entity: episodesEntity!, insertIntoManagedObjectContext: self.managedContext)
var episode: AnyObject = currentPodcast.episode.mutableCopy() as! NSMutableOrderedSet
println("There are \(episodes.count) items in the episodes array")
episodesToSave.title = episodes[i].title
episodesToSave.downloadURL = episodes[i].enclosures[0].valueForKey("url") as! String
episodesToSave.showNotes = episodes[i].summary
episode.addObject(episodesToSave)
currentPodcast.episode = episode.copy() as! NSOrderedSet
}
self.coreDataStack.saveContext()
}
The issue with this was that my ManagedObjectContext wasn't being passed around properly. println(context) returned different memory addresses between the App Delegate and View Controller #3. I didn't find a proper way to fix this so I am just passing the context between view controllers. Having the right context fixed it.