Error when trying to save Core Data objects - ios

I have been trying to set up, what seems like it should be, a simple app that allows a user to update a food item with a price and store where the price was found. The main issue I know is trying to blend Swift with Objective-C even though Apple hasn't worked out the kinks yet for Swift and it's ever changing.
Anyways I have set up my AllowedTableViewController as follows
class AllowedTableViewController: UITableViewController {
var myAllowedList : Array<AnyObject> = []
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!
let freq = NSFetchRequest(entityName: "FoodAllowed")
myAllowedList = context.executeFetchRequest(freq, error: nil)!
tableView.reloadData()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "update" {
var indexPath: NSIndexPath = self.tableView.indexPathForSelectedRow()!
var selectedItem: NSManagedObject = myAllowedList[indexPath.row] as NSManagedObject
let IVC: AllowedViewController = segue.destinationViewController as AllowedViewController
IVC.name = selectedItem.valueForKey("name")as String
IVC.store = selectedItem.valueForKey("store")as String
IVC.price = selectedItem.valueForKey("price")as String
IVC.existingItem = selectedItem
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return myAllowedList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Configure the Cell
let CellID: NSString = "Allowed"
var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(CellID) as UITableViewCell
var data: NSManagedObject = myAllowedList[indexPath.row] as NSManagedObject
cell.textLabel.text = (data.valueForKeyPath("name")as String)
var pri = data.valueForKeyPath("price")as String
var str = data.valueForKeyPath("store")as String
cell.detailTextLabel?.text = "\(pri) name/s - \(str)"
return cell
}
//Override to support conditional editing of the table view
override func tableView(tableView: UITableView?, canEditRowAtIndexPath indexPath: NSIndexPath?) -> Bool {
//Return NO if you do not want the specified item to be editable
return true
}
//Override to support editing the table view
override func tableView(tableView: UITableView?, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath?) {
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!
if editingStyle == UITableViewCellEditingStyle.Delete {
if let tv = tableView {
context.deleteObject(myAllowedList[indexPath!.row] as NSManagedObject)
myAllowedList.removeAtIndex(indexPath!.row)
tv.deleteRowsAtIndexPaths([indexPath!.row], withRowAnimation: UITableViewRowAnimation.Fade)
}
var error: NSError? = nil
if !context.save(&error) {
abort()
}
}
}
}
My user editable text field code in the view controller are as follows.
{
class AllowedViewController: UIViewController {
#IBOutlet var textFieldname: UITextField!
#IBOutlet var textFieldstore: UITextField!
#IBOutlet var textFieldprice: UITextField!
var name: String = ""
var store: String = ""
var price: String = ""
var existingItem: NSManagedObject!
override func viewDidLoad() {
super.viewDidLoad()
if existingItem == nil {
textFieldname.text = name
textFieldstore.text = store
textFieldprice.text = price
}
// Do any additional setup after loading the view.
}
func saveTapped(sender: AnyObject) {
//Reference to our app delegate
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
//Reference NS Managed Object Context
let contxt: NSManagedObjectContext = appDel.managedObjectContext!
let en = NSEntityDescription.entityForName("FoodAllowed", inManagedObjectContext: contxt)!
//Check if item exists
if (existingItem != nil) {
existingItem.setValue(textFieldname.text as String, forKey: "name")
existingItem.setValue(textFieldstore.text as String, forKey: "store")
existingItem.setValue(textFieldprice.text as String, forKey: "price")
}else {
//Create instance of pur data model and intialize
var newItem = DataModel(entity: en, insertIntoManagedObjectContext: contxt)
//Map our properties
newItem.name = [textFieldname.text]
newItem.store = [textFieldstore.text]
newItem.price = [textFieldprice.text]
//Save our content
contxt.save(nil)
println(newItem)
//Navigate back to root view controll
self.navigationController?.popToRootViewControllerAnimated(true)
}
}
func cancelTapped(sender: AnyObject) {
//Navigate back to root view controll
self.navigationController?.popToRootViewControllerAnimated(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
}
My NSManaged DataModel is
{
#objc(DataModel)
class DataModel: NSManagedObject {
//properties feeding the attributes in our entity
//must match the entity attributes
#NSManaged var name: [String]
#NSManaged var store: [String]
#NSManaged var price: [String]
}
}
When I run the app in the simulator I get the following error
'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "name"; desired type = NSString; given type = Swift._NSSwiftArrayImpl; value = (Apples).'
What am I doing wrong?

The error message indicates that "name", "store" and "price" are String properties
of your Core Data entity, but you have defined them as [String], i.e. an array
of strings. It should be
#objc(DataModel)
class DataModel: NSManagedObject {
#NSManaged var name: String
#NSManaged var store: String
#NSManaged var price: String
}
And consequently
newItem.name = textFieldname.text // not: [textFieldname.text]
// ...
Better yet, let Xcode generate the managed object subclasses
(Editor -> Create NSManagedObject Subclass ... in the Xcode menu).

Related

Object view state with Unidirectional Data Flow with Swift and Realm

I am using Swift with Realm to build an unidirectional data flow App.
I am wondering why I can not use an object as current application state.
var people is always updated when I add new person but var oldestPerson is never updated.
This is my Store.swift file
class Person: Object {
dynamic var name: String = ""
dynamic var age: Int = 0
}
// MARK: Application/View state
extension Realm {
var people: Results<Person> {
return objects(Person).sorted("age")
}
var oldestPerson: Person? {
return objects(Person).sorted("age").first
}
}
// MARK: Actions
extension Realm {
func addPerson(name: String, age: Int) {
do {
try write {
let person = Person()
person.name = name
person.age = age
add(person)
}
} catch {
print("Add Person action failed: \(error)")
}
}
}
let store = try! Realm()
The state of oldest in my view layer never change but people change as it should.
import RealmSwift
class PersonsTableViewController: UITableViewController {
var notificationToken: NotificationToken?
var people = store.people
var oldest = store.oldestPerson
override func viewDidLoad() {
super.viewDidLoad()
updateView()
notificationToken = store.addNotificationBlock { [weak self] (_) in
self?.updateView()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("PersonTableViewCell") as! PersonTableViewCell
cell.person = people[indexPath.row]
return cell
}
func updateView() {
print(oldest)
tableView.reloadData()
}
}
Change your declaration of
var oldest = store.oldestPerson
to:
var oldest: Person? { return store.oldestPerson }

tableview error while searching

Hi I have two arrays and only one array is updating with search bar.. I keep the TitleArray to show in tableView title and detailsArray to show in tableView subtitle.. once I start searching only title following my typing but subtitle nothing change.
#IBOutlet weak var AirportsTableView: UITableView!
var TitleArray = [String]()
var DetailsArray = [String]()
var NumberOfRows = 0
var filteredNamesArray = [String]()
var filteredDetailsArray = [String]()
var resultSearchController = UISearchController!()
**override func viewDidLoad() {
super.viewDidLoad()**
// Do any additional setup after loading the view.
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.resultSearchController.searchBar.sizeToFit()
self.resultSearchController.loadViewIfNeeded()
self.AirportsTableView.tableHeaderView = self.resultSearchController.searchBar
self.AirportsTableView.reloadData()
parseJSON()
}
func parseJSON() {
if let path = NSBundle.mainBundle().pathForResource("airports", ofType: "json") {
do {
let data = try NSData(contentsOfURL: NSURL(fileURLWithPath: path), options: NSDataReadingOptions.DataReadingMappedIfSafe)
let jsonObj = JSON(data: data)
if jsonObj != JSON.null {
// print("jsonData:\(jsonObj)")
NumberOfRows = jsonObj.count
for i in 0...NumberOfRows {
let City = jsonObj[i]["city"].string as String!
let Country = jsonObj[i]["country"].string as String!
let Iata = jsonObj[i]["iata"].string as String!
let Name = jsonObj[i]["name"].string as String!
self.TitleArray.append("\(City) - \(Country) - \(Iata)")
self.DetailsArray.append("\(Name)")
}
} else {
print("could not get json from file, make sure that file contains valid json.")
}
} catch let error as NSError {
print(error.localizedDescription)
}
} else {
print("Invalid filename/path.")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if self.resultSearchController.active
{
return self.filteredNamesArray.count
} else
{
return self.TitleArray.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell?
if self.resultSearchController.active
{
cell!.textLabel?.text = self.filteredNamesArray[indexPath.row]
} else
{
cell!.textLabel?.text = self.TitleArray[indexPath.row]
cell!.detailTextLabel?.text = self.DetailsArray[indexPath.row]
}
return cell!
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredNamesArray.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.TitleArray as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredNamesArray = array as! [String]
self.AirportsTableView.reloadData()
}
// MARK: - Segues
/*
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "AirportDetails" {
if let indexPath = self.AirportsTableView.indexPathForSelectedRow {
let airportDetail : Airports = TitleArray[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! AllWaysFlightsViewController
controller.airportDetail = airportDetail
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
*/
Instead of using two separate arrays use only one array and populate it with object containing both variables you are using to populate the tableView.
class Address {
var city: String
var detail: String
init(city: String, detail:String) {
self.city = city
self.detail = detail
}
}
Parse your json like this:
for i in 0...NumberOfRows {
let City = jsonObj[i]["city"].string as String!
let Country = jsonObj[i]["country"].string as String!
let Iata = jsonObj[i]["iata"].string as String!
let Name = jsonObj[i]["name"].string as String!
let city = "\(City) - \(Country) - \(Iata)"
let address = Address(city: city, detail: Name)
self.TitleArray.append(address)
self.filteredNamesArray.append(address)
}
Filter your title array containing addresses. Your titlearray and filtered array both contains same data for the first time you can refer to the json parsing for this. Here you can use one for filtering and when search bar is empty it user cancel his search you can re-populate your array from the other one.
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredNamesArray.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF.city CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.TitleArray as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredNamesArray = array as! [Address]
self.AirportsTableView.reloadData()
}
your tableView logic will be changed accordingly
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return self.filteredNamesArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell?
let address = self.filteredNamesArray[indexPath.row]
cell!.textLabel?.text = address?.city
cell!.detailTextLabel?.text = address?.detail
return cell!
}
You need to change the way you approach filtering the data so that rather than just apply a predicate you explicitly iterate and check the predicate, if you find a match then you take that item and the corresponding description into your filtered arrays.
Something like:
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredNamesArray.removeAll(keepCapacity: false)
self.filteredDetailsArray.removeAll(keepCapacity: false)
let searchString = searchController.searchBar.text!
var index = 0
for title in self.TitleArray
if title.rangeOfString(searchString).location != NSNotFound {
self.filteredNamesArray.append(title)
self.filteredDetailsArray.append(self.DetailsArray[index])
}
index += 1
}
self.AirportsTableView.reloadData()
}

How to save data from ViewController using NSCoding in Swift [duplicate]

This question already has an answer here:
How to store value generated from a ViewController using NSCoding in Swift
(1 answer)
Closed 7 years ago.
I need to save data from a segued ViewController (“ScoreView.swift”) to “ScoreHistory.swift” using NSCoding. I tried but the data isn't showing up in "ScoreTableViewController.swift". What am I missing?
I have this ScoreView.swift which has the following code: (Pls note that this is a "segued" view where data has been passed from another ViewController)
class ScoreView: UIViewController {
var dateToday = NSDate()
var score: ScoreHistory?
var numberofquestions:String = ""
var scorepassed:String = ""
var scorepercentpassed:String = ""
var scoreremarkspassed:String = ""
var totalduration:String!
var incorrectanswerspassed:String = ""
var skippedquestionspassed:String = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
datePlayedLabel.text = dateToday.description
totalScoreLabel.text = scorepassed
scorePercentage.text = scorepercentpassed
totalAnsweredLabel.text = numberofquestions
totalDurationLabel.text = totalduration
gameStatusLabel.text = "Exam Finished"
// NSCoding
if let score = score {
datePlayedLabel.text = score.datePlayed
totalScoreLabel.text = score.totalScore
totalAnsweredLabel.text = score.totalAnswered
totalDurationLabel.text = score.totalDuration
gameStatusLabel.text = score.gameStatus
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if backMenu === sender {
let datePlayed = datePlayedLabel.text ?? ""
let totalScore = totalScoreLabel.text ?? ""
let totalAnswered = totalAnsweredLabel.text ?? ""
let totalDuration = totalDurationLabel.text ?? ""
let gameStatus = gameStatusLabel.text ?? ""
// Set the score to be passed to ScoreTableViewController after the unwind segue.
score = ScoreHistory(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
}
NSKeyedArchiver.archiveRootObject(score!, toFile: ScoreHistory.ArchiveURL.path!)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
datePlayedLabel.text = dateToday.description
totalScoreLabel.text = scorepassed
scorePercentage.text = scorepercentpassed
totalAnsweredLabel.text = numberofquestions
totalDurationLabel.text = totalduration
gameStatusLabel.text = "Exam Finished"
}
// Labels
}
}
I have ScoreHistory.swift, which has the following code:
class ScoreHistory: NSObject, NSCoding {
// MARK: Properties
var datePlayed: String
var totalScore: String
var totalAnswered: String
var totalDuration: String
var gameStatus: String
// MARK: Archiving Paths
static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("scores")
// MARK: Types
struct PropertyKey {
static let datePlayedKey = "datePlayed"
static let totalScoreKey = "totalScore"
static let totalAnsweredKey = "totalAnswered"
static let totalDurationKey = "totalDuration"
static let gameStatusKey = "gameStatus"
}
// MARK: Initialization
init?(datePlayed: String, totalScore: String, totalAnswered: String, totalDuration: String, gameStatus: String) {
// Initialize stored properties.
self.datePlayed = datePlayed
self.totalScore = totalScore
self.totalAnswered = totalAnswered
self.totalDuration = totalDuration
self.gameStatus = gameStatus
super.init()
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(datePlayed, forKey: PropertyKey.datePlayedKey)
aCoder.encodeObject(totalScore, forKey: PropertyKey.totalScoreKey)
aCoder.encodeObject(totalAnswered, forKey: PropertyKey.totalAnsweredKey)
aCoder.encodeObject(totalDuration, forKey: PropertyKey.totalDurationKey)
aCoder.encodeObject(gameStatus, forKey: PropertyKey.gameStatusKey)
}
required convenience init?(coder aDecoder: NSCoder) {
let datePlayed = aDecoder.decodeObjectForKey(PropertyKey.datePlayedKey) as! String
let totalScore = aDecoder.decodeObjectForKey(PropertyKey.totalScoreKey) as! String
let totalAnswered = aDecoder.decodeObjectForKey(PropertyKey.totalAnsweredKey) as! String
let totalDuration = aDecoder.decodeObjectForKey(PropertyKey.totalDurationKey) as! String
let gameStatus = aDecoder.decodeObjectForKey(PropertyKey.gameStatusKey) as! String
// Must call designated initializer.
self.init(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
}
}
Here is the full code of ScoreTableViewController.swift:
class ScoreTableViewController: UITableViewController {
// MARK: Properties
var scores = [ScoreHistory]()
var dateToday = NSDate()
override func viewDidLoad() {
super.viewDidLoad()
// Load any saved scores, otherwise load sample data.
if let savedScores = loadScores() {
scores += savedScores
} else {
// Load the sample data.
loadSampleScores()
}
}
func loadSampleScores() {
let score1 = ScoreHistory(datePlayed: dateToday.description, totalScore: "0", totalAnswered: "0", totalDuration: "0", gameStatus: "started")!
scores += [score1]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return scores.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "ScoreHistoryTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ScoreHistoryTableViewCell
// Fetches the appropriate note for the data source layout.
let score = scores[indexPath.row]
cell.datePlayedLabel.text = score.datePlayed
cell.totalScoreLabel.text = score.datePlayed
cell.totalScoreLabel.text = score.totalScore
cell.totalAnsweredLabel.text = score.totalAnswered
cell.totalDurationLabel.text = score.totalDuration
cell.gameStatusLabel.text = score.gameStatus
return cell
}
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
scores.removeAtIndex(indexPath.row)
saveScores()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" {
let scoreDetailViewController = segue.destinationViewController as! ScoreViewController
// Get the cell that generated this segue.
if let selectedScoreCell = sender as? ScoreHistoryTableViewCell {
let indexPath = tableView.indexPathForCell(selectedScoreCell)!
let selectedScore = scores[indexPath.row]
scoreDetailViewController.score = selectedScore
}
}
}
// MARK: NSCoding
func saveScores() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(scores, toFile: ScoreHistory.ArchiveURL.path!)
if !isSuccessfulSave {
print("Failed to save scores...")
}
}
func loadScores() -> [ScoreHistory]? {
return NSKeyedUnarchiver.unarchiveObjectWithFile(ScoreHistory.ArchiveURL.path!) as? [ScoreHistory]
}
#IBAction func unwindToScoreList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? ScoreViewController, score = sourceViewController.score {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing note.
scores[selectedIndexPath.row] = score
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
// Add a new score.
let newIndexPath = NSIndexPath(forRow: scores.count, inSection: 0)
scores.append(score)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
saveScores()
}
}
}
}
GOAL: My goal is to record/store all session data from “ScoreView.swift” whenever a user finishes a quiz game.
The "ScoreView" is shown after each quiz game, I plan to record each quiz results in "ScoreHistory.swift." How do I do it?
The easiest solution is to save the changed values from the UITextField instances back to the score instance in ScoreView (why is score optional at all since you always pass a non-optional score instance ??) and unwind the segue.
Then the array is saved in the method unwindToScoreList of ScoreTableViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if backMenu === sender {
score?.datePlayed = datePlayedLabel.text ?? ""
score?.totalScore = totalScoreLabel.text ?? ""
score?.totalAnswered = totalAnsweredLabel.text ?? ""
score?.totalDuration = totalDurationLabel.text ?? ""
score?.gameStatus = gameStatusLabel.text ?? ""
}
}
No archiving in ScoreView !
Your loadScores function is loading an archived array of scores:
func loadScores() -> [ScoreHistory]? {
return NSKeyedUnarchiver.unarchiveObjectWithFile(ScoreHistory.ArchiveURL.path!) as? [ScoreHistory]
}
In your segue, you are only archiving a single score. You can't archive a ScoreHistory instance and expect to unarchive a ScoreHistory array. Where you currently have:
score = ScoreHistory(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
You need to change this to:
var scores = loadScores() ?? []
score = ScoreHistory(datePlayed: datePlayed, totalScore: totalScore, totalAnswered: totalAnswered, totalDuration: totalDuration, gameStatus: gameStatus)
scores.append(score)
saveScores(scores)
Where loadScores and saveScores are the same as the code in ScoreTableViewController, although I've added the scores to save as a parameter given this code creates a local var.
UPDATE: It's late and I wasn't paying enough attention. You need to handle loadScores returning nil, and of course scores should be var not let or you won't be able to add to it. With these changes, scores should no longer be optional, so you won't need to unwrap it.

TableController doesn't show the data from Core-data

I want to make simple Contact by using core-data and tableView for practicing CoreData.
So I have watched youtube and write it's code.
Finally, I thought that I got this, I made by myself, but tableView doesn't contain any data, I could compile though. Could anyone tell me what is wrong? and hopefully tell me how to check the stored data in core-data?
import UIKit
import CoreData
class ViewController: UIViewController {
var context = (UIApplication.sharedApplication().delegate as!AppDelegate).managedObjectContext
var stores : Contact? = nil
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var phoneTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
if stores != nil {
nameTextField.text = stores?.name
phoneTextField.text = stores?.phone
context?.save(nil)
} }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func saveTapped(sender: AnyObject) {
let context = self.context
// Get the description of the entity
if stores != nil { let storeDescription = NSEntityDescription.entityForName("stores", inManagedObjectContext: context!)
// Then, We Create the Managed Object to be inserted into the cored data
stores = Contact(entity: storeDescription!, insertIntoManagedObjectContext: context)
}
// set the attributes
stores?.name = nameTextField.text
stores?.phone = phoneTextField.text
context!.save(nil) // Save The object
let alert = UIAlertView(title: "저장 완료", message: "\(nameTextField.text)님이 전화번호부에 저장 되었습니다", delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
}
And here is my tableviewcontroller.
import UIKit
import CoreData
class TableViewController: UITableViewController , NSFetchedResultsControllerDelegate{
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var frc : NSFetchedResultsController = NSFetchedResultsController()
var stores = [Contact]()
override func viewDidLoad() {
super.viewDidLoad()
frc.delegate = self
frc.performFetch(nil)
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func viewWillAppear(animated: Bool) {
var error:NSError?
let request = NSFetchRequest(entityName: "Contact")
stores = context?.executeFetchRequest(request, error: &error) as! [Contact]
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return stores.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
let save = stores[indexPath.row]
cell.textLabel!.text = save.name
cell.detailTextLabel!.text = save.phone
return cell
}
func getFetchedResultsController() ->NSFetchedResultsController {
frc = NSFetchedResultsController(fetchRequest: listFetchRequest(), managedObjectContext: context!, sectionNameKeyPath: nil, cacheName: nil)
return frc
}
func listFetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Contact")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
if segue.identifier == "edit"
{
let destViewController = segue.destinationViewController as! ViewController
let indexPath = self.tableView.indexPathForSelectedRow()
let row = indexPath?.row
destViewController.stores = stores[row!]
}
}
}
Update ----Since I fixed my viewController, It works. It would be like
import UIKit
import CoreData
class ViewController: UIViewController {
var context = (UIApplication.sharedApplication().delegate as!AppDelegate).managedObjectContext
var stores : Contact? = nil
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var phoneTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
if stores != nil {
nameTextField.text = stores?.name
phoneTextField.text = stores?.phone
context?.save(nil)
} }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func saveTapped(sender: AnyObject) {
if stores != nil {
edit()
}else {
addNew()
}
navigationController?.popViewControllerAnimated(true)
}
func addNew() {
let description = NSEntityDescription.entityForName("Contact", inManagedObjectContext: context!)
let stores = Contact(entity: description!, insertIntoManagedObjectContext: context)
stores.name = nameTextField.text
stores.phone = phoneTextField.text
context?.save(nil)
}
func edit() {
stores!.name = nameTextField.text
stores!.phone = phoneTextField.text
context?.save(nil)
}
}
1. How to locate and View the SQLlite file.
You can find out the path of the .sqlite file using the below function.
func applicationDirectoryPath() -> String {
return NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last! as! String
}
Then use any third party tools to view the content of the Sqlite database like SQLLite Manager (a firefox addon)
2. Displaying data in TableView.
In your tableViewController, instead of using stores variable Use NSFetchedResultController (frc) to retrieve and show the data in TableView. Check out this link for using NSFetchedResultController with TableViewController.

Save and load data to / from Core Data database (iOS 8)

First of all I have to say that I am pretty new to iOS programming. I develop and Core Data App with Swift. I have a UITableViewController, called tanningarTableViewController" where I want the data to be displayed the data is going to be added (saved) in another ViewController called "nyTankning".
I have followed a tutorial on Youtube, but because Swift has changed during since the WWDC I have had to search for other sultans sometimes.
I am not 100% sure The UITableViewController is coded right, but I guess, because it seems like the problem is with the saving? I have read about some NSFetchedController or something like that, should I use that?
Here is the button I expect to save the data:
#IBAction func saveButton(sender: AnyObject) {
// Reference to out app delegate
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
// Reference moc
let contxt: NSManagedObjectContext = appDel.managedObjectContext!
let en = NSEntityDescription.entityForName("Tankningslista", inManagedObjectContext: contxt)
// Create instance of our data model and initialize
var nyTankning = Model(entity: en!, insertIntoManagedObjectContext: contxt)
// Map our properies
nyTankning.liter = (textFieldLiter.text as NSString).floatValue
nyTankning.kronor = (textFieldKronor.text as NSString).floatValue
nyTankning.literpris = (textFieldLiterpris.text as NSString).floatValue
// Save or context
contxt.save(nil)
println(nyTankning.liter)
println(nyTankning.kronor)
println(nyTankning.literpris)
if contxt.save(nil) {
// Fetch the Data from Core Data first....
let fetchRequest = NSFetchRequest(entityName: "Tankningslista")
var error:NSError?
var result: NSArray = contxt.executeFetchRequest(fetchRequest, error: nil)!
for res in result {
// Now you can display the data
println(res.liter)
println(res.kronor)
println(res.literpris)
}
// End of the fetching
} else {
println("error")
}
// navigate back to root vc
self.navigationController?.popToRootViewControllerAnimated(true)
}
And here is the code for the UITableViewController
import UIKit
import CoreData
class tankningarTableViewController: UITableViewController {
var Tankningar: Array<AnyObject> = []
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func viewDidAppear(animated: Bool) {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let contxt:NSManagedObjectContext = appDel.managedObjectContext!
let freq = NSFetchRequest(entityName: "Tankningslista")
var error :NSError?
let fetchedResults = contxt.executeFetchRequest(freq, error: &error) as? [NSManagedObject]
if let results = fetchedResults { Tankningar = results
}
else
{
println("Could not fetch \(error)")
}
tableView.reloadData()
println(Tankningar)
println("freq")
println(freq)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return Tankningar.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Configure the cell...
let CellID: NSString = "Cell"
var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(CellID) as UITableViewCell
let fetchRequest = NSFetchRequest(entityName: "Tankningslista")
var data: NSManagedObject = Tankningar[indexPath.row] as NSManagedObject
var literLabel = data.valueForKeyPath("liter") as? String
var kronorLabel = data.valueForKeyPath("kronor") as? String
var literprisLabel = data.valueForKeyPath("literpris") as? String
cell.textLabel?.text = data.valueForKeyPath("liter") as? String
return cell
}
When I check the debugger it seems like the array that I created in the TableViewController is empty:
[]
freq
<NSFetchRequest: 0x7ffd4ad24830> (entity: Tankningslista; predicate: ((null)); sortDescriptors: ((null)); type: NSManagedObjectResultType; )
34.0
551.0
14.0
39.0
551.0
14.0
[, ]
freq
<NSFetchRequest: 0x7ffd4ae8c330> (entity: Tankningslista; predicate: ((null)); sortDescriptors: ((null)); type: NSManagedObjectResultType; )
What is wrong?

Resources