Now what I am adding an item to a cart it is appearing correctly. But when I am reopening the page, the data is lost. I am trying to store the cartTableView data in userdefaults. I have tried several methods but couldn't turn up. Here is my code for cartViewController :
import UIKit
class CartViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var arrdata = [jsonstruct]()
var categorydata = [Categories]()
var imgdata = [Images]()
var cartArray = [CartStruct]()
@IBOutlet var cartTableView: UITableView!
@IBOutlet var totalCount: UILabel!
@IBOutlet var subtotalPrice: UILabel!
@IBOutlet var shippingPrice: UILabel!
@IBOutlet var totalPrice: UILabel!
@IBOutlet var proceedBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
cartArray = (UserDefaults.standard.array(forKey: "cartt") as? [CartStruct])!
cartTableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cartArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CartCellTableViewCell", for: indexPath) as! CartCellTableViewCell
cell.cartImageView.downloadImage(from: cartArray[indexPath.row].cartItems.images.first?.src ?? "place_holder_image")
cell.productNameCart.text = cartArray[indexPath.row].cartItems.name
cell.prodductDescCart.text = cartArray[indexPath.row].cartItems.categories.first?.type
cell.productPriceCart.text = cartArray[indexPath.row].cartItems.price
cell.addBtn.addTarget(self, action: #selector(add(sender:)), for: .touchUpInside)
cell.addBtn.tag = indexPath.row
let cartQuantity = cartArray[indexPath.row].cartQuantity
cell.prodCount.text = "\(cartQuantity)"
if cartQuantity >= 0 {
cell.subBtn.isUserInteractionEnabled = true;
cell.subBtn.addTarget(self, action: #selector(sub(sender:)), for: .touchUpInside)
cell.subBtn.tag = indexPath.row
} else {
cell.subBtn.isUserInteractionEnabled = false;
}
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
cartArray.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.reloadData()
}
}
@objc func add(sender: UIButton){
if cartArray[sender.tag].cartQuantity >= 0 {
cartArray[sender.tag].cartQuantity = 1
cartTableView.reloadData()
}
}
@objc func sub(sender: UIButton){
if cartArray[sender.tag].cartQuantity > 0 {
cartArray[sender.tag].cartQuantity -= 1
cartTableView.reloadData()
}
}
}
But here is showing some runtime error : " Thread 1: Swift runtime failure: force unwrapped a nil value "
Please suggest me how can I fix the whole thing.. Thanks in advance!
CodePudding user response:
You can use UserDefaults
, But You should use core data or realm for better performance.
how to use this code?
// Save:
CartCache.save([Cart])
// Get data: let cart = CartCache.get()
struct CartCache {
static let key = "CartCache"
static func save(_ value: [CartStruct]!) {
UserDefaults.standard.set(try? PropertyListEncoder().encode(value), forKey: key)
}
static func get() -> [CartStruct]! {
var cartData: User!
if let data = UserDefaults.standard.value(forKey: key) as? Data {
cartData = try? PropertyListDecoder().decode([User].self, from: data)
return cartData!
} else {
return []
}
}
static func remove() {
UserDefaults.standard.removeObject(forKey: key)
}
}
CodePudding user response:
You should use different data saving methods other than user defaults. User defaults mostly used to save small informations like name, username, gender etc. I recommend you to use Core Data instead of user defaults and here is a very useful explanation and example projec of Core Data.
https://www.raywenderlich.com/7569-getting-started-with-core-data-tutorial
Have a great day :)
CodePudding user response:
As many people have mentioned UserDefaults is not really meant for storing this type of data. From the docs:
The UserDefaults class provides a programmatic interface for interacting with the defaults system. The defaults system allows an app to customize its behavior to match a user’s preferences. For example, you can allow users to specify their preferred units of measurement or media playback speed. Apps store these preferences by assigning values to a set of parameters in a user’s defaults database. The parameters are referred to as defaults because they’re commonly used to determine an app’s default state at startup or the way it acts by default.
However I feel like this is still a valid question and something that every IOS/MacOS developer should know how to use.
NSUserDefaults is simple and easy to use, but there are some things you have to understand.
Namely you can only store property list items.
A default object must be a property list—that is, an instance of (or for collections, a combination of instances of) NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.
This means that CartStruct cannot simply be persisted in UserDefaults, because it is not a NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary or a combination of these.
Now using UserDefaults is simple they have a singleton for the app called standard.
UserDefaults.standard.setValue("Hello Defaults", forKey: "Hello_Key")
let greeting = UserDefaults.standard.value(forKey: "Hello_Key")
print(greeting!)// prints Hello Dafaults
The next question becomes how to turn your struct into data. I'm sure if you ask that question on here and post the code for your struct you'll get a quick and easy solution. Once you have data you can save that in UserDefaults or better yet in the file system. In fact I think that really is the question your trying to ask is how to persist and retrieve your struct data for use in a tableview.
CodePudding user response:
Yes, we can store in UserDefaults, But it is not flexible as compare to Core Data or other storage for add, updates or delete operations.
Below code supposed to work.
You need to make "CartStruct" as Codable
struct CartStruct : Codable {
var cartItems: jsonstruct
var cartQuantity: Int
}
Next, Add this to insert and store it in tap "addToCartbtnTapped" .(DetailViewController)
func saveCart(data: CartStruct) {
let defaults = UserDefaults.standard
if let cdata = defaults.data(forKey: "cartt") {
var cartArray = try! PropertyListDecoder().decode([CartStruct].self, from: cdata)
cartArray.append(data)
cartCount.text = "\(cartArray.count)"
if let updatedCart = try? PropertyListEncoder().encode(cartArray) {
UserDefaults.standard.set(updatedCart, forKey: "cartt")
}
}
}
@IBAction func addToCartbtnTapped(_ sender: Any) {
if let info = detailInfo {
let cartData = CartStruct(cartItems: info, cartQuantity: 1)
self.saveCart(data: cartData)
showAlert()
(sender as AnyObject).setTitle("Go to Cart", for: .normal)
addToCartbtn.isUserInteractionEnabled = false
}
}
@IBAction func cartTappedToNavigate(_ sender: Any) {
let cart = self.storyboard?.instantiateViewController(withIdentifier: "CartViewController") as? CartViewController
self.navigationController?.pushViewController(cart!, animated: true)
}
Now, Fetch your cart data from where you stored.(CartViewController)
override func viewDidLoad() {
super.viewDidLoad()
self.getCartData()
}
func getCartData() {
let defaults = UserDefaults.standard
if let data = defaults.data(forKey: "cartt") {
cartArray = try! PropertyListDecoder().decode([CartStruct].self, from: data)
cartTableView.reloadData()
}
}