How can I store the data in CoreData ? I'm using this to display it in my tableView cells but how can I save and retrieve from my database ?
Here is the code:
var arrOfDict = [[String :AnyObject]]()
var dictToSaveNotest = [String :AnyObject]()
class SecondViewController: UIViewController {
#IBAction func addItem(_ sender: Any)
{
dictToSaveNotest.updateValue(notesField.text! as AnyObject, forKey: "notesField")
arrOfDict.append(dictToSaveNotest)
}
and then to view in tableViewCell, I used the code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCellID") as! CustomCell
cell.textView.text = arrOfDict[indexPath.row]["notesField"] as! String!
}
I tried this in my addItem
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let task = Task(context: context)
task.notes=notesField.text!
//save the data to coreData
(UIApplication.shared.delegate as! AppDelegate).saveContext()
but not sure.
Yes, that would generally be how you'd save to Core Data. You create an instance of the object. Set the properties and then save. Are you receiving any errors? Or do you just want to be sure that your code works?
If you want to verify that your code works, the best thing to do would be to try to access the saved data. If you can see your saved data retrieved from your database, then your code worked :)
In order to save your input as a Task object, if you have a textField and a notesField on your screen to take input, you'd do something like this:
let task = Task(context: context)
task.title = textField.text
task.notes = notesField.text
//save the data to coreData
(UIApplication.shared.delegate as! AppDelegate).saveContext()
Related
I am new to programming and currently building an app where I collect data from Firebase.
I thought it would be nice to implement this refreshing animation, when the view is pulled down. Now I have the problem that nothing gets refreshed at all (it was working fine in the beginning). Also every time the app launches the tableView is empty at first and when you make the pushdown it reloads, but wrong.
I am really struggling. Can anyone see anything wrong in my code?
class ViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var newsfeedTableView: UITableView!
#IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
private let refreshControl = UIRefreshControl()
var ref: DatabaseReference!
var posts = [String]()
var timeRef = [String]()
var userRef = [String]()
override func viewDidLoad() {
super.viewDidLoad()
addSlideMenuButton()
setupTableView()
ref = Database.database().reference()
ref.child("posts").observe( .childAdded, with: { snapshot in
let newPost = snapshot.value as? NSDictionary
let post_title:String = newPost!["post_title"] as? String ?? "error"
let newPostTime = snapshot.value as? NSDictionary
let post_time:String = newPostTime!["post_time"] as? String ?? "error collecting timestamp"
let newPostUser = snapshot.value as? NSDictionary
let post_user:String = newPostUser!["post_user"] as? String ?? "Team"
self.posts.insert(post_title, at: 0)
self.timeRef.insert(post_time, at: 0)
self.userRef.insert(post_user, at: 0)
self.newsfeedTableView.reloadData()
})
}
private func setupTableView() {
if #available(iOS 10.0, *) {
newsfeedTableView.refreshControl = refreshControl
} else {
newsfeedTableView.addSubview(refreshControl)
}
refreshControl.addTarget(self, action: #selector(refreshNewsFeedTableView(_:)), for: .valueChanged)
}
#objc private func refreshNewsFeedTableView(_ sender: Any) {
//self.refreshControl.beginRefreshing()
self.newsfeedTableView.reloadData()
self.refreshControl.endRefreshing()
self.activityIndicatorView.stopAnimating()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! NewsfeedCell
cell.customTextLabel?.text = posts[indexPath.row]
cell.customTimeLabel?.text = timeRef[indexPath.row]
cell.customNameLabel?.text = userRef[indexPath.row]
return cell
}
}
class NewsfeedCell: UITableViewCell {
#IBOutlet weak var customTextLabel: UILabel!
#IBOutlet weak var customNameLabel: UILabel!
#IBOutlet weak var customTimeLabel: UILabel!
}
Here
#objc private func refreshNewsFeedTableView(_ sender: Any)
you don't do any API requests to get the data then reload the table and end refreshing , and since you use observe here
ref.child("posts").observe( .childAdded, with: { snapshot in
then you'll get any new data , if you need to implement the refresh then insert all database code inside a function and replace observe with observeSingleEvent and call it from viewDidLoad and from the refresh selector method
ref.child("posts").observeSingleEvent( .value, with: { snapshot in
Note with the suggested approach you will get all the childs at a time so don't forget to clean the array inside the callback of observeSingleEvent to avoid duplicating values , also consider making one dataSource array instead of 3 here
self.posts.insert(post_title, at: 0)
self.timeRef.insert(post_time, at: 0)
self.userRef.insert(post_user, at: 0)
probably with creating a model
struct Item {
let post:String
let time:String
let user:String
}
Only problem I can see in the code is, you are using .childAdded to observe events from firebase but according to firebase document:
The child_added event is typically used when retrieving a list of
items from the database. Unlike value which returns the entire
contents of the location, child_added is triggered once for each
existing child and then again every time a new child is added to the
specified path. The event callback is passed a snapshot containing the
new child's data. For ordering purposes, it is also passed a second
argument containing the key of the previous child.
So, if you want to receive all the data of any particular node, you need to use value event but not child_added as stated here:
The value event is used to read a static snapshot of the contents
at a given database path, as they existed at the time of the read
event. It is triggered once with the initial data and again every time
the data changes. The event callback is passed a snapshot containing
all data at that location, including child data. In the code example
above, value returned all of the blog posts in your app. Everytime a
new blog post is added, the callback function will return all of the
posts.
Reload UItableView in DispatchQueue.main, try this code.
override func viewDidLoad() {
super.viewDidLoad()
addSlideMenuButton()
setupTableView()
ref = Database.database().reference()
ref.child("posts").observe( .childAdded, with: { snapshot in
let newPost = snapshot.value as? NSDictionary
let post_title:String = newPost!["post_title"] as? String ?? "error"
let newPostTime = snapshot.value as? NSDictionary
let post_time:String = newPostTime!["post_time"] as? String ?? "error collecting timestamp"
let newPostUser = snapshot.value as? NSDictionary
let post_user:String = newPostUser!["post_user"] as? String ?? "Team"
self.posts.insert(post_title, at: 0)
self.timeRef.insert(post_time, at: 0)
self.userRef.insert(post_user, at: 0)
DispatchQueue.main.async() {
self.newsfeedTableView.reloadData()
}
})
}
I'm new to swift and having trouble with setting value to FIRDataSnapshot. I simply created a calculator. I took MealCaloryArray in didload method and display it tableviewcell (with the help of FIRDataSnapshot list) and the calory is changed in the plus function and I have to send the new value to tableviewcell again. However, I couldnt set the new value in FIRDataSnapshot array list. I tried to useself.calory[buttonRow].setValue(<value: n, forUndefinedKey:"")but I dont have proper "forUndefinedKey" value. Do you have any suggestions?
My nested Firebase DB Structure and code blocks is attached.
Firebase Child Structure:
Database Screenshot
var calory: [FIRDataSnapshot]! = []
override func viewDidLoad() {
ref = FIRDatabase.database().reference()
let CoursesRef = ref.child("CompanyMeals")
CoursesRef.observe(.childAdded, with: { snapshot in
self.calory = snapshot.childSnapshot(forPath: "MealCaloryArray").children.allObjects as! [FIRDataSnapshot]
self.calory.append(snapshot)
self.ingredientTableView.reloadData()
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ingredientTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MealCalculatorListCell
let cellcalory = self.calory[indexPath.row].value as? Double
if (cellcalory != nil) {
cell.itemTotalCalory.text = ("\(cellcalory!)")//String (describing: cellcalory)
let cellcalory1 = Int(cellcalory!)
firstCaloriesArray.append((cellcalory1 as AnyObject))
}
}
#IBAction func plusAction(sender: UIButton) {
cell.itemTotalCalory.text = String ((Int(oldcalory!) + fcCalory))
let newcalory = cell.itemTotalCalory.text
let n = String(newcalory!)
self.calory[buttonRow].setValue(value: n, forUndefinedKey: "")
}
I working on a project that is written in swift 3.0. My requirement is to save data that i enter on some text fields and populate one of those attributes in to a table view, and once a row is selected I wants to update that records (re-assign values on my text fields).
However im having an issue with my code when i try to fetch data that i have saved in core data and assigning them in to an array. Basically I have an entity named "Task" and it got three attributes, and since i wants to populate one of those attributes(called "name") that i have saved on core data, to a table view i have written the code as follow. But im getting an exception in the following line in my code saying "Could not cast value of type NSTaggedPointerString (0x10d8f7b90) to NSArray (0x10d8f7c58)".
The error line and the code as bellow.
tasks += expName as! [Task]
Here is my full code:
import UIKit
import CoreData
class TableViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let appDelegate : AppDelegate = UIApplication.shared.delegate as! AppDelegate
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var tasks = [Task] ()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
//var error : NSError?
let request = NSFetchRequest <NSFetchRequestResult> (entityName: "Task")
request.returnsObjectsAsFaults = false
do {
let results = try context.fetch(request)
// check data existance
if results.count>0 {
print(results.count)
for resultGot in results as! [NSManagedObject]{
if let expName = resultGot.value(forKey:"name"){
print("expence name is :", expName)
tasks += expName as! [Task]
print("my array is : \(tasks)")
}
}
}
}catch{
print("No Data to load")
}
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = tasks [indexPath.row] as? String
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowEditTask"{
let v = segue.destination as! ViewController
let indexPath = self.tableView.indexPathForSelectedRow
let row = indexPath?.row
}
}
The error message says that NSTaggedPointerString (expName) can not be cast to NSArray ([Task])
Your goal is to add all Tasks to the task array if the name property is not nil but you're trying to add the name which causes the error.
Some suggestions:
fetch(context: returns always an array of the NSManagedObject subclass so cast it immediately.
Since you are using NSManagedObject subclass get the name property directly rather than with valueForKey.
The check for > 0 is not needed because the loop will be skipped in case of an empty array.
let results = try context.fetch(request) as! [Task]
// check data existance
print(results.count)
for task in results {
if let expName = task.name {
print("expence name is :", expName)
tasks += task
print("my array is : \(tasks)")
}
}
or shorter
let results = try context.fetch(request) as! [Task]
tasks.filter{ $0.name != nil }
The most efficient way is to filter the tasks before the fetch via an appropriate predicate.
I have a one to many relationship from Set to Card for a basic Flashcard App modelled in my Core Data.
Each Set has a set name, set description, and a relationships many card1s. Each Card1 has a front, back, and photo. In my table view, I've managed to retrieve all saved Sets from core data and display them. Now I want to fetch each Set's cards when a user clicks on the appropriate cell in my next view controller.
This is my code for the table view controller:
// MARK: Properties
var finalArray = [NSManagedObject]()
override func viewDidLoad() {
getAllSets()
println(finalArray.count)
}
func getAllSets() {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName:"Set")
var error: NSError?
let fetchedResults = managedContext.executeFetchRequest(fetchRequest,error: &error) as? [NSManagedObject]
println("Am in the getCardSets()")
if let results = fetchedResults {
finalArray = results
println(finalArray.count)
}
else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
// MARK: Displaying the data
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return finalArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! SetTableViewCell
let sets = finalArray[indexPath.row]
cell.setName.text = sets.valueForKey("setName")as? String
cell.setDescription.text = sets.valueForKey("setDescription")as? String
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" {
let dest = segue.destinationViewController as! Display
// Get the cell that generated this segue.
if let selectedCell = sender as? SetTableViewCell {
let indexPath = tableView.indexPathForCell(selectedCell)!
let selectedSet = finalArray[indexPath.row]
dest.recievedSet = selectedSet
}
}
}
In my destination view controller, how would I go about retrieving all the cards in that the recievedSet? I've tried converting the NSSet to an array and casting it to a [Card1] array but when I attempt to display the first Card1's front String property onto the label, the app crashes, giving me the error
CoreData: error: Failed to call designated initializer on NSManagedObject class 'NSManagedObject'
fatal error: Array index out of range
This is my code for the detailed viewController.
#IBOutlet weak var front: UILabel!
var finalArray = [Card1]()
finalArray = retrievedSet.allObjects as![Card1]
front.text = finalArray[0].front
Give your detail controller a property of type CardSet (I use "CardSet" because "Set" is a Swift built-in type name). You pass the selected set to this controller.
You could have a property by which you sort, or generate an array without a particular order with allObjects.
var cardArray = [Card1]()
var cardSet: CardSet?
viewDidLoad() {
super.viewDidLoad()
if let validSet = cardSet {
cardArray = validSet.cards.allObjects as! [Card1]
}
}
Your code is not working because finalArray is of type [CardSet], so finalArray[indexPath.row] is of type CardSet which is not transformable into type NSSet. Rather the relationship to Card1s is the NSSet you are looking for.
Finally, I recommend to give the detail controller a NSFetchedResultsController, have an attribute to sort by and use the passed CardSet in the fetched results controller's predicate.
I'm new to iOS development and was wanting to know which data type I should specify to store multiple strings (array). The app is to do with food and I need to store multiple ingredients as one attribute.
I was thinking of making ingredient as entity, but I just want to make it easy for a starter.
I have read about transformable type but people don't seem to recommend using it to store arrays.
Warning: opinionated answer ahead.
You don't.
Storing things in an array does not make anything easier for you. On the contrary, it will make things much harder just an hour in. Imagine you want to show all Recipes that contain a selected Ingredient. That wouldn't be easy with your array hack, with a proper model it's only a couple line of code.
I would recommend to use a good old relationship with a "Join-entity".
Yes, this is more complicated than hacking something together that barely works. But it's the correct way.
What you was thinking of is exactly what you should do. Core Data is made to store values in array like structure. You should create entity Ingredients and connect your Food entity (or whatever you would like to call it) with relationship with Ingredients entity.
there is a way. You can do each element manually e.g.
You have your array:
let employee: NSMutableArray = []
employee.addObject(["name":"Bill","LastName":"Hanks"])
employee.addObject(["name":"Rolex","LastName":"Swarzer"])
employee.addObject(["name":"Clive","LastName":"Martin"])
employee.addObject(["name":"Jimi","LastName":"Hendrix"])
Assuming you have created your coreData with Entity "Employee" and Attributes "name" and "lastname" you do the following to add it in...
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
let context = appDel.managedObjectContext
for item in employee {
do {
let newUser = NSEntityDescription.insertNewObjectForEntityForName("Employee", inManagedObjectContext: context)
newUser.setValue(item["name"], forKey: "name")
newUser.setValue(item["LastName"], forKey: "lastname")
try context.save()
} catch {
//do nothing
}
You can then fetch all elements using your fetch request or the NSFetched Results Controller
I have done in Swift 4,
Storing more Arrays into allDataArray (One Array). Fetching array objects from CoreData (AllData) and Displaying in TableView
import UIKit
import Foundation
import CoreData
class ViewController: UIViewController {
var allTableDataArray : [AllData] = [AllData]()
let allDataArray : NSMutableArray = []
var listOfArray1 = ["#849578", "#849302"]
var listOfArray2 = ["Vasuki Shiv", "Prathap Dusi"]
override func viewDidLoad() {
super.viewDidLoad()
saveAllDataToCoredata()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
fetchAllDataFromCoredata()
}
func saveAllDataToCoredata() {
deleteAllData(entity: "AllData")
let context = PersistenceSerivce.context
allDataArray.add(["requestNo" : listOfArray1[0], "vendorName" : listOfArray2[0]])
allDataArray.add(["requestNo" : listOfArray1[1] , "vendorName" : listOfArray2[1]])
for item in (allDataArray){
do {
let newUser = NSEntityDescription.insertNewObject(forEntityName: "AllData", into: context)
guard let requestNoNew = item as? [String:Any] else {
return
}
let requestNoStr = requestNoNew["requestNo"] as! String
newUser.setValue(requestNoStr, forKey: "requestNo")
guard let vendorNameNew = item as? [String:Any] else {
return
}
let vendorNameStr = vendorNameNew["vendorName"] as! String
newUser.setValue(vendorNameStr, forKey: "vendorName")
PersistenceSerivce.saveContext()
try context.save()
} catch {
//do nothing
}
}
}
func fetchAllDataFromCoredata(){
let context = PersistenceSerivce.context
let fetchRequest = NSFetchRequest<AllData>(entityName: "AllData")
allTableDataArray.removeAll()
do {
allTableDataArray = try context.fetch(fetchRequest)
} catch {
print("Unable to fetch from Coredata", error)
}
}
func deleteAllData(entity: String) {
let managedContext = PersistenceSerivce.context
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
fetchRequest.returnsObjectsAsFaults = false
do
{
let results = try managedContext.fetch(fetchRequest)
for managedObject in results
{
let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
managedContext.delete(managedObjectData)
}
} catch let error as NSError {
print("Delete all data in \(entity) error : \(error) \(error.userInfo)")
}
}
}
//MARK:- UITableView
extension ViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (allTableDataArray.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: “TableViewCellID”) as? TableViewCell
let allData = allTableDataArray[indexPath.row]
cell?.requestNoLabel.text = allData.requestNo
cell?.vendorNameLabel.text = allData.vendorName
return cell!
}
}