NSUserDefaults save and read in tableview as an Array - arrays

ok so I have the user input a name into a textfield and a button passes the name to a tableview in another view, the table view lists the name but when a new name is added it overwrites the previous name instead of listing all names added to the tableview. here is my code:
viewcontroller1:
#IBAction func addPlant(sender: AnyObject) {
let array = self.title
NSUserDefaults.standardUserDefaults().setObject(array, forKey: "userName")
NSUserDefaults.standardUserDefaults().synchronize()
}
viewcontroller2:
#IBOutlet weak var tableView: UITableView!
var userDefaults = NSUserDefaults.standardUserDefaults()
var ourText = String()
var textArray:[String] = [String]()
override func viewDidLoad() {
super.viewDidLoad()
//self.tableview.delegate = self
self.tableView.dataSource = self
ourText = userDefaults.stringForKey("userName")!
textArray.append(ourText)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return textArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = textArray[indexPath.row]
return cell
}

You can use
textArray = NSUserDefaults.standardUserDefaults().arrayForKey("userName")
Or
textArray = NSUserDefaults.standardUserDefaults().objectForKey("userName")

Related

How to implement userDefaults with a Global Variable array

I have an empty array set as a global variable that is populated with array items from a tableview. This is used to populate another tableview. This data needs to persist so that when the user returns to the app, their tableview data is in the same state they left it, i.e. populate with data from the array.
Though I've looked for dozens of tutorials and examples. I've also hacked at it myself to make it work and every time I reload the app, the array is empty. How can I get that global variable array to hold onto it's array data?
var sharedData = [String]()
This is my 1st VC where I have setup functions for the UserDefaults. And I've executed my saveArray() func every time a change is made to the array. I've then executed retrieveArray() func every time I need to load from the array.
import UIKit
var sharedData = [String]()
struct Keys {
static let arrayKey = "arrayKey"
}
let defaults = UserDefaults.standard
func saveArray() {
defaults.set(sharedData, forKey: Keys.arrayKey)
}
func retrieveArray() {
var savedData = defaults.object(forKey: Keys.arrayKey) as? [String] ?? []
savedData.append(contentsOf: sharedData)
}
class ViewController: UIViewController {
var effect:UIVisualEffect!
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet var tableView: UITableView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var visualEffectView: UIVisualEffectView!
let materialData = ["One", "Two", "Three", "Four"]
var searchMaterial = [String]()
var searching = false
#IBAction func favoritesButtonArrayUpdate(_ sender: UIBarButtonItem) {
print(sharedData)
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
saveArray()
retrieveArray()
print(sharedData)
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
print(self.materialData[indexPath.row], "selected!")
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let favorite = UITableViewRowAction(style: .default, title: "Favorite") { (action, indexPath) in
var data: String
if self.searching {
data = self.searchMaterial[indexPath.row]
} else {
data = self.materialData[indexPath.row]
}
sharedData.append(data)
saveArray()
print(sharedData)
}
favorite.backgroundColor = UIColor.orange
return [favorite]
}
}
This is my 2nd VC which displays the array data stored in the global variable array sharedData. I've again added all the func when making changes to the array and pulling data from the array.
import UIKit
class FavoritesViewController: UIViewController {
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
saveArray()
retrieveArray()
}
}
extension FavoritesViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
retrieveArray()
return sharedData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
retrieveArray()
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.text = sharedData[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
sharedData.remove(at: indexPath.row)
saveArray()
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
}
}
}
The problem could be here:
let savedData: [String] = userDefaults.object(forKey: "arrayKey") as? [String] ?? []
Try changing it with:
let savedData: [String] = userDefaults?.object(forKey: "arrayKey") as? [String] ?? []
This is because UserDefaults must be unwrapped to refer to member object. Give it a try
Based on MrHim recommendations I removed the saveArray and retrieveArray func from the viewDidLoad of my first VC and left retrieveArray in viewDidLoad of my second VC. Having saveArray in my viewDidLoads was overwriting the array with empty data. I then needed to retrieve the array data in the proper place in my second VC. Then in my numberOfRowsInSection I removed retrieveArray.

How to save an Array in UserDefault

I'm using a UITabBarController to create a contact list, but when I'm trying to save the array to load the data when I restart the app is giving me problems where the data isn't displayed. I'm using UserDefaults to save the data and the restore when the app is restarted.
In this code I sent data from a textfield to the array named list.
import UIKit
class NewContactoViewController: UIViewController {
#IBOutlet weak var input: UITextField!
#IBAction func add(_ sender: Any) {
if (input.text != "") {
list.append(input.text!)
UserDefaults.standard.set(list, forKey: "SavedValue")
input.text = ""
}
}
}
In this code I'm printing the data in a table, and trying to save it with user defaults.
import UIKit
var list = [String]()
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let x = UserDefaults.standard.object(forKey: "SavedValue") as? String {
return (x.count)
}
return (0)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
if let x = UserDefaults.standard.dictionary(forKey: "SavedValue") as? String {
cell.textLabel?.text = [x[indexPath.row]]
}
return(cell)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
list.remove(at: indexPath.row)
myTableView.reloadData()
}
}
override func viewDidAppear(_ animated: Bool) {
myTableView.reloadData()
}
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
You are saving an array of strings but you are reading a single string (or even a dictionary) which obviously cannot work. There is a dedicated method stringArray(forKey to read a string array.
Apart from the issue never read from UserDefaults to populate the data source in the table view data source and delegate methods, do it in viewDidLoad or viewWillAppear for example
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let savedArray = UserDefaults.standard.stringArray(forKey: "SavedValue") {
list = savedArray
}
myTableView.reloadData()
}
Put the data source array in the view controller. A global variable as data source is very bad programming habit.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var list = [String]()
...
In numberOfRowsInSection return the number of items in list and return is not a function, there are no parentheses
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
Same in cellForRow. Get the item from list and use reusable cells and again, return is not a function.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = list[indexPath.row]
return cell
}
Note :
Consider that UserDefaults is the wrong place to share data between view controllers. Use segues, callbacks or protocol / delegate.

Table View To seperate view with segue

This has stumped me and Ive tried a few ways of doing of tutorials and on stack answers but its still not building.
So basically:
Im getting core data and then placing that into an array. (works fine)
After that Im just display first and last name in the cells (works fine)
User taps on cell to see athleteDetalView
I have placed a few print statements to see where its going wrong.
Thanks In advance for your input.
import UIKit
import CoreData
class athleteViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//labels
#IBOutlet weak var athleteLabel: UILabel!
//buttons
#IBOutlet weak var athleteCreate: UIBarButtonItem!
#IBOutlet weak var athleteTableView: UITableView!
var athleteArray:[Athlete] = []
var myIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
athleteTableView.delegate = self
athleteTableView.dataSource = self
self.fetchData()
self.athleteTableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return athleteArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "athleteName", for: indexPath)
let name = athleteArray[indexPath.row]
cell.textLabel!.text = name.firstName! + " " + name.lastName!
let athleteName = name.firstName!
let lastName = name.lastName!
let age = name.age!
let sport = name.sport!
let email = name.email!
print(athleteName)
print(lastName)
print(age)
print(sport)
print(email)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myIndex = indexPath.row
let currentCell
//let storyboard = UIStoryboard(name: "Main", bundle: nil)
//let destination = storyboard.instantiateViewController(withIdentifier: "athleteDetailsViewController") as! athleteDetailsViewController
//let athName = athleteArray[myIndex]
//testdata
//test data
performSegue(withIdentifier: "athleteDetailsSegue", sender: self)
//self.navigationController?.pushViewController(destination, animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "athleteDetailsSegue") {
var destination = segue.destination as! athleteDetailsViewController
destination.firstNameString = athleteName
destination.lastNameString = lastName
destination.ageString = age
destination.sportString = sport
destination.emailString = email
//destination.getImage = name.image
}
}
func fetchData(){
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do{
athleteArray = try context.fetch(Athlete.fetchRequest())
}
catch{
print(error)
}
}
The variables that you are setting in tableView:cellForRowAt:indexPath: are local variables and are not available in the function prepareForSegue:. Try to declaring the variables at the top as properties of the class.

Swift: How can I get the records from the database according to the cell pressed

Is it possible to get the data from the external database when a UItableViewCell is pressed?
I managed to create a UItableView where I am displaying the data from the database. If I press a cell then all the data that are linked to it should be displayed. For eg. if I have 4 main categories in the database such as TOOLS, OTHERS, SECURITY, PETS and each of them has its sub-catecory and are linked with each other in the database. So if I click on Pets, it should filter out and only Show me CATS, DOGS, COWS, LIONS. When I run this SQL I am able to get the information but cant figure it this out on Swift.
UItableViewCell is in my FirstviewController and its the Main Category .
When I click here it goes to my destination VC and has the table again in here.enter image description here
DestViewController is the sub-category
enter image description here
My CategoryList_ViewController.swift
import Foundation
import UIKit
import WebKit
class CategoryList_ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
#IBAction func refresh(sender: AnyObject) {
get()
}
var values:NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
get();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func get(){
let url = NSURL(string: "c:\deskstop\mobiletec\assignment\assignment2\cat.php")
let data = NSData(contentsOfURL: url!)
values = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
tableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return values.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CategoryList_TableViewCell
let maindata = values[indexPath.row]
cell.categoryLabel.text = maindata["NAME"] as? String
return cell;
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "catView" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let value = values[indexPath.row]
let controller = segue.destinationViewController as! SubCatergory_ViewController
controller.cate_Id = value["id"] as! String
controller.catTitleRec = value["NAME"] as! String
}
}
}
}
my SubCatergory_ViewController.swift
import Foundation
import UIKit
import WebKit
class SubCatergory_ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var caID: UILabel!
#IBOutlet weak var catTitle_Label: UILabel!
#IBAction func refresh(sender: AnyObject) {
get()
}
var values:NSArray = []
var catTitleRec = ""
var cate_Id = ""
override func viewDidLoad() {
super.viewDidLoad()
catTitle_Label.text = catTitleRec
caID.text = cate_Id
get();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func get(){
let request = NSMutableURLRequest(URL: NSURL(string: "c:\deskstop\mobiletec\assignment\assignment2\subcat.php")!)
request.HTTPMethod = "GET"
let postString = "a=\(cate_Id)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return values.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! subCateListTableViewCell
let maindata = values[indexPath.row]
cell.categoryLabel.text = maindata["NAME"] as? String
return cell;
}
}
and my subcat.php
<?php
$connection = mysql_connect(........);
$catefilter = $_GET['a'];
if(!$connection){
die('Connection Failed');
}
else{
$dbconnect = #mysql_select_db($database_UNIASSIGNMENT, $connection);
if(!$dbconnect){
die('Could not connect to Database');
}
else{
$query = 'SELECT category_group.group_id , category.NAME FROM category_group LEFT JOIN category ON category.id = category_group.category_id WHERE category_group.group_id =' . $catefilter ;
$resultset = mysql_query($query, $connection);
$records= array();
while($r = mysql_fetch_assoc($resultset)){
$records[] = $r;
}
echo json_encode($records);
}
}
?>
My first VC works fine but my second VC doesnot get the data
Thanks for your time :)
SK
To access the cell that has been pressed, you need to call the didSelectRowAtIndexPath function.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let value = values[indexPath.row]
let vc = storyboard?.instantiateViewController(withIdentifier: "SubCatergory_ViewController") as! SubCatergory_ViewController
vc.cate_Id = value["NAME"] as! String
//self.navigationController?.pushViewController(vc, animated: true)
self.present(vc, animated: true, completion: nil)
}
First you get the value out of your values Array on the indexPark.row. Then you instantiate your second viewController.
Then you set your String value of cate_Id to the desired String value of your item value. And then you just need to present the new viewController.
If you're using a UINavigationController and you want a "back" button, then you use: self.navigationController?.pushViewController(vc, animated: true)
If you just want to present the viewController, you use self.present(vc, animated: true, completion: nil)
Comment or uncomment whatever presentation method you prefer.

populate dynamic array from segue swift

for the first view i prepare to segue 3 textFields:(Sorry for my bad english as well)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "salvar") {
var view = segue.destinationViewController as! SecondView
view.end = endereco.text
view.area = area.text
view.data = data.text
}
}
the problem is on secondView the array is repeating (and don't add new data to array, only replace it), making the 3 labels from cell repeating with the same data, heres the secondView code:
class secondView: UITableViewController {
var end = ""
var area = ""
var data = ""
var array: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
self.array = [end, area, data]
}
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 self.array.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! AgendaCell
cell.label1.text = self.array[indexPath.row] as String
cell.label2.text = self.array[indexPath.row] as String
cell.label3.text = self.array[indexPath.row] as String
return cell
}
how i can really add new data to array, and place each for the ordered label from cell?

Resources