I have been trying to save data when reloading app. However some how data won't save.
my global variable
let defaults = UserDefaults.standard
this is my code on AddTaskView
#IBAction func addTask(_ sender: Any) {
let date = datePicker.date
let dateStr = dateFormatter.string(from: date)
taskArray.append(selectedTask)
dateArray.append(dateStr)
defaults.set(selectedTask, forKey: "task")
defaults.set(dateStr, forKey: "date")
dismiss(animated: true, completion: nil)
}
In my ViewController I have my viewWillApper
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
defaults.string(forKey: "task")
defaults.string(forKey: "date")
tableView.reloadData()
}
If I print the data coming back from AddTaskView It will print on console
but data disappear when reloading app
tableView.dataSource = self (saved in my viewDidLoad)
this is my tableView
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell", for: indexPath)
cell.textLabel?.text = "\(indexPath.row + 1). \(taskArray[indexPath.row])"
cell.detailTextLabel?.text = dateArray[indexPath.row]
return cell
}
What Am I doing wrong?
thanks
You have multiple checks to do:
defaults.string(forKey: "task") has a return value that you dont use
As you append entered data, check if its has been append.
If entered value is there, check count of rows of the table.
I believe the key to solve your problem is in one of these points.
Took me a while but I finally figured
#IBAction func addTask(_ sender: Any) {
let date = datePicker.date
let dateStr = dateFormatter.string(from: date)
taskArray.append(selectedTask)
dateArray.append(dateStr)
defaults.set(taskArray, forKey: "task")
defaults.set(dateArray, forKey: "date")
defaults.synchronize()
dismiss(animated: true, completion: nil)
}
func loadUserDefaults() {
if let tempStr = defaults.stringArray(forKey: "task") {
taskArray = tempStr as [String]
}
if let tempDateStr = defaults.stringArray(forKey: "date") {
dateArray = tempDateStr as [String]
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
loadUserDefaults()
}
Related
I made a struct dictionary to get the user title and URL, and then I store them on the phone but when I come to retrieve the data in cellForRow method the cell label is empty, what should appear is the title.(tableView starts off empty until user starts to populate it with the AddArticle action)
So my question is if I'm doing it right because the cell label just turns out nil?
Struct Dictionary:
struct AddMagazine {
let rssUrl: String
let title: String
init(dict: [String : String]){
title = dict["title"] ?? ""
rssUrl = dict["rssUrl"] ?? ""
}
}
var userMagazineTitle = [AddMagazine]()
Getting values from textField:
#IBAction func AddArticle(_ sender: Any) {
animateIn()
tableView.isScrollEnabled = false
}
func addArticleTitle() {
let UserMagazines = AddMagazine.init(dict: ["title": RssTitle.text!, "rssUrl": RssText.text!])
let storedRssUrl = UserMagazines.rssUrl
self.dataString = storedRssUrl
//setting
defaults.set(dataString, forKey: "storedArray")
userMagazineTitle.append(UserMagazines)
tableView.reloadData()
}
Trying to retrieve title here:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyFeedTableViewCell
let headlineName = defaults.object(forKey: "storedArray") as? AddMagazine
cell.myHeadline.text = headlineName?.title
cell.indentationLevel = 3
return cell
}
You’re storing a String object in defaults for “storedArray” but then you typecast it to an AddMagazine when you read it from defaults. Change what you store or read it as a string.
I agree with #Joakim Danielson. You are storing storedRssUrl which is a string into userdefaults and while retrieving you are type casting as AddMagazine hence it will be nil.
self.dataString = storedRssUrl
//setting
defaults.set(dataString, forKey: "storedArray") --> Here you are storing string
let headlineName = defaults.object(forKey: "storedArray") as? AddMagazine --> Here you are fetching as AddMagazine.
//It should be like this
let headlineName = defaults.object(forKey: "storedArray") as? String
I have a project I'm close to completing. My last problem arises when I've downloaded CloudKit records to an array to be displayed in a tableview. Here is my code for the query portion of the controller.
for result in results!
{
let tablerestaurant = Restaurant()
if let name = result.value(forKey: "Name") as! String? {
tablerestaurant.name = name
}
// Do same for image
if let imageAsset = result.object(forKey: "Picture") as! CKAsset? {
if let data = try? Data(contentsOf: imageAsset.fileURL) {
tablerestaurant.image = UIImage(data: data)
}
}
self.tablerestaurantarray.append(tablerestaurant) // tablerestaurant is an array holding string and image instances of class Restaurant
self.restaurantArray.append(result) // restaurantArray holds CKRecords to be segued to the next controller
OperationQueue.main.addOperation( { () -> Void in
self.tableView.reloadData()
self.activity.isHidden = true
})}
and here is my cellForRowAtIndexPath Tablecell function, with the cache portion commented along with the global arrays here
var tablerestaurantarray: [Restaurant] = []
let cache = NSCache<NSString, Restaurant>()
-
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "restaurantcell") as? RestaurantTableCell
/////////this is the cache portion//////////////
var oneRestaurant: Restaurant = tablerestaurantarray[indexPath.row]
if let cachedVersion = cache.Restaurant(forKey: "image")
{
oneRestaurant = cachedVersion
}
else
{
oneRestaurant = Restaurant()
cache.setObject(oneRestaurant, forKey: "image")
}
//////////////////////////
let restaurant: Restaurant = tablerestaurantarray[indexPath.row]
cell?.name?.text = oneRestaurant.name
// cell?.picture?.image = oneRestaurant.image
return cell!
I've attempted to copy this simply cache procedure from here https://www.hackingwithswift.com/example-code/system/how-to-cache-data-using-nscache and transcribe it towards my own use. However, the debugger states that
NSCache<NSString, Restaurant> has no member Restaurant
which it does. So I'm lost as to my problem. Would you have an idea?
Update 2
var tablerestaurantarray: [Restaurant] = []
let cache = NSCache<NSString , Restaurant>()
var oneRestaurant: Restaurant = tablerestaurantarray[indexPath.row]
if let cachedVersion = cache.object(forKey: "image") { } //error is called here
{
oneRestaurant = cachedVersion
}
else
{
oneRestaurant = Restaurant()
cache.setObject(oneRestaurant, forKey: "image")
}
I am on a Timer project that takes a value from a variable through unwind segue to pass it to another view controller, then append its value to an Array that should be used to insert a row with the task name whenever the user pressed save button I need to save the result permanently, but I am confused which data should I save the value of the variable itself, the row, or the array?
var taskList = [String]()
#IBAction func saveToTaskList (segue: UIStoryboardSegue) {
let newItemViewController = segue.source as! AddTaskTableViewController
let name = newItemViewController.itemName
let date = newItemViewController.date
print("itemName passed is: \(name)")
if name == "" {
}
else {
print(date)
taskList.append(name!)
let indexToInsert = taskList.count == 0 ? 0 : taskList.count - 1
let indexPath = IndexPath(row: indexToInsert, section: 0)
tableView.insertRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
}
}
add task view controller
Timer / Task List
Ok, thanks to the hint of Matt Le Fleur
I solved the issue by using objects as below:
#IBAction func saveToTaskList (segue: UIStoryboardSegue) {
let newItemViewController = segue.source as! AddTaskTableViewController
var name = newItemViewController.itemName
let date = newItemViewController.date
let itemsObject = UserDefaults.standard.object(forKey: "items")
var items:[String]
if let tempItems = itemsObject as? [String] {
items = tempItems
items.append(name!)
print(items)
} else {
items = [name!]
}
UserDefaults.standard.set(items, forKey: "items")
name = ""
}
Then added a ViewDidAppear as below:
override func viewDidAppear(_ animated: Bool) {
let itemsObject = UserDefaults.standard.object(forKey: "items")
if let tempItems = itemsObject as? [String] {
items = tempItems
}
tableView.reloadData()
}
I have a tableView with messages.
Messages are store into parse.com.
I download asynchronously, put the message in a struct message array
import UIKit
var messMgr : messageObjet = messageObjet()
var date : NSDate = NSDate()
struct message {
var commentText = ""
var commentDate = ""
}
class messageObjet: NSObject {
var messageData = [message]()
func addMessage(comment : String, date : String) {
//messageData.append(message(commentText: comment, commentDate: date))
var mess = message(commentText: comment, commentDate: date)
messageData.insert(mess, atIndex: 0)
}
}
and populate my tableView
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CommentTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as CommentTableViewCell
cell.tag = indexPath.row
// Configure the cell...
// check if it is in the cache
println("the messageCache in cellForRow in commentTableViewController is \(self.commentCache.objectForKey(indexPath.row))")
if let messageCached: AnyObject = self.commentCache.objectForKey(indexPath.row){
cell.messageLabel.text = messageCached as? String
}
if let dateCached: AnyObject = self.dateCache.objectForKey(indexPath.row){
cell.dateLabel.text = dateCached as? String
}
else if messMgr.messageData.count != 0 {
if cell.tag == indexPath.row {
cell.messageLabel.text = messMgr.messageData[indexPath.row].commentText
self.commentCache.setObject(cell.messageLabel.text!, forKey: indexPath.row)
cell.dateLabel.text = messMgr.messageData[indexPath.row].commentDate
self.dateCache.setObject(cell.dateLabel.text!, forKey: indexPath.row)
}
}
return cell
}
I have modal viewController to add a new message.
In order to display immediately the message after dismissing the modal VC
i did this
#IBAction func sendComment(sender: AnyObject) {
let uuid = userDefaults.stringForKey("ApplicationUniqueIdentifier")
var comment = PFObject(className:"Comment")
comment.setObject(textToSend.text, forKey: "CommentText")
comment.setObject(post, forKey: "Post")
comment.setObject(uuid, forKey: "from")
comment.saveEventually()
let date = NSDate()
newMessage.commentText = textToSend.text as String
newMessage.commentDate = date.relativeTimeToString() as String
messMgr.messageData.insert(newMessage, atIndex: 0)
// messMgr.addMessage(textToSend.text as String, date: date.relativeTimeToString() as String)
NSNotificationCenter.defaultCenter().postNotificationName("reloadMessageTableView", object: nil)
println(messMgr.messageData)
self.dismissViewControllerAnimated(true, completion: nil)
}
The problem is when i come back to my tableView the message added to the index 0 is always displayed as the previous message and when i print my message array the index 0 message is the good one..
Any idea ?
Ok the problem was that i store the message in a NSCache with the indexPath as a key...
Removed this and everyThings work good.
I'm trying to build a simple app in which the user would input a date from a date picker(a birthday or any event), then the code would subtract today's date from the date the user gave the app, to get the difference in days and then display a countdown of the number of days left in a table view, along with the name of the event. I have 2 View Controllers, the main TableViewController and then the AddViewController. I've figured out how to get the difference in days, my table view is set up and all the code is running quite well. I just have a problem in displaying the number of days left in the table view as they are integers. I would love to get some help from more experienced developers as I'm relatively new to iOS programming. So without further due, here's my code:
#IBAction func addButtonTapped(sender: AnyObject) {
var chosen: NSDate = self.datePicker.date
var today: NSDate = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.DayCalendarUnit, fromDate: today, toDate: chosen, options: nil)
let secondsInADay = ((60 * 60) * 24)
let daysLeft = (components.hashValue / secondsInADay)
var userDefaults: NSUserDefaults = NSUserDefaults()
var itemList: NSMutableArray? = userDefaults.objectForKey("itemList") as? NSMutableArray
var dataSet: NSMutableDictionary = NSMutableDictionary()
dataset.setObject(eventTextField.text, forKey: "itemEventName")
dataSet.setObject(datePicker.date, forKey: "itemEventDate")
dataSet.setObject(daysLeft, forKey: "itemDaysLeft")
if ((itemList) != nil) {
var newMutableList: NSMutableArray = NSMutableArray()
for dict: AnyObject in itemList! {
newMutableList.addObject(dict as NSDictionary)
}
userDefaults.removeObjectForKey("itemList")
newMutableList.addObject(dataSet)
userDefaults.setObject(newMutableList, forKey: "itemList")
} else {
userDefaults.removeObjectForKey("itemList")
itemList = NSMutableArray()
itemList!.addObject(dataSet)
userDefaults.setObject(itemList, forKey: "itemList")
}
userDefaults.synchronize()
self.navigationController?.popToRootViewControllerAnimated(true)
}
So above is the code in my addViewController, users input a name from a text field, and a date from a date picker. Below is a part of the code in my tableViewController. Basically I want the Int daysLeft to be displayed in the cell.
var toDoItems: NSMutableArray = NSMutableArray()
override func viewDidAppear(animated: Bool) {
var userDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
var itemListFromUserDefaults: NSMutableArray? = userDefaults.objectForKey("itemList") as? NSMutableArray
if ((itemListFromUserDefaults) != nil) {
toDoItems = itemListFromUserDefaults!
}
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
var toDoItem: NSDictionary = toDoItems.objectAtIndex(indexPath.row) as NSDictionary
cell.textLabel?.text = toDoItem.objectForKey("itemDaysLeft") as? String
return cell
}
Put a break point on this line: var toDoItem: NSDictionary = toDoItems.objectAtIndex(indexPath.row) as NSDictionary to see what toDoItemContains, just to verify that it is actually what you expect it to be. But I think the below code may help.
Instead of:
cell.textLabel?.text = toDoItem.objectForKey("itemDaysLeft") as? String
try:
cell.textLabel?.text = NSString(format:"%#", toDoItem.objectForKey("itemDaysLeft")) as String