Here is the full @interface and @implementation of the CoreDataFetchedResultsController Class. I am using a bridging header file for since it's in Objective-C. I've also included the .swift file that is a subclass. I am sorry for the impcomlete code in the code above.\
Thanks for the recommendation on the new Swift stuff. I'll be sure to take a look.
Take care,
Jon
.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
@interface CoreDataFetchedResultsViewController : UIViewController <NSFetchedResultsControllerDelegate, UITableViewDataSource, UITableViewDelegate>
/
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic, strong) UITableView *storyTableView;
@property (nonatomic, strong) UIRefreshControl *refreshControl;
/
@property (nonatomic) BOOL suspendAutomaticTrackingOfChangesInManagedObjectContext;
@property BOOL debug;
/
- (void) performFetch;
@end
.m
@interface CoreDataFetchedResultsViewController ()
@property (nonatomic) BOOL beganUpdates;
@end
@implementation CoreDataFetchedResultsViewController
@synthesize fetchedResultsController = _fetchedResultsController;
@synthesize suspendAutomaticTrackingOfChangesInManagedObjectContext = _suspendAutomaticTrackingOfChangesInManagedObjectContext;
@synthesize refreshControl;
@synthesize storyTableView;
/
/
#pragma mark - Fetching
- (void)performFetch
{
if (self.fetchedResultsController)
{
if (self.fetchedResultsController.fetchRequest.predicate)
{
if (self.debug) NSLog(@"[%@ %@] fetching %@ with predicate: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName, self.fetchedResultsController.fetchRequest.predicate);
} else
{
if (self.debug) NSLog(@"[%@ %@] fetching all %@ (i.e., no predicate)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName);
}
NSError *error;
BOOL success = [self.fetchedResultsController performFetch:&error];
if (!success) NSLog(@"[%@ %@] performFetch: failed", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
if (error) NSLog(@"[%@ %@] %@ (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]);
} else
{
if (self.debug) NSLog(@"[%@ %@] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
}
[self.storyTableView reloadData];
}
/
- (void)setFetchedResultsController:(NSFetchedResultsController *)newfrc
{
NSFetchedResultsController *oldfrc = _fetchedResultsController;
if (newfrc != oldfrc)
{
_fetchedResultsController = newfrc;
newfrc.delegate = self;
if ((!self.title || [self.title isEqualToString:oldfrc.fetchRequest.entity.name]) && (!self.navigationController || !self.navigationItem.title))
{
self.title = newfrc.fetchRequest.entity.name;
}
if (newfrc)
{
if (self.debug) NSLog(@"[%@ %@] %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), oldfrc ? @"updated" : @"set");
[self performFetch];
} else
{
if (self.debug) NSLog(@"[%@ %@] reset to nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
[self.storyTableView reloadData];
}
}
}
/
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
/
/
}
/
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
/NSInteger rows = 0;
if ([[self.fetchedResultsController sections] count] > 0)
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
rows = [sectionInfo numberOfObjects];
}
return rows;*/
/
/
}
/
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}
/
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
/
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [self.fetchedResultsController sectionIndexTitles];
}
/
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
{
[self.storyTableView beginUpdates];
self.beganUpdates = YES;
}
}
/
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.storyTableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.storyTableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationNone];
break;
case NSFetchedResultsChangeMove:
break;
case NSFetchedResultsChangeUpdate:
break;
}
}
}
/
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.storyTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.storyTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
break;
case NSFetchedResultsChangeUpdate:
[self.storyTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
[self.storyTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.storyTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
/
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
if (self.beganUpdates) [self.storyTableView endUpdates];
}
/
- (void)endSuspensionOfUpdatesDueToContextChanges
{
_suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;
}
/
- (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend
{
if (suspend) {
_suspendAutomaticTrackingOfChangesInManagedObjectContext = YES;
} else {
[self performSelector:@selector(endSuspensionOfUpdatesDueToContextChanges) withObject:0 afterDelay:0];
}
}
/
@end
.swift
import UIKit
import CoreData
class FLOViewController: CoreDataFetchedResultsViewController, FLODataHandlerDelegate, UITableViewDataSource, UITableViewDelegate
{
/
let kFLOCyclingURL = "http:/
let kTriathleteURL = "http:/
let kVeloNewsURL = "http:/
let kCyclingNewsURL = "http:/
let kRoadBikeActionURL = "http:/
let kIronmanURL = "http:/
var dataHandler : FLODataHandler?
var articleView : ArticleViewController?
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
lazy var articleContext: NSManagedObjectContext = self.appDelegate.managedObjectContext!
/
@IBOutlet weak var backgroundImage: UIImageView!
@IBOutlet var floViewControllerTableView: UITableView!
/
override func viewDidLoad()
{
super.viewDidLoad()
/
self.storyTableView = self.floViewControllerTableView
self.refreshControl = UIRefreshControl()
self.refreshControl!.addTarget(self, action: "refreshInvoked:state:", forControlEvents: UIControlEvents.ValueChanged)
/
println(self.storyTableView)
println(self.refreshControl)
self.storyTableView.addSubview(self.refreshControl!)
/
}
/
override func viewWillAppear(animated: Bool)
{
/
self.setUpFetchedResultsController()
}
/
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
/
func setUpFetchedResultsController()
{
var fetchRequest = NSFetchRequest(entityName: "Article")
var predicate = NSPredicate(format: "feed == 'FLO Cycling'")
fetchRequest.predicate = predicate
var sortDescriptors = NSSortDescriptor(key: "pubDate", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptors]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.articleContext, sectionNameKeyPath: nil, cacheName: nil)
}
/
@IBAction func back(sender: AnyObject)
{
/
self.navigationController?.popViewControllerAnimated(true)
}
/
@IBAction func markAllAsRead(sender: AnyObject)
{
/
let alert = UIAlertController(title: nil, message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)
/
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
alert.addAction(cancelAction)
/
let markAllAsReadAction = UIAlertAction(title: "Mark All As Read", style: UIAlertActionStyle.Default, handler: {(alert: UIAlertAction!)
in
let fetchRequest = NSFetchRequest(entityName: "Article")
let predicate = NSPredicate(format: "feed == 'FLO Cycling'")
fetchRequest.predicate = predicate
var fetchError : NSError?
let fetchedObjects = self.articleContext.executeFetchRequest(fetchRequest, error: &fetchError) as! [Article]
for element in fetchedObjects
{
if (element.theNewArticle.intValue == 1)
{
element.theNewArticle = NSNumber(int: 0)
}
var error : NSError?
if (self.articleContext.save(&error))
{
println(error!.localizedDescription)
}
}
})
alert.addAction(markAllAsReadAction)
/
self.presentViewController(alert, animated: true, completion: nil)
}
/
func refreshInvoked(sender: AnyObject, state: UIControlState)
/
{
let URLTempDictionary = ["FLO Cycling" : kFLOCyclingURL, "Triathlete" : kTriathleteURL, "Velo News" : kVeloNewsURL, "Cycling News" : kCyclingNewsURL, "Road Bike Action" : kRoadBikeActionURL, "Ironman" : kIronmanURL]
/
self.dataHandler = FLODataHandler(myDictionary: URLTempDictionary, myDelegate: self)
/
self.dataHandler!.startConnectionHandler()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("FloArticleCell", forIndexPath: indexPath) as! FloArticleCell
/
let selectedCellColor = UIView()
selectedCellColor.backgroundColor = UIColor(red: 0.0, green: 174.0/255.0, blue: 239.0/255, alpha: 0.8)
cell.selectedBackgroundView = selectedCellColor
/
/
cell.preservesSuperviewLayoutMargins = false
cell.layoutMargins = UIEdgeInsetsZero
cell.separatorInset = UIEdgeInsetsZero
/
var tempArticle = self.fetchedResultsController!.objectAtIndexPath(indexPath) as? Article
if tempArticle!.theNewArticle.intValue == 1
{
/
cell.articleTwoLineTitleLabel.text = ""
cell.articleTwoLineDateLabel.text = ""
/
/
var myLabel = UILabel(frame: CGRectMake(0,0,282,350))
myLabel.font = UIFont(name: "Avenir-Heavy", size: 14.0)
myLabel.numberOfLines = 0
myLabel.lineBreakMode = NSLineBreakByWordWrapping
myLabel.text = tempArticle!.title
var labelSize = myLabel.text!.boundingRectWithSize(
*/
/
cell.articleTwoLineTitleLabel.text = tempArticle!.title
/
var articleDateFormat = NSDateFormatter()
articleDateFormat.dateFormat = "EEE, MMM d yyyy, hh:mm aa"
var articleDateString = articleDateFormat.stringFromDate(tempArticle!.pubDate)
cell.articleTwoLineDateLabel.text = articleDateString
cell.articleDot.hidden = false
/var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss ZZ"
/
var tempDate = dateFormatter.dateFromString(tempString!)*/
}
else
{
/
cell.articleTwoLineTitleLabel.text = ""
cell.articleTwoLineDateLabel.text = ""
/
cell.articleTwoLineTitleLabel.text = tempArticle!.title
/
var articleDateFormat = NSDateFormatter()
articleDateFormat.dateFormat = "EEE, MMM d yyyy, hh:mm aa"
var articleDateString = articleDateFormat.stringFromDate(tempArticle!.pubDate)
cell.articleTwoLineDateLabel.text = articleDateString
cell.articleDot.hidden = true
}
/let tempArticle = self.fetchedResultsController.objectAtIndexPath(indexPath) as! Article
if tempArticle.theNewArticle.intValue == 1
{
}*/
return cell
}
/
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
/
let selectedArticle = self.fetchedResultsController!.objectAtIndexPath(indexPath) as? Article
/
selectedArticle!.theNewArticle = NSNumber(int: 0)
var error : NSError?
if (!self.articleContext.save(&error))
{
println(error)
}
/
self.articleView = self.storyboard!.instantiateViewControllerWithIdentifier("ArticleViewController") as? ArticleViewController
self.articleView!.currentArticle = selectedArticle!
self.articleView!.articleContext = self.articleContext
println("This is coming from didSelectRowAtIndexPath \(self.articleView!.currentArticle!.link)")
/
self.navigationController!.pushViewController(self.articleView!, animated: true)
/
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
/
func floDataHanlderDidFinishLoading(dataHandler : FLODataHandler) -> ()
{
/
self.refreshControl!.endRefreshing()
}
/
func floDataHandlerDidFailWithError(dataHandler : FLODataHandler, error : NSError) -> ()
{
}
}