Trouble with write to coredata iOS - ios

Please help me! when I press the record button in СoreData the following error:
This my code (AddViewController):
class AddPersonViewController: UIViewController {
var textFieldFirstName: UITextField!
var textFieldLastName: UITextField!
var textFieldAge: UITextField!
var barButtonAdd: UIBarButtonItem!
func createNewPerson(sender: AnyObject){
let appDelegate = UIApplication.sharedApplication().delegate
as AppDelegate
let managedObjectContext = appDelegate.managedObjectContext
let newPerson =
NSEntityDescription.insertNewObjectForEntityForName("Person",
inManagedObjectContext: managedObjectContext!) as? Person
if let person = newPerson{
person.firstName = textFieldFirstName.text
person.lastName = textFieldLastName.text
if let age = textFieldAge.text.toInt(){
person.age = age
} else {
person.age = 18
}
var savingError: NSError?
if managedObjectContext!.save(&savingError){
navigationController!.popViewControllerAnimated(true)
} else {
println("Failed to save the managed object context")
}
} else {
println("Failed to create the new person object")
}
}
override func viewDidLoad() {
super.viewDidLoad()
title = "New Person"
var textFieldRect = CGRect(x: 20,
y: 80,
width: view.bounds.size.width - 40,
height: 31)
textFieldFirstName = UITextField(frame: textFieldRect)
textFieldFirstName.placeholder = "First Name"
textFieldFirstName.borderStyle = .RoundedRect
textFieldFirstName.autoresizingMask = .FlexibleWidth
textFieldFirstName.contentVerticalAlignment = .Center
view.addSubview(textFieldFirstName)
textFieldRect.origin.y += 37
textFieldLastName = UITextField(frame: textFieldRect)
textFieldLastName.placeholder = "Last Name"
textFieldLastName.borderStyle = .RoundedRect
textFieldLastName.autoresizingMask = .FlexibleWidth
textFieldLastName.contentVerticalAlignment = .Center
view.addSubview(textFieldLastName)
textFieldRect.origin.y += 37
textFieldAge = UITextField(frame: textFieldRect)
textFieldAge.placeholder = "Age"
textFieldAge.borderStyle = .RoundedRect
textFieldAge.autoresizingMask = .FlexibleWidth
textFieldAge.keyboardType = .NumberPad
textFieldAge.contentVerticalAlignment = .Center
view.addSubview(textFieldAge)
barButtonAdd = UIBarButtonItem(title: "Add",
style: .Plain,
target: self,
action: "createNewPerson:")
navigationItem.rightBarButtonItem = barButtonAdd
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
textFieldFirstName.becomeFirstResponder()
}
}
TablePerson:
class PersonsListTableViewController: UITableViewController,
NSFetchedResultsControllerDelegate {
struct TableViewConstants{
static let cellIdentifier = "Cell"
}
var barButtonAddPerson: UIBarButtonItem!
var frc: NSFetchedResultsController!
var managedObjectContext: NSManagedObjectContext?{
return (UIApplication.sharedApplication().delegate
as AppDelegate).managedObjectContext
}
func addNewPerson(sender: AnyObject){
/* This is a custom segue identifier that we have defined in our
storyboard that simply does a "Show" segue from our view controller
to the "Add New Person" view controller */
performSegueWithIdentifier("addPerson", sender: nil)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
barButtonAddPerson = UIBarButtonItem(barButtonSystemItem: .Add,
target: self,
action: "addNewPerson:")
}
func controllerWillChangeContent(controller: NSFetchedResultsController!) {
tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController!,
didChangeObject anObject: AnyObject!,
atIndexPath indexPath: NSIndexPath!,
forChangeType type: NSFetchedResultsChangeType,
newIndexPath: NSIndexPath!) {
if type == .Delete{
tableView.deleteRowsAtIndexPaths([indexPath],
withRowAnimation: .Automatic)
}
else if type == .Insert{
tableView.insertRowsAtIndexPaths([newIndexPath],
withRowAnimation: .Automatic)
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController!) {
tableView.endUpdates()
}
override func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
let sectionInfo = frc.sections![section] as NSFetchedResultsSectionInfo
return sectionInfo.numberOfObjects
}
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier(
TableViewConstants.cellIdentifier,
forIndexPath: indexPath) as UITableViewCell
let person = frc.objectAtIndexPath(indexPath) as Person
cell.textLabel.text = person.firstName + " " + person.lastName
cell.detailTextLabel!.text = "Age: \(person.age)"
return cell
}
override func setEditing(editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if editing{
navigationItem.setRightBarButtonItem(nil, animated: true)
} else {
navigationItem.setRightBarButtonItem(barButtonAddPerson, animated: true)
}
}
override func tableView(tableView: UITableView,
commitEditingStyle editingStyle: UITableViewCellEditingStyle,
forRowAtIndexPath indexPath: NSIndexPath){
let personToDelete = self.frc.objectAtIndexPath(indexPath) as Person
managedObjectContext!.deleteObject(personToDelete)
if personToDelete.deleted{
var savingError: NSError?
if managedObjectContext!.save(&savingError){
println("Successfully deleted the object")
} else {
if let error = savingError{
println("Failed to save the context with error = \(error)")
}
}
}
}
override func tableView(tableView: UITableView,
editingStyleForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCellEditingStyle {
return .Delete
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Persons"
navigationItem.leftBarButtonItem = editButtonItem()
navigationItem.rightBarButtonItem = barButtonAddPerson
/* Create the fetch request first */
let fetchRequest = NSFetchRequest(entityName: "Person")
let ageSort = NSSortDescriptor(key: "age", ascending: true)
let firstNameSort = NSSortDescriptor(key: "firstName", ascending: true)
fetchRequest.sortDescriptors = [ageSort, firstNameSort]
frc = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: managedObjectContext!,
sectionNameKeyPath: nil,
cacheName: nil)
frc.delegate = self
var fetchingError: NSError?
if frc.performFetch(&fetchingError){
println("Successfully fetched")
} else {
println("Failed to fetch")
}
}
}
and Person.swift:
#objc(Person) class Person: NSManagedObject {
#NSManaged var age: NSNumber
#NSManaged var firstName: String
#NSManaged var lastName: String
}
Everything seems to be fine, but I keep getting an error so that it was not possible to write data: "Failed to create the new person object"
this is link to my Xcode project:
download
I don't understand why I keep getting ab error. Please help!

"Failed to create the new person object" being printed means newPerson is nil. A good reason for that could be that your managedObjectContext is nil. Could you check and report back?

I find solution. I xcdatamodeld does not have a class in which the variables were described by Person. Thank you for your answers. And sorry for my stupidity.

Related

Swift Xcode 13 Programmatic UITableViewController nil delegate

The Delegated function fires but crashes as its nil, the objects in the items array is populated by CoreData, this var model: CoreDataModel = CoreDataModel(CoreDataController.shared) has to be instantiated rather than as expected in the viewDidLoad to prevent a nil error for the table view row count (model.items.count)
On startup the items array is the complete Sqlite DB, on search its the subset of the table and printing to the console proves the array is changed and only has the subset of Albums.
BaseViewController
import UIKit
import CoreData
protocol UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
}
protocol MasterModel {
var client: LastFMClient { get }
func searchFeed(with userSearchTerm: String?, completion: #escaping (Bool) -> Void)
}
protocol DataReloadTableViewDelegate: class {
func reloadAlbumsTable()
}
class BaseViewController: UITableViewController, MasterModel {
let cellId = "sdlfjowieurewfn3489844224947824dslaksjfs;ad"
let logoContainer = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
let image = UIImage(named: "lastFMRedBlack")
let searchBar = UISearchBar()
let client = LastFMClient()
var model: CoreDataModel = CoreDataModel(CoreDataController.shared)
private var searchResults: Root?
override func viewDidLoad() {
super.viewDidLoad()
setupSearchController()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
tableView.register(SubtitleTableViewCell.self, forCellReuseIdentifier: cellId)
tableView.tableFooterView = UIView(frame: CGRect.zero)
tableView.separatorColor = UIColor(red: 72.5/255, green: 0/255, blue: 0/255, alpha: 1)
imageView.contentMode = .scaleAspectFit
imageView.image = image
logoContainer.addSubview(imageView)
navigationItem.titleView = logoContainer
print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask))
model.delegate = self
model.fetchAllAlbums()
}
// MARK - SearchBar
private func setupSearchController() {
searchBar.sizeToFit()
searchBar.placeholder = "Search for Album"
searchBar.delegate = self
showSearchBarButton(shouldShow: true)
}
func showSearchBarButton (shouldShow: Bool) {
if shouldShow {
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(handleShowSearchBar))
} else {
searchBar.showsCancelButton = true
navigationItem.rightBarButtonItem = nil
}
}
func search(shouldShow: Bool) {
showSearchBarButton(shouldShow: !shouldShow)
navigationItem.titleView = shouldShow ? searchBar : logoContainer
}
#objc func handleShowSearchBar(){
search(shouldShow: true)
searchBar.becomeFirstResponder()
}
// MARK - API Request
func searchFeed(with userSearchTerm: String?, completion: #escaping (Bool) -> Void) {
// Use the API to get data
client.getFeed(from: LastFMRequest.albumSearch(userSearchTerm: userSearchTerm) ) { result in
switch result {
case .success(let data):
do {
let data = try DataParser.parse(data, type: RootNode.self)
self.searchResults = data.results
completion(true)
} catch {
print(error.localizedDescription)
completion(false)
}
case .failure(let error):
print(error.localizedDescription)
completion(false)
}
}
}
}
extension BaseViewController: UISearchBarDelegate {
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searchBar.text = nil
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
search(shouldShow: false)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let searchTextString = searchBar.text else { return }
searchFeed(with: searchTextString.replacingOccurrences(of: " ", with: "+").lowercased(), completion: {_ in
if self.searchResults!.albumMatches.album.count == 0 {
DispatchQueue.main.async {
let alertController = UIAlertController(title: "No Albums Found", message: "Try Another Keyword(s)", preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default) { action in
print("Pressed OK")
}
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
} else {
let dataManager = DataManager(data: self.searchResults!)
do {
try dataManager.saveData()
} catch {
print(error)
}
}
})
search(shouldShow: false)
searchBar.resignFirstResponder()
}
}
class SubtitleTableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension BaseViewController: UITableViewDataSource {
var numberOrSections: Int { return 1 }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard section >= 0 && section < numberOrSections else { return 0 }
return model.items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let albumItem = model.items[indexPath.row]
cell.textLabel?.text = albumItem.value(forKeyPath: "name") as? String
cell.detailTextLabel?.text = albumItem.value(forKeyPath: "artist") as? String
cell.accessoryType = .disclosureIndicator
// Populate the cell from the object
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = DetailViewController()
let albumItem = model.items[indexPath.row]
vc.iamgeURL = albumItem.value(forKeyPath: "imageUrl") as? String
vc.albumName = albumItem.value(forKeyPath: "name") as? String
navigationController?.pushViewController(vc, animated: true)
}
}
extension BaseViewController: DataReloadTableViewDelegate {
func reloadAlbumsTable(){
DispatchQueue.main.async {
print(self.model.items.count)
self.tableView.reloadData()
}
}
}
CoreDataModel
import Foundation
import CoreData
class CoreDataModel {
weak var delegate: DataReloadTableViewDelegate?
let coreDataController: CoreDataController
var items:[Albums] = []
init(_ coreDataController: CoreDataController) {
self.coreDataController = coreDataController
self.coreDataController.mainContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}
internal func saveSearchAlbums(responseData: Root) throws {
let newSearch = Searches(context: coreDataController.mainContext)
newSearch.searchQuery = responseData.attr.forField
for (_, element) in responseData.albumMatches.album.enumerated() {
let newAlbum = Albums(context: coreDataController.mainContext)
let artistName = element.artist
let albumName = element.name
let imageUrlTwo = element.image[2].text
let imageUrlZero = element.image[0].text
let imageUrlOne = element.image[1].text
var imageUrl: String = ""
if !JustLetters.blank(text: imageUrlTwo) {
imageUrl = imageUrlTwo
}
if !JustLetters.blank(text: imageUrlZero) {
imageUrl = imageUrlZero
}
if !JustLetters.blank(text: imageUrlOne) {
imageUrl = imageUrlOne
}
if !JustLetters.blank(text: artistName) && !JustLetters.blank(text: albumName) && !JustLetters.blank(text: imageUrl) {
newAlbum.searches = newSearch
newAlbum.artist = artistName
newAlbum.name = albumName
newAlbum.imageUrl = imageUrl
newSearch.addToAlbums(newAlbum)
}
}
// Save context
coreDataController.saveContext()
fetchAlbumsByKeyword(searchTerm: responseData.attr.forField)
}
internal func fetchAlbumsByKeyword(searchTerm: String) {
// Create Fetch Request
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Albums")
// Add Sort Descriptor
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
// Add Predicate
let predicate = NSPredicate(format: "name CONTAINS[c] %#", searchTerm)
fetchRequest.predicate = predicate
do {
items = try coreDataController.mainContext.fetch(fetchRequest) as! [Albums]
} catch {
print(error)
}
delegate!.reloadAlbumsTable()
}
internal func fetchAllAlbums() {
// Create the FetchRequest for all searches
let allAlbums: NSFetchRequest = Albums.fetchRequest()
do {
items = try coreDataController.mainContext.fetch(allAlbums)
} catch {
print(error)
}
}
}
Delegate is assigned/set on the class name and not on any instance identifier so a delegate can only be set on a class with one instance
I am unable to show specific proof, I rely on cause and effect of a single change to make the above statement.
I had more than one instance of CoreDataModel, I set the delegate on the first instance in the viewDidLoad, the second instance is set on the search click. I refactored out the DataManager Class which itself creates and instance of CoreDataModel.
The final result is the delegate is not nil and performs as expected. Repo 'show_album_search_results' branch

Appending Values Consistently in Swift

EDIT: Just changing the code to show my attempt at the Singleton suggestion.
So I am attempting to create an application which would take the selection of a user from a UITableView and pass that to another UITableView. Then, it would be appended to the array in that view and presented as the new table. The idea being that users can select from multiple lists and create one list made of all their selections.
However, I am extremely new to iOS development and while I can get it to let me take the selected value and show it in the new UITableView, it only sends the one value and does not keep or append it. Meaning I can never show multiple additions to the list.
So, what I'm getting atm is the ability to select a cell, let's say "Kevin Smith", and that value gets sent to the new UITableView and shown to the user. But if I go and select another value, "John Smith", then only John shows up and Kevin is gone.
Here is my three controllers involved:
The first UITAbleView Controller
class PlayerViewController: UITableViewController {
var resultsController: NSFetchedResultsController<Player>!
let CDSPlayer = coreDataStackPlayer()
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsMultipleSelection = true
let request: NSFetchRequest<Player> = Player.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "level", ascending: true)
request.sortDescriptors = [sortDescriptor]
resultsController = NSFetchedResultsController(
fetchRequest: request,
managedObjectContext: CDSPlayer.managedContext,
sectionNameKeyPath: nil,
cacheName: nil
)
resultsController.delegate = self
do{
try resultsController.performFetch()
} catch {
print("Perform Fetch Error: \(error)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return resultsController.sections?[section].numberOfObjects ?? 0
}
override func tableView(_ _tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath)
let selectedIndexPaths = tableView.indexPathsForSelectedRows
let rowIsSelected = selectedIndexPaths != nil && selectedIndexPaths!.contains(indexPath)
cell.accessoryType = rowIsSelected ? .checkmark : .none
let player = resultsController.object(at: indexPath)
cell.textLabel?.text = player.name
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "player2form", sender: tableView.cellForRow(at: indexPath))
}
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(style:.destructive, title: "Delete"){(action, view, completion) in
let player = self.resultsController.object(at: indexPath)
self.resultsController.managedObjectContext.delete(player)
do {
try self.resultsController.managedObjectContext.save()
self.errorMessage(message: "Player Character Deleted.");
completion(true)
} catch {
print("Delete Failed: \(error)")
self.errorMessage(message: "Failed to Delete Player Character.");
completion(false)
}
}
action.image = UIImage(named: "trash")
action.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [action])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if let _ = sender as? UIBarButtonItem, let vc = segue.destination as? AddPlayerViewController{
vc.managedContext = resultsController.managedObjectContext
}
if let cell = sender as? UITableViewCell, let vc = segue.destination as? AddPlayerViewController{
vc.managedContext = resultsController.managedObjectContext
if let indexPath = tableView.indexPath(for: cell){
let player = resultsController.object(at: indexPath)
Service.shared.allPlayers.append(player)
vc.player = player
}
}
}
//Error Function
func errorMessage(message:String){
let alert = UIAlertController(title: "Alert", message: message, preferredStyle:UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title:"Ok", style:UIAlertActionStyle.default, handler:nil);
alert.addAction(okAction);
self.present(alert, animated: true, completion:nil);
}
}
extension PlayerViewController: 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: .automatic)
}
case .delete:
if let indexPath = indexPath{
tableView.deleteRows(at: [indexPath], with: .automatic)
}
case .update:
if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath){
let player = resultsController.object(at: indexPath)
cell.textLabel?.text = player.name
}
default:
break
}
}
The Selection view that passes the data:
class AddPlayerViewController: UIViewController {
var managedContext: NSManagedObjectContext!
var player: Player?
var selectedItems = [String]()
#IBOutlet weak var done_btn: UIButton!
#IBOutlet weak var playerInput: UITextField!
#IBOutlet weak var cancel_btn: UIButton!
#IBAction func add2combat(_ sender: UIButton) {
self.performSegue(withIdentifier: "player2combat", sender: self)
}
#IBAction func done(_ sender: UIButton) {
guard let name = playerInput.text, !name.isEmpty else {
return //Add notice user cannot save empty items
}
if let player = self.player {
player.name = name
} else {
//Set values from input to the cell
let player = Player(context: managedContext)
player.name = name
}
do {
try managedContext.save()
playerInput.resignFirstResponder()
dismiss(animated: true)
} catch {
print("Error Saving Player Character: \(error)")
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "player2combat" {
let selectedItems : [String] = [playerInput.text!]
let otherVc = segue.destination as! CombatSceneViewController
otherVc.selectedItems = selectedItems
print(selectedItems)
}
}
#IBAction func cancel(_ sender: UIButton) {
playerInput.resignFirstResponder()
dismiss(animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
playerInput.becomeFirstResponder()
if let player = player {
playerInput.text = player.name
playerInput.text = player.name
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
And the UITableView that gets the data and tries to append it to the existing Array.
class CombatSceneViewController: UITableViewController{
var selectedItems = Service.shared.allSelectedItems
override func viewDidLoad() {
super.viewDidLoad()
let otherVC = AddPlayerViewController()
selectedItems.append(contentsOf: otherVC.selectedItems)
saveData()
loadData()
print(selectedItems)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return selectedItems.count
}
override func tableView(_ _tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "CombatCell", for: indexPath as IndexPath)
cell.textLabel?.text = selectedItems[indexPath.item]
return cell
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableView.reloadData()
}
func saveData() {
let data = NSMutableData()
// 1
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let path = paths[0]
let file = (path as NSString).appendingPathComponent("Persistent.plist")
//2
let archiver = NSKeyedArchiver(forWritingWith: data)
archiver.encode(selectedItems, forKey: "Agents")
archiver.finishEncoding()
data.write(toFile: file, atomically: true)
}
func loadData() {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let path = paths[0]
let file = (path as NSString).appendingPathComponent("Persistent.plist")
// 1
if FileManager.default.fileExists(atPath: file) {
if let data = NSData(contentsOfFile: file) {
let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)
selectedItems = unarchiver.decodeObject(forKey: "Agents") as! [String]
unarchiver.finishDecoding()
}
}
}
Singleton Class:
class Service {
static let shared = Service()
var allPlayers = [Player]()
var allSelectedItems = [String]()
}
I hope I did the formatting right... this is my first post on here, be gentle^^;.
The problem is that when you go back the old player is gone because you don't keep it , you can try to create a singleton for that
class Service {
static let shared = Service()
var allPlayers = [Player]()
var allSelectedItems = [String]()
}
// keep it here
if let cell = sender as? UITableViewCell, let vc = segue.destination as? AddPlayerViewController{
vc.managedContext = resultsController.managedObjectContext
if let indexPath = tableView.indexPath(for: cell){
let player = resultsController.object(at: indexPath)
Service.shared.allPlayers.append(player) // add this line
vc.player = player
}
}
Do same logic to append selected items and when you reach final tableView access them with
Service.shared.allPlayers
Or
Service.shared.selectedItems
That way you have a persisted container for all the selected players & items accessible anyWhere inside the app

If sectionNameKeyPath is not nil I cannot start my app

Whenever I try to run my application when my sectionNameKeyPath is not nil, it crashes with the error
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'
but whenever I set that to nil it works and I add some data, quit the app, set the sectionNameKeyPath back to my Core Data attribute, it now works just fine! This is the 7th hour today I'm fighting with this bug and I just can't solve it. What is wrong in my code?
//
// ViewController.swift
// Expense Manager
//
// Created by Andrei Vataselu on 10/3/17.
// Copyright © 2017 Andrei Vataselu. All rights reserved.
//
import UIKit
import SideMenu
import CoreData
import SwipeCellKit
let green = UIColor(red:0.00, green:0.62, blue:0.45, alpha:1.0)
let red = UIColor(red:0.95, green:0.34, blue:0.34, alpha:1.0)
let appDelegate = UIApplication.shared.delegate as? AppDelegate
var userMoney : [UserMoney] = []
var managedObjectContext: NSManagedObjectContext? = appDelegate?.persistentContainer.viewContext
class ViewController: UIViewController, NSFetchedResultsControllerDelegate {
#IBOutlet weak var sumTextField: UITextField!
#IBOutlet weak var userBudgetLabel: UILabel!
#IBOutlet var tap: UITapGestureRecognizer!
#IBOutlet weak var topView: UIView!
#IBOutlet weak var plusButton: UIButton!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var moreBtn: UIButton!
func userBudgetCount(_ section: Int) -> Int{
return fetchedResultsController.sections![section].numberOfObjects
}
func getUserBudgetAtIndexPath(indexPath : IndexPath) -> Budget {
return fetchedResultsController.object(at: indexPath) as Budget
}
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboard()
tableView.delegate = self
tableView.dataSource = self
self.tableView.tableFooterView = UIView()
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchCoreDataObject()
}
func fetchCoreDataObject() {
self.fetch { (complete) in
if complete {
if userBudgetCount(0) >= 1 {
userBudgetLabel.text = replaceLabel(number: userMoney[userMoney.count - 1].userMoney)
tableView.isHidden = false
plusButton.isHidden = false
moreBtn.isHidden = false
} else {
tableView.isHidden = true
userBudgetLabel.text = "Bugetul tau"
plusButton.isHidden = true
moreBtn.isHidden = true
}
}
}
}
var fetchedResultsController: NSFetchedResultsController<Budget> {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest = NSFetchRequest<Budget>(entityName: "Budget")
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "dateSubmitted" , 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: managedObjectContext!, sectionNameKeyPath: "dateSection", cacheName: nil)
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
do {
try _fetchedResultsController!.performFetch()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController<Budget>? = nil
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func initialAddButtonPressed(_ sender: Any) {
if sumTextField.text != "" {
self.saveMoney(userMoney: (sumTextField.text! as NSString).doubleValue, completion: { (complete) in
})
self.save(sumText: sumTextField.text! , dataDescription: "Buget initial", dataColor: green) {
complete in
if complete {
tableView.isHidden = false
}
}
userBudgetLabel.text = "\(sumTextField.text!) RON"
self.fetchCoreDataObject()
tableView.reloadData()
} else {
sumInvalidAlert()
}
self.dismissKeyboard()
sumTextField.text = ""
}
#IBAction func plusButtonPressed(_ sender: Any) {
let plusController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let addBudgetAction = UIAlertAction(title: "Adauga buget", style: .default) {
(action) -> Void in
guard let createAddBudgetVC = self.storyboard?.instantiateViewController(withIdentifier: "AddBudgetVC") else { return }
self.presentViewController(createAddBudgetVC)
}
let addExpenseAction = UIAlertAction(title: "Adauga plata", style: .default) {
(action) -> Void in
guard let createAddExpenseVC = self.storyboard?.instantiateViewController(withIdentifier: "AddExpenseVC") else { return }
self.presentViewController(createAddExpenseVC)
}
let cancelAction = UIAlertAction(title: "Anuleaza", style: .cancel, handler: nil)
plusController.addAction(addBudgetAction)
plusController.addAction(addExpenseAction)
plusController.addAction(cancelAction)
present(plusController, animated: true, completion: nil)
}
#IBAction func moreButtonPressed(_ sender: Any) {
}
}
extension ViewController {
func fetch(completion: (_ complete: Bool) -> ()){
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
let fetchMoneyRequest = NSFetchRequest<UserMoney>(entityName: "UserMoney")
do{
userMoney = try managedContext.fetch(fetchMoneyRequest)
print("success")
completion(true)
} catch {
debugPrint("Could not fetch \(error.localizedDescription)")
completion(false)
}
}
func removeCell(atIndexPath indexPath: IndexPath){
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
do {
try managedContext.save()
debugPrint("removeCell CONTEXT SAVED")
} catch {
debugPrint("removeCell CONTEXT NOT SAVED \(error.localizedDescription)")
}
}
func cancelCell(color: UIColor, atIndexPath indexPath: IndexPath){
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
if color.description == green.description {
// scade buget
userMoney[userMoney.count - 1].userMoney -= (getUserBudgetAtIndexPath(indexPath: indexPath).dataSum! as NSString).doubleValue
} else {
userMoney[userMoney.count - 1].userMoney += (getUserBudgetAtIndexPath(indexPath: indexPath).dataSum! as NSString).doubleValue
}
do {
try managedContext.save()
} catch {
print("cancelCell Managed Context Saving ERROR: \(error.localizedDescription)")
}
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource,SwipeTableViewCellDelegate {
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
guard orientation == .right else {
let cancelAction = SwipeAction(style: .default, title: "Anuleaza"){
(action, indexPath)
in
self.cancelCell(color: self.getUserBudgetAtIndexPath(indexPath: indexPath).dataColor as! UIColor, atIndexPath: indexPath)
self.removeCell(atIndexPath: indexPath)
self.fetchCoreDataObject()
tableView.deleteRows(at: [indexPath], with: .automatic)
}
cancelAction.backgroundColor = UIColor(red:0.16, green:0.63, blue:0.74, alpha:1.0)
return [cancelAction]
}
let deleteAction = SwipeAction(style: .destructive, title: "Sterge") { (action, indexPath) in
self.removeCell(atIndexPath: indexPath)
self.fetchCoreDataObject()
tableView.deleteRows(at: [indexPath], with: .automatic)
}
deleteAction.backgroundColor = red
return [deleteAction]
}
func numberOfSections(in tableView: UITableView) -> Int {
if let sections = fetchedResultsController.sections {
return sections.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "expenseCell") as? ExpenseCell else { return UITableViewCell() }
print("indexPathRow: \(indexPath.row) | indexPathSection: \(indexPath.section)")
let budget = fetchedResultsController.object(at: indexPath) as Budget
cell.delegate = self
cell.configureCell(budget: budget)
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return UITableViewCellEditingStyle.none
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let sections = fetchedResultsController.sections {
let currentSection = sections[section]
return currentSection.numberOfObjects
}
return 0
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if let sections = fetchedResultsController.sections {
let currentSections = sections[section]
return currentSections.name
}
return nil
}
}

Duplicated messages Using JSQMessagerViewController (Swift) (iOS)

I have a JSQMessagersViewController which is connected to a NavigationController. Moreover, I have a ViewController with a tableView with the contacts. Thus, when you click on a cell from the tableView, it opens the JSQMessagerViewController with that conversation. Inside the conversation, when the user sends a message the first time everything works fine. However, once the user goes out the conversation to the tableViewController, if he gets back in a conversation and sends a message, the message gets duplicated by the number of time the user when out and back in. In addition, once the message is duplicated and the user goes out of the conversation, once he click on the same conversation, the message is no longer duplicated. Indeed, when I verify on the database (Firebase), there is no duplicated messages. I can't figure out what is creating this loop.
MessageReceivedDelegate.swift
import Foundation
import Firebase
protocol MessageReceivedDelegate: class {
func message_received(senderID: String, senderName: String, text: String, target: String)
}
class messages_help {
private static let _instance = messages_help()
weak var delegate: MessageReceivedDelegate?
private var currentTarget = String()
static var Instance: messages_help {
return _instance
}
func sendMessage(senderID: String, senderName: String, text: String, target: String) {
let ref = FIRDatabase.database().reference(fromURL: )
let data: Dictionary <String, Any> = [Constants.sender_id: senderID, Constants.sender_name: senderName, Constants.text: text, Constants.target: target]
ref.child("messages").childByAutoId().setValue(data)
}
func getData() {
let ref = FIRDatabase.database().reference(fromURL: )
let user = FIRAuth.auth()?.currentUser
let curr = ref.child("messages")
curr.observe(.childAdded, with: {(snapshot) in
//print(snapshot)
//Get Value from DataBase
print()
if let data = snapshot.value as? NSDictionary {
if let senderID = data[Constants.sender_id] as? String{
if let senderName = data[Constants.sender_name] as? String {
if let target = data[Constants.target] as? String{
if user?.uid == senderID || user?.email == target{
if let text = data[Constants.text] as? String {
self.delegate?.message_received(senderID: senderID, senderName: senderName, text: text, target: target)
}
}
}
}
}
}
},withCancel: nil)
}
}
}
ChatViewController.swift
import UIKit
import JSQMessagesViewController
import MobileCoreServices
import AVKit
import FirebaseAuth
class ChatViewController: JSQMessagesViewController,
MessageReceivedDelegate {
#IBOutlet var targetLabel: UINavigationItem!
var messages = [JSQMessage]()
#IBOutlet var chatLabel: UINavigationItem!
var i = 0
var targetEmail = String()
override func viewDidLoad() {
super.viewDidLoad()
let user = FIRAuth.auth()?.currentUser
messages_help.Instance.delegate = self
self.senderId = user?.uid
self.senderDisplayName = user?.email
self.targetLabel.title = targetEmail
setupBackButton()
// Show Button to simulate incoming messages
self.inputToolbar.contentView.leftBarButtonItem = nil
if Language().check_language() == "Fr"{
self.inputToolbar.contentView.textView.placeHolder = "Nouveau message";
}
else if Language().check_language() == "Es"{
self.inputToolbar.contentView.textView.placeHolder = "Nuevo mensaje";
}
self.inputToolbar.contentView.rightBarButtonItem.setImage(UIImage(named: "paper_plane"), for: .normal)
self.inputToolbar.contentView.rightBarButtonItem.setTitleColor(UIColor.brown, for: .normal)
automaticallyScrollsToMostRecentMessage = true
//self.collectionView?.reloadData()
//messages_help.Instance.getData()
// Do any additional setup after loading the view.
messages.removeAll()
messages_help.Instance.getData()
}
func go() {
let toViewController = storyboard?.instantiateViewController(withIdentifier: "previous") as! PreviousRequestsViewController
//Go to the page
self.present(toViewController, animated:true, completion: nil)
}
//message received
func message_received(senderID: String, senderName: String, text: String, target: String) {
if target == targetEmail || senderName == targetEmail {
//print("Number of loop\n")
//i = i+1
// print(i)
/**
* Scroll to actually view the indicator
*/
self.scrollToBottom(animated: true)
messages.append(JSQMessage(senderId: senderID, displayName: senderName, text: text))
}
collectionView.reloadData()
}
// Number of rows
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
// Cell
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell
return cell
}
// Display messages
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageDataForItemAt indexPath: IndexPath!) -> JSQMessageData! {
return messages[indexPath.item]
}
// Avatar
override func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {
return JSQMessagesAvatarImageFactory.avatarImage(with: UIImage(named: "iTunesArtwork"), diameter: 30)
}
//the bubble
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! {
let bubble_fact = JSQMessagesBubbleImageFactory()
let message = messages[indexPath.item]
if message.senderId == self.senderId {
return bubble_fact?.outgoingMessagesBubbleImage(with: UIColor.brown)
}
else {
return bubble_fact?.incomingMessagesBubbleImage(with: UIColor.darkGray)
}
}
// When pressed sent
override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) {
messages_help.Instance.sendMessage(senderID: senderId, senderName: senderDisplayName, text: text, target: self.targetEmail )
//dismiss text from text fild
finishSendingMessage()
}
func setupBackButton() {
if Language().check_language() == "Fr"{
let backButton = UIBarButtonItem(title: "Retour", style: UIBarButtonItemStyle.plain, target: self, action: #selector(backButtonTapped))
backButton.tintColor = UIColor.brown
navigationItem.leftBarButtonItem = backButton
}
else if Language().check_language() == "Es"{
let backButton = UIBarButtonItem(title: "Regresa", style: UIBarButtonItemStyle.plain, target: self, action: #selector(backButtonTapped))
backButton.tintColor = UIColor.brown
navigationItem.leftBarButtonItem = backButton
} else {
let backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.plain, target: self, action: #selector(backButtonTapped))
backButton.tintColor = UIColor.brown
navigationItem.leftBarButtonItem = backButton
}
}
func backButtonTapped() {
self.dismiss(animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
}
ContactChatViewController.swift
import UIKit
import Firebase
class ContactChatViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
#IBOutlet var chat_TableView: UITableView!
var ref: FIRDatabaseReference!
var publish = [String?]()
var data_to_send = String()
var encountered = Set<String>()
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference(fromURL: )
getDataa()
}
// get data from trips
func getDataa() {
// Reference to current user
let user = FIRAuth.auth()?.currentUser
let curr = ref.child("messages")
curr.observe(.childAdded, with: {(snapshot) in
if let data = snapshot.value as? [String: AnyObject] {
let use = messages()
use.setValuesForKeys(data)
//Append new data if same user
if user?.email == use.target || user?.email == use.sender_name {
if self.encountered.contains(use.sender_name!) || (user?.email)! == use.sender_name! {
//print("Already inside")
} else {
self.encountered.insert(use.sender_name!)
self.publish.append(use.sender_name)
}
//Appen to to array even if current user sent a message but is not the target
if user?.email == use.sender_name{
if self.encountered.contains(use.target!) {
print("Already inside")
} else {
self.encountered.insert(use.target!)
self.publish.append(use.target)
}
}
}
//Load data in U Thread
DispatchQueue.main.async {
self.chat_TableView.reloadData()
}
}
}
,withCancel: nil)
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
// hide separators
tableView.separatorStyle = .none
//return count with database
return publish.count
}
// Assign rows a value
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell_user = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "previous_cell")
cell_user.selectionStyle = .none
cell_user.backgroundColor = UIColor.clear
cell_user.textLabel?.textColor = UIColor.brown
cell_user.textLabel?.font = UIFont.systemFont(ofSize: 25)
// Check for duplicates
cell_user.textLabel?.text = self.publish[indexPath.row]
return cell_user
}
//Clicked on a cell
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
data_to_send = self.publish[indexPath.row]!
self.performSegue(withIdentifier: "chatSegue", sender: self)
//let chatView = ChatViewController()
//chatView.targetEmail = self.publish[indexPath.row]!
//let chatNavigationController = UINavigationController(rootViewController: chatView)
//present(chatNavigationController, animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//let toViewController = storyboard?.instantiateViewController(withIdentifier: "chatPage") as! ChatViewController
//toViewController.targetEmail = data_to_send
//Go to the page
//self.present(toViewController, animated:true, completion: nil)
let navVC = segue.destination as? UINavigationController
let chatVC = navVC?.viewControllers.first as! ChatViewController
chatVC.targetEmail = data_to_send
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Doesn't save editing text in UITextView

I have to ViewControllers and two classes MasterViewController.swift and LoginViewController.swift. In first class I have tableView in which you can add record and it open new ViewController with class DetailViewController.swift in with is textView in which I can add text and when I go back to tableView it must save my text, but when I do it it doesn't save. This is cod from MasterViewController:
import UIKit
import CoreData
class MasterViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate {
#IBOutlet var tableView: UITableView!
var isAuthenticated = false
var managedObjectContext: NSManagedObjectContext? = nil
var _fetchedResultsController: NSFetchedResultsController? = nil
var didReturnFromBackground = false
override func awakeFromNib() {
super.awakeFromNib()
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItem = self.editButtonItem()
view.alpha = 0
let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
self.navigationItem.rightBarButtonItem = addButton
NSNotificationCenter.defaultCenter().addObserver(self, selector: "appWillResignActive:", name: UIApplicationWillResignActiveNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "appDidBecomeActive:", name: UIApplicationDidBecomeActiveNotification, object: nil)
}
#IBAction func unwindSegue(segue: UIStoryboardSegue) {
isAuthenticated = true
view.alpha = 1.0
}
func appWillResignActive(notification : NSNotification) {
view.alpha = 0
isAuthenticated = false
didReturnFromBackground = true
}
func appDidBecomeActive(notification : NSNotification) {
if didReturnFromBackground {
self.showLoginView()
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(false)
self.showLoginView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showLoginView() {
if !isAuthenticated {
self.performSegueWithIdentifier("loginView", sender: self)
}
}
func insertNewObject(sender: AnyObject) {
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) as! NSManagedObject
newManagedObject.setValue(NSDate(), forKey: "date")
newManagedObject.setValue("New Note", forKey: "noteText")
var error: NSError? = nil
if !context.save(&error) {
abort()
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath)as! NSManagedObject
(segue.destinationViewController as! DetailViewController).detailItem = object
}
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.fetchedResultsController.sections?.count ?? 0
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = self.fetchedResultsController.sections![section] as! NSFetchedResultsSectionInfo
return sectionInfo.numberOfObjects
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
self.configureCell(cell, atIndexPath: indexPath)
return cell
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
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)
var error: NSError? = nil
if !context.save(&error) {
abort()
}
}
}
func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
cell.textLabel?.text = object.valueForKey("noteText")!.description
}
#IBAction func logoutAction(sender: AnyObject) {
isAuthenticated = false
self.performSegueWithIdentifier("loginView", sender: self)
}
var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest = NSFetchRequest()
let entity = NSEntityDescription.entityForName("Note", inManagedObjectContext: self.managedObjectContext!)
fetchRequest.entity = entity
fetchRequest.fetchBatchSize = 20
let sortDescriptor = NSSortDescriptor(key: "date", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = [sortDescriptor]
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master")
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !_fetchedResultsController!.performFetch(&error) {
abort()
}
return _fetchedResultsController!
}
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
switch type {
case .Insert:
self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
case .Delete:
self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
default:
return
}
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
case .Delete:
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
case .Update:
self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, atIndexPath: indexPath!)
case .Move:
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
default:
return
}
}
}
My DetailViewControoler.swift:
import UIKit
import CoreData
class DetailViewController: UIViewController, UITextViewDelegate {
let ManagedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate)
#IBOutlet weak var detailTextView: UITextView!
var note: Note? = nil
var detailItem: AnyObject? {
didSet {
self.configureView()
}
}
func configureView() {
if let detail: Note = self.detailItem as? Note {
if let detailTextView = self.detailTextView {
detailTextView.text = detail.noteText
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.configureView()
}
func textViewDidEndEditing( textView: UITextView) {
if let detail: Note = self.detailItem as? Note {
if let detailTextView = self.detailTextView {
detail.noteText = detailTextView.text
}
}
ManagedObjectContext.managedObjectContext!.save(nil)
}
}

Resources