In my Swift iOS app, I have a table view which is supposed to show JSON objects from a mysql server, like a blog reader app. BUT now the table view is showing up empty because I tried implementing NSUserDefaults to save when the user clicks a button on each row to move to another section. I'm basically saving the indexPath and Section of the row that the user clicked on so when the user opens the app the next time, it remember where it was moved to. But the table view is showing up empty and I have no idea on how to fix it, hopefully someone here does. Thank You!
ViewController.swift
// Outlets and Variables
@IBOutlet weak var myTableView: UITableView!
let searchController = UISearchController(searchResultsController: nil)
var jsonArray: NSMutableArray = []
var testArray = [Test]()
var followedArray = [Test]()
var filteredArray = [Test]()
// NSUserDefaults
var data: [Any]?
var items: [[Any]]?
override func viewDidLoad() {
super.viewDidLoad()
// Custom Cell
self.myTableView.dataSource = self
self.myTableView.delegate = self
// Load Data from Server
self.retrieveData()
// NSUserDefaults
self.fetchData()
}
// Number of Rows in Section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items?[section].count ?? 0
}
// Number of Sections
func numberOfSections(in tableView: UITableView) -> Int {
return self.items?.count ?? 0
}
// CellForRowAt indexPath
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let CellIdentifier = "Cell"
var cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! CustomCell
if cell != cell {
cell = CustomCell(style: UITableViewCellStyle.default, reuseIdentifier: CellIdentifier)
}
// Configuring the cell
var testObject: Test
if !(searchController.isActive && searchController.searchBar.text != "") {
if indexPath.section == 0 {
testObject = followedArray[indexPath.row]
cell.populateCell(testObject, isFollowed: true, indexPath: indexPath, parentView: self)
}
else if indexPath.section == 1 {
testObject = testArray[indexPath.row]
cell.populateCell(testObject, isFollowed: false, indexPath: indexPath, parentView: self)
}
}
else {
testObject = filteredArray[indexPath.row]
cell.populateCell(testObject, isFollowed: false, indexPath: indexPath, parentView: self)
}
return cell
}
// Follow Button
@IBAction func followButtonClick(_ sender: UIButton!) {
// Adding row to tag
let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: self.myTableView)
if let indexPath = self.myTableView.indexPathForRow(at: buttonPosition) {
let cell = self.myTableView.cellForRow(at: indexPath) as! CustomCell
// Change Follow to Following
(sender as UIButton).setImage(UIImage(named: "follow.png")!, for: .normal)
// Checking wether to import from testArray or filteredArray to followedArray
if !(searchController.isActive && searchController.searchBar.text != "") {
self.myTableView.beginUpdates()
// ----- Inserting Cell to followedArray -----
followedArray.insert(testArray[indexPath.row], at: 0)
myTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .fade)
// ----- Removing Cell from testArray -----
testArray.remove(at: indexPath.row)
let rowToRemove = indexPath.row
self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 1)], with: .fade)
self.myTableView.endUpdates()
// NSUserDefaults
saveSorting() { "\($0)" }
myTableView.reloadData()
}
else {
self.myTableView.beginUpdates()
// ----- Inserting Cell to followedArray -----
let testObject: Test = filteredArray[indexPath.row]
let indexOfObjectInArray = testArray.index(of: testObject)
followedArray.insert(testObject, at: 0)
// ----- Removing Cell from filteredArray -----
filteredArray.remove(at: indexPath.row)
testArray.remove(at: indexOfObjectInArray!)
let rowToRemove = indexPath.row
self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 0)], with: .fade)
self.myTableView.endUpdates()
// NSUserDefaults
saveSorting() { "\($0)" }
myTableView.reloadData()
}
}
}
// Retrieving Data from Server
func retrieveData() {
let getDataURL = "http://exampleip.org/get.php"
let url: NSURL = NSURL(string: getDataURL)!
do {
let data: Data = try Data(contentsOf: url as URL)
jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray
// Looping through jsonArray
for i in 0..<jsonArray.count {
// Create Test Object
let tID: String = (jsonArray[i] as AnyObject).object(forKey: "id") as! String
let tName: String = (jsonArray[i] as AnyObject).object(forKey: "testName") as! String
// Add Test Objects to Test Array
testArray.append(Test(testName: tName, andTestID: tID))
}
}
catch {
print("Error: (Retrieving Data)")
}
myTableView.reloadData()
}
// NSUserDefaults
func fetchData() {
// request from remote or local
data = [testArray]
// Update the items to first section has 0 elements,
// and place all data in section 1
items = [[], data ?? []]
// apply ordering
applySorting() { "\($0)" }
// save ordering
saveSorting() { "\($0)" }
// refresh the table view
myTableView.reloadData()
}
func applySorting(_ dataIdBlock: (Any) -> String) {
// get all saved ordering
guard let data = self.data else { return }
let ordering = DataHandling.allSavedOrdering(data.count)
var result: [[Any]] = [[], []]
for (section, ordering) in ordering {
guard section <= 1 else { continue } // make sure the section is 0 or 1
let rows = data.filter({ obj -> Bool in
return ordering.index(where: { $0.dataId == .some(dataIdBlock(obj)) }) != nil
})
result[section] = rows
}
self.items = result
}
func saveSorting(_ dataIdBlock: (Any) -> String) {
guard let items = self.items else { return }
for (section, rows) in items.enumerated() {
for (row, item) in rows.enumerated() {
let indexPath = IndexPath(row: row, section: section)
let dataId = dataIdBlock(item)
let ordering = DataHandling(dataId: dataId, indexPath: indexPath)
// Warning is here
ordering.save(defaults: indexPath.defaultsKey)
}
}
}
}
extension IndexPath {
var defaultsKey: String {
return "data_handling_\(section)_\(row)"
}
}
CustomCell.swift
class CustomCell: UITableViewCell {
func populateCell(_ testObject: Test, isFollowed: Bool, indexPath: IndexPath, parentView: Any) {
if isFollowed {
self.followedButton.tag = indexPath.row
self.followedButton.addTarget(parentView, action: #selector(ViewController.followedButtonClick(_:)), for: .touchUpInside)
self.followedButton.isHidden = false
self.followButton.isHidden = true
}
else {
self.followButton.tag = indexPath.row
self.followButton.addTarget(parentView, action: #selector(ViewController.followButtonClick(_:)), for: .touchUpInside)
self.followedButton.isHidden = true
self.followButton.isHidden = false
}
}
}
DataHandling.swift Handling NSUserDefault
class DataHandling: NSObject, NSCoding {
var indexPath: IndexPath?
var dataId: String?
init(dataId: String, indexPath: IndexPath) {
super.init()
self.dataId = dataId
self.indexPath = indexPath
}
required init(coder aDecoder: NSCoder) {
if let dataId = aDecoder.decodeObject(forKey: "dataId") as? String {
self.dataId = dataId
}
if let indexPath = aDecoder.decodeObject(forKey: "indexPath") as? IndexPath {
self.indexPath = indexPath
}
}
func encode(with aCoder: NSCoder) {
aCoder.encode(dataId, forKey: "dataId")
aCoder.encode(indexPath, forKey: "indexPath")
}
func save(defaults box: String) -> Bool {
let defaults = UserDefaults.standard
let savedData = NSKeyedArchiver.archivedData(withRootObject: self)
defaults.set(savedData, forKey: box)
return defaults.synchronize()
}
convenience init?(defaults box: String) {
let defaults = UserDefaults.standard
if let data = defaults.object(forKey: box) as? Data,
let obj = NSKeyedUnarchiver.unarchiveObject(with: data) as? DataHandling,
let dataId = obj.dataId,
let indexPath = obj.indexPath {
self.init(dataId: dataId, indexPath: indexPath)
} else {
return nil
}
}
class func allSavedOrdering(_ maxRows: Int) -> [Int: [DataHandling]] {
var result: [Int: [DataHandling]] = [:]
for section in 0...1 {
var rows: [DataHandling] = []
for row in 0..<maxRows {
let indexPath = IndexPath(row: row, section: section)
if let ordering = DataHandling(defaults: indexPath.defaultsKey) {
rows.append(ordering)
}
rows.sort(by: { $0.indexPath! < $1.indexPath! })
}
result[section] = rows
}
return result
}
}