I'm currently trying to load different entities from my CoreData model into one UITableView but under different sections. I've tried the following method:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
// #warning Incomplete method implementation.
// Return the number of rows in the section.
var rows = 0
if section == 0 {
rows = crew.count
} else if section == 1 {
rows = aircraft.count
} else if section == 2 {
rows = batteries.count
}
return rows
}
But that doesn't seem to work. It returns the first crew.count for every section. I should note that crew, aircraft and batteries are arrays of NSManagedObject.
Does anyone have any advice on how to implement what I'm looking for?
Thanks.
EDIT: Here are other methods I'm implementing for clarity..
var crew = [NSManagedObject]()
var aircraft = [NSManagedObject]()
var batteries = [NSManagedObject]()
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let crewFetchRequest = NSFetchRequest(entityName:"Crew")
var crewError: NSError?
let crewFetchedResults = managedContext.executeFetchRequest(crewFetchRequest, error: &crewError) as? [NSManagedObject]
if let crewResults = crewFetchedResults {
crew = crewResults
} else {
println("Could not fetch \(crewError), \(crewError!.userInfo)")
}
let aircraftFetchRequest = NSFetchRequest(entityName:"Aircraft")
var aircraftError: NSError?
let aircraftFetchedResults = managedContext.executeFetchRequest(aircraftFetchRequest, error: &aircraftError) as? [NSManagedObject]
if let aircraftResults = crewFetchedResults {
aircraft = aircraftResults
} else {
println("Could not fetch \(aircraftError), \(aircraftError!.userInfo)")
}
let batteryFetchRequest = NSFetchRequest(entityName:"Battery")
var batteryError: NSError?
let batteryFetchedResults = managedContext.executeFetchRequest(batteryFetchRequest, error: &batteryError) as? [NSManagedObject]
if let batteryResults = crewFetchedResults {
batteries = batteryResults
} else {
println("Could not fetch \(batteryError), \(batteryError!.userInfo)")
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 3
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
// #warning Incomplete method implementation.
// Return the number of rows in the section.
var rows = 0
if section == 0 {
rows = crew.count
} else if section == 1 {
rows = aircraft.count
} else if section == 2 {
rows = batteries.count
}
return rows
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String?
{
return sectionTitles[section]
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("CrewCell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
switch indexPath.section.description
{
case "0":
cell.textLabel!.text = crew[indexPath.item].valueForKey("name") as? String
if crew[indexPath.item].valueForKey("pilot") as? Bool == true {
cell.detailTextLabel!.text = "Pilot"
} else {
cell.detailTextLabel!.text = " "
}
break;
case "1":
cell.textLabel!.text = "Aircraft"
break;
case "2":
cell.textLabel!.text = "Battery"
break;
default:
break;
}
return cell
}
Your code is good, but you will need to set the number of section in tableview as well by implementing
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3
}
EDITED:
From the edited question I can now clearly see that your problem is with your data source
let aircraftFetchRequest = NSFetchRequest(entityName:"Aircraft")
var aircraftError: NSError?
let aircraftFetchedResults = managedContext.executeFetchRequest(aircraftFetchRequest, error: &aircraftError) as? [NSManagedObject]
if let aircraftResults = crewFetchedResults {
aircraft = aircraftResults
} else {
println("Could not fetch \(aircraftError), \(aircraftError!.userInfo)")
}
let batteryFetchRequest = NSFetchRequest(entityName:"Battery")
var batteryError: NSError?
let batteryFetchedResults = managedContext.executeFetchRequest(batteryFetchRequest, error: &batteryError) as? [NSManagedObject]
if let batteryResults = crewFetchedResults {
batteries = batteryResults
}
if let aircraftResults = crewFetchedResults and if let batteryResults = crewFetchedResults will return crewFetchResults so all crew,aircrafts and batteries arrays are holding exactly the same elements. You must replace these lines with if let aircraftResults = aircraftFetchedResults and if let batteryResults = batteryFetchedResults. Hope I was clear enough
Related
I wanted to know how to load selected data from my table view, for example when selecting segment index is equal 1, the table view will reload and will only show data which status is equal to approved. Cause as you have seen from my below code, I have loaded all the data with all the statuses: . for example if segmentView.selectedSegmentIndex == 1 table will reload with the data which status is equal to approved. I could already determine the selected index. what i want is how to access those data from the table view that i could load selected data depending to status
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "ToDoListTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ToDoListTableViewCell
cell.delegate = self
// let toDoActionItem = fetchedResultsController.object(at: indexPath)
if let getTempDetails: [String : Any] = getAllDetail[indexPath.row] {
print("ang tanan data:" , getTempDetails)
if let str = getTempDetails["status"] as? [String: String] {
if let name = str["name"] {
if name == "ongoing" {
cell.toDoItemLabel.text = getTempDetails["name"] as? String
cell.statuslabel.backgroundColor = created
// cell.label.textColor = UIColor(red: 0.9294, green: 0.3333, blue: 0.1804, alpha: 1.0)
// cell.backgroundColor = created
}
else if name == "approved" {
cell.toDoItemLabel.text = getTempDetails["name"] as? String
cell.statuslabel.backgroundColor = done
cell.checkBoxButton.isSelected = true
}
else if name == "for approval" {
cell.toDoItemLabel.text = getTempDetails["name"] as? String
cell.statuslabel.backgroundColor = pending
}else if name == "near expiry" {
cell.toDoItemLabel.text = getTempDetails["name"] as? String
cell.statuslabel.backgroundColor = neardue
} else if name == "expired" {
cell.toDoItemLabel.text = getTempDetails["name"] as? String
cell.statuslabel.backgroundColor = expired
} else {
print("false")
cell.toDoItemLabel.text = "LOLS"
}
}
}
}
code for segment (in selecting segment)
func selectSegmentInSegmentView(segmentView: SMSegmentView) {
if segmentView.selectedSegmentIndex == 1 {
print("ang index nga emo ge click is one")
// let selectedSegment : SMSegment = segmentView.selectedSegment!
// self.userName = selectedSegment.label.text!
} else {
logic here
}
self.setUpTableView()
or could be self.tableView.reloadData()
}
The best option Would be like:
make a common array for Display, that will be used to display the data in tableView:
and the Use it like this for all the conditions like:
make a Common Function like this:
func filterDataStatusWise(strStatus:String){
for dict in arrMainResponse{
let strStatus = arrMainResponse["status"]
if strStatus["name"] = strStatus{
//Whatever data add in arrForDisplay here
}
tableView.reloadData()
}
}
And then Use it like this:
var arrForDisplay = [String:Any]()
if condition1{
filterDataStatusWise(strStatus: "ongoing")
}else if condition2{
filterDataStatusWise(strStatus: "approved")
}else if condition3{
filterDataStatusWise(strStatus: "for approval")
}else if condition4{
filterDataStatusWise(strStatus: "near expiry")
}else{
filterDataStatusWise(strStatus: "expired")
}
Hope it helps!
You just need to create 5 separate arrays to load while selection of different segment of UISegmentControl.
var ongoingArr = [[String: Any]]() // For segment index 0
var approvedArr = [[String: Any]]() // For segment index 1
var forApprovalArr = [[String: Any]]() // For segment index 2
var nearExpiryArr = [[String: Any]]() // For segment index 3
var expiredArr = [[String: Any]]() // For segment index 4
You have the whole data getAllDetail, and you are getting the data in this array by API or previous screen:
var getAllDetail = [[String: Any]]()
If you are getting the data from API, then load above 5 arrays after loading the data in getAllDetail. Fo that just create an extension of array as:
extension Array where Element == [String: Any] {
func filterArray(_ statusName: String) -> [Element] {
return self.filter { infoDict -> Bool in
if let statusDict = infoDict["status"] as? [String: String], let name = statusDict["name"] {
return name == statusName
}
return false
}
}
}
and, load above 5 arrays:
func loadSegmentArray() {
ongoingArr = getAllDetail.filterArray("ongoing")
approvedArr = getAllDetail.filterArray("approved")
forApprovalArr = getAllDetail.filterArray("for approval")
nearExpiryArr = getAllDetail.filterArray("near expiry")
expiredArr = getAllDetail.filterArray("expired")
// Select 0th index of segment and reload table
segmentView.selectedSegmentIndex = 0
self.setUpTableView() // Reload Table view
}
In your func selectSegmentInSegmentView, just reload table view:
func selectSegmentInSegmentView(segmentView: SMSegmentView) {
self.setUpTableView()
}
And update your UITabelView delegate and datasource methods, according to these 5 arrays.
For Sample, I am writing numberOfRowsInSection and cellForRowAt indexPath:
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch segmentView.selectedSegmentIndex {
case 0:
return ongoingArr.count
case 1:
return approvedArr.count
case 2:
return forApprovalArr.count
case 3:
return nearExpiryArr.count
case 4:
return expiredArr.count
default:
return 0
}
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "ToDoListTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ToDoListTableViewCell
cell.delegate = self
var infoDict = [String: Any]()
switch segmentView.selectedSegmentIndex {
case 0:
infoDict = ongoingArr[indexPath.row]
cell.statuslabel.backgroundColor = // Color of onging
case 1:
infoDict = approvedArr[indexPath.row]
cell.statuslabel.backgroundColor = // Color of approvedArr
case 2:
infoDict = forApprovalArr[indexPath.row]
cell.statuslabel.backgroundColor = // Color of forApprovalArr
case 3:
infoDict = nearExpiryArr[indexPath.row]
cell.statuslabel.backgroundColor = // Color of nearExpiryArr
case 4:
infoDict = expiredArr[indexPath.row]
cell.statuslabel.backgroundColor = // Color of expiredArr
default:
cell.statuslabel.backgroundColor = .black
}
cell.toDoItemLabel.text = infoDict["name"] as? String
return cell
}
Filter array with respect to selected index(status):
func tableView(tableView: UITableView, numberOfRowsInSection section:
Int) -> Int {
switch segmentView.selectedSegmentIndex {
case 0:
if self.expiredArray == nil {
return 0
}
return (self.expiredArray?.count)!
case 1:
if self.approvedArray == nil {
return 0
}
return (self.complaintArray?.count)!
default:
break
}
return 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch segmentView.selectedSegmentIndex {
case 0:
// Load from expired array
case 1:
// Load from approved array
default:
break
}
return UITableViewCell()
}
func selectSegmentInSegmentView(segmentView: SMSegmentView) {
if segmentView.selectedSegmentIndex == 1 {
// Write your code to filter array here based on status
} else {
// Write your code to filter array here based on status
}
// reload tableview here.....
}
I am following this tutorial for expanding and collapsing my table view section. As this demo is done in swift 2.2 I have made all the changes according to swift 3.0 . I am stuck at the below function at if condition(currentSectionCells[row]["isVisible"]) which gives me error as "Type 'NSFastEnumerationIterator.Element' (aka 'Any' has no subscript members)'".
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0...((currentSectionCells as! [[String: AnyObject]]).count - 1) {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
I have tried type casting it as below
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0...((((currentSectionCells) as? NSMutableArray)?.count)! - 1) {
let temp = [currentSectionCells][row] as? NSMutableDictionary
let temp2 = temp?["isVisible"] as! Bool
if temp2 == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
But this gives me a crash at runtime on this line "let temp2 = temp?["isVisible"] as! Bool"
Crash says "EXC_BAD_INSTRUCTION" and the temp shows as nil.
Please help guys. TIA
Table View Delegate and Data Source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if cellDescriptors != nil {
return cellDescriptors.count
}
else {
return 0
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return visibleRowsPerSection[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath: indexPath as NSIndexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! CustomCell
if currentCellDescriptor["cellIdentifier"] as! String == "sectionCellIdentifier" {
if let primaryTitle = currentCellDescriptor["secondaryTitle"]
{
cell.sectionTitleLabel.text = primaryTitle as? String
}
}
else if currentCellDescriptor["cellIdentifier"] as! String == "shortAnswerCell" {
cell.questionTitle.text = currentCellDescriptor["primaryTitle"] as? String
cell.questionTextView.text = currentCellDescriptor["secondaryTitle"] as? String
cell.answerTextView.text = currentCellDescriptor["answerTitle"] as? String
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let temp = cellDescriptors[indexPath.section] as? NSArray
let temp2 = temp?[indexOfTappedRow ] as? NSDictionary
let temp3 = temp2?["isExpandable"] as! Bool
if temp3 == true {
var shouldExpandAndShowSubRows = false
if temp3 == false {
// In this case the cell should expand.
shouldExpandAndShowSubRows = true
}
temp2?.setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (temp2?["additionalRows"] as! Int)) {
(temp![i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
getIndicesOfVisibleRows()
tblExpandable.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: UITableViewRowAnimation.fade)
}
I worked on that tutorial as well and completed it successfully in swift3.Your solution is given below modify accordingly.
class yourClass: UIViewController
{
#IBOutlet weak var profileTableView: UITableView!
internal var visibleRowsPerSection = [[Int]]()
internal var cellDescriptors: NSMutableArray!
// VIEW DID LOAD
override func viewDidLoad() {
super.viewDidLoad()
profileTableView.showsVerticalScrollIndicator = false
loadProfileControllerData()
profileTableSetUp()
// Do any additional setup after loading the view.
}
func loadProfileControllerData(){
if let path = Bundle.main.path(forResource: "CellDescriptor", ofType: "plist") {
cellDescriptors = NSMutableArray(contentsOfFile: path)
}
getIndicesOfVisibleRows()
profileTableView.reloadData()
}
// SHOW PARENT VISIBLE ROWS AND SAVE THERE ROW INDEX IN ARRAY
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors.objectEnumerator().allObjects as! [[[String:Any]]]{
var visibleRows = [Int]()
for row in 0..<currentSectionCells.count {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
print(visibleRowsPerSection)
}
}
// GET REQUIRED OBJECT OF TYPE [String: Any]
func getCellDescriptorForIndexPath(indexPath: NSIndexPath) -> [String: Any] {
let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let cellDescriptorss = cellDescriptors[indexPath.section] as! NSArray
let data = cellDescriptorss.object(at: indexOfVisibleRow) as! [String:Any]
return data
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//----------------------
// EXTENSION TO OUR PROFILE CLASS THAT DETERMINE OUR CLASS CONFIRM 2 IMPORTANT DELEGATES
extension profileViewController : UITableViewDelegate,UITableViewDataSource{
//MARK-: TABLE VIEW DELEGATE FUNCTIONS
// RETURN NUMBER OF SECTION IN TABLE VIEW
public func numberOfSections(in tableView: UITableView) -> Int{
if cellDescriptors.count != 0{
return cellDescriptors.count
}
else{
return 0
}
}
// RETURN NUMBER OF ROWS IN EACH SECTION OF TABLE VIEWS
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return visibleRowsPerSection[section].count
}
/* Return object of UITableViewCell that contains table SECTON data and USER profile data */
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath: indexPath as NSIndexPath)
let menuCell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! yourCellClass
if currentCellDescriptor["cellIdentifier"] as! String == "parent"{
}
else if currentCellDescriptor["cellIdentifier"] as! String == "child"{
menuCell.backgroundColor = UIColor.clear
}
return menuCell
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let cellDescriptorss = cellDescriptors[indexPath.section] as! NSArray
var data = cellDescriptorss.object(at: indexOfTappedRow) as! [String:Any]
if data["isExpandable"] as! Bool == true{
var shouldExpandAndShowSubRows = false
if data["isExpanded"] as! Bool == true{
shouldExpandAndShowSubRows = false
(cellDescriptorss[indexOfTappedRow] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
}
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (data["additionalRows"] as! Int)) {
(cellDescriptorss[i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
getIndicesOfVisibleRows()
self.profileTableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: UITableViewRowAnimation.fade)
}
Thank You for helping me out, I was stuck at a point where the sections weren't expanding even after your help, so just made some changes in the syntax as Swift 3.0 is very specific about type casting hence the didSelectRowAt wasn't functioning properly. Here is the complete didSelectRowAt method. Happy coding.
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if (cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow] ["isExpandable"] as! Bool == true {
var shouldExpandAndShowSubRows = false
if (cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow]["isExpanded"] as! Bool == false {
// In this case the cell should expand.
shouldExpandAndShowSubRows = true
}
((cellDescriptors[indexPath.section] as! NSMutableArray)[indexOfTappedRow] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + ((cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow]["additionalRows"] as! Int)) {
((cellDescriptors[indexPath.section] as! NSMutableArray)[i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
Swift 3/4 without use of NSMutable arrays based on the tutorial and all the code wrapped in a model.
class CellsDescriptorModel {
private var cellDescriptors: [[[String:Any]]]!
private var visibleRowsPerSection : [[Int]]
var CellDescriptors : [[[String:Any]]] { get { return cellDescriptors }}
var VisibleRowsPerSection : [[Int]] { get { return visibleRowsPerSection }}
init(plist:String) {
visibleRowsPerSection = [[Int]]()
if let url = Bundle.main.url(forResource:plist, withExtension: "plist") {
do {
let data = try Data(contentsOf:url)
cellDescriptors = try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as! [[[String:Any]]]
} catch {
print(error)
}
}
getIndicesOfVisibleRows()
}
func getCellDescriptorForIndexPath(indexPath: IndexPath) -> [String: Any] {
let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row]
return cellDescriptors[indexPath.section][indexOfVisibleRow]
}
func expandCell(indexPath:IndexPath) {
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if cellDescriptors[indexPath.section][indexOfTappedRow] ["isExpandable"] as! Bool == true {
var shouldExpandAndShowSubRows = false
if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] as! Bool == false {
shouldExpandAndShowSubRows = true
}
cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] = shouldExpandAndShowSubRows
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (cellDescriptors[indexPath.section][indexOfTappedRow]["additionalRows"] as! Int)) {
cellDescriptors[indexPath.section][i]["isVisible"] = shouldExpandAndShowSubRows
}
}
else {
if cellDescriptors[indexPath.section][indexOfTappedRow]["cellIdentifier"] as! String == "DataPickerTableViewCell" {
var indexOfParentCell: Int!
for index in (0..<indexOfTappedRow).reversed() {
if cellDescriptors[indexPath.section][index]["isExpandable"] as! Bool == true {
indexOfParentCell = index
break
}
}
cellDescriptors[indexPath.section][indexOfParentCell]["secondaryTitle"] = ""
cellDescriptors[indexPath.section][indexOfParentCell]["isExpanded"] = false
for i in (indexOfParentCell + 1)...(indexOfParentCell + (cellDescriptors[indexPath.section][indexOfParentCell]["additionalRows"] as! Int)) {
cellDescriptors[indexPath.section][i]["isVisible"] = false
}
}
}
getIndicesOfVisibleRows()
}
private func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0..<currentSectionCells.count {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
}
So my table view is not loading anything and I think it's because of this warning that I get. It saids the save function is not being used so how can it load something that is not saved. What I am saving is the indexPath and Section of the row that the user selected via a button action in the row.
Warning:
Result of call to 'save(defaults:)' is unused
Code:
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)
}
}
}
}
NSCoder Class for DataHandling / ordering.save
DataHandling.swift
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
}
}
Other code I'm using:
// 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
}
Saving it with:
saveSorting() { "\($0)" }
Loading it in ViewDidLoad:
func fetchData() {
// Load Data from Server to testArray
retrieveData()
// 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()
}
Loading Code:
// Loading
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
}
The DataHandling instance's save(defaults:) function technically returns a value, even if you don't use it. To silence this warning, assign it to _ to signify that you don't intend to use the result value, e.g.:
_ = ordering.save(defaults: indexPath.defaultsKey)
or
let _ = ordering.save(defaults: indexPath.defaultsKey)
Just to be clear, this is almost definitely not why your tableview is not loading data. It should be pretty insignificant. The indexPath.defaultsKey is being saved (assuming the API works).
var accountId = String()
var dataRows = [NSDictionary]()
var grandChilds = [NSDictionary]()
var dataOfGrandChilds = NSMutableDictionary()
override func viewDidLoad() {
super.viewDidLoad()
print("loaded hie \(accountId)")
let request = SFRestAPI.sharedInstance().requestForQuery("SELECT Name,Id FROM Account where parentid='\(self.accountId)'");
//SFRestAPI.sharedInstance().send(request, delegate: self);
SFRestAPI.sharedInstance().sendRESTRequest(request, failBlock: {error in print(error)}, completeBlock: { responce in print(responce)
self.dataRows = responce["records"] as! [NSDictionary]
var counter = 0;
for i in self.dataRows
{
let requestForGrandChilds = SFRestAPI.sharedInstance().requestForQuery("select Name,Id from Account where parentid='\(i["Id"]!)'")
SFRestAPI.sharedInstance().sendRESTRequest(requestForGrandChilds,
failBlock:
{
error in print(error)
print("error block")
},
completeBlock:
{
responceChild in
self.grandChilds = responceChild["records"] as! [NSDictionary]
self.dataOfGrandChilds["\(counter)"] = self.grandChilds
print(self.dataOfGrandChilds)
counter += 1
print("control still in inner competion block")
dispatch_async(dispatch_get_main_queue(),
{ () -> Void in
print("Control came to main queue")
self.tableView.reloadData()
})
})
}
})
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if(dataOfGrandChilds.count > 0 ){
return dataOfGrandChilds.count
}
return 1
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return dataRows[section]["Name"] as? String
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("toViewChilds", forIndexPath: indexPath)
print(indexPath.section)
print(dataOfGrandChilds["\(indexPath.section)"])
if let tempData = dataOfGrandChilds["\(indexPath.section)"]
{
cell.textLabel?.text = tempData[indexPath.row]["Name"] as? String
}
return cell
}
In the first request I'm trying fetch direct child accounts of an Id. In the completion block of 1st request I'm trying to fetch grand child accounts .
dataRows is having data for section headers ( Which are direct child names).
dataOfGrandChilds is dictionary that is holding section number as key and corresponding grandChilds array as its value.
On reloading my tableView I'm able to display only 1st sections child but not second sections child. Please help finding solution.
The error that i'm getting is
2016-06-07 11:10:30.764 iCRM[67964:11218133] *** Terminating app due
to uncaught exception 'NSRangeException', reason: '-[__NSCFArray
objectAtIndex:]: index (1) beyond bounds (1)'
var accountId = String()
var dataRows = [NSDictionary]()
var grandChilds = [NSDictionary]()
var dataOfGrandChilds = NSMutableDictionary()
override func viewDidLoad() {
super.viewDidLoad()
print("loaded hie \(accountId)")
let request = SFRestAPI.sharedInstance().requestForQuery("SELECT Name,Id FROM Account where parentid='\(self.accountId)'");
//SFRestAPI.sharedInstance().send(request, delegate: self);
SFRestAPI.sharedInstance().sendRESTRequest(request, failBlock: {error in print(error)}, completeBlock: { responce in print(responce)
self.dataRows = responce["records"] as! [NSDictionary]
var counter = 0;
for i in self.dataRows
{
let requestForGrandChilds = SFRestAPI.sharedInstance().requestForQuery("select Name,Id from Account where parentid='\(i["Id"]!)'")
SFRestAPI.sharedInstance().sendRESTRequest(requestForGrandChilds,
failBlock:
{
error in print(error)
print("error block")
},
completeBlock:
{
responceChild in
self.grandChilds = responceChild["records"] as! [NSDictionary]
self.dataOfGrandChilds["\(counter)"] = self.grandChilds
print(self.dataOfGrandChilds)
counter += 1
print("control still in inner competion block")
dispatch_async(dispatch_get_main_queue(),
{ () -> Void in
print("Control came to main queue")
self.tableView.reloadData()
})
})
}
})
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if let countOfRows = dataOfGrandChilds[section]?.count // to avoid un-wrapping nil value
{
return countOfRows
}
return 1
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return dataRows[section]["Name"] as? String
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("toViewChilds", forIndexPath: indexPath)
print(indexPath.section)
print(dataOfGrandChilds["\(indexPath.section)"])
if let tempData = dataOfGrandChilds["\(indexPath.section)"]
{
if(tempData.count != 0 )//to avoid array index out of bound exceptions
{
cell.textLabel?.text = tempData[indexPath.row]["Name"] as? String
}
}
return cell
}
Changes done are :
numberOfRowsInSection():
returned correct number of rows
cellForRowAtIndexPath():
tempData.count != 0 as we need to avoid NSRangeException
I'd like to append the 'userVotes' column in the following parse table into an array using Swift -
Here is my code -
import UIKit
import Parse
class MusicPlaylistTableViewController: UITableViewController {
var usernames = [String]()
var songs = [String]()
var voters = [String]()
var numVotes = 0
override func viewDidLoad() {
super.viewDidLoad()
tableView.separatorColor = UIColor.grayColor()
let query = PFQuery(className:"PlaylistData")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
self.usernames.removeAll()
self.songs.removeAll()
self.voters.removeAll()
for object in objects {
let username = object["username"] as? String
self.usernames.append(username!)
let track = object["song"] as? String
self.songs.append(track!)
let title = object["userVotes"]! as? String
self.voters.append(title!)
print("Array: \(self.voters)")
}
self.tableView.reloadData()
}
} else {
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return usernames.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellTrack", forIndexPath: indexPath) as! TrackTableViewCell
//cell.username.text = usernames[indexPath.row]
cell.username.text = usernames[indexPath.row]
cell.songTitle.text = songs[indexPath.row]
cell.votes.text = "\(numVotes)"
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
}
}
I would like the parse array column to append as follows -
[["user1,"user5,"user9"],["user1,"user2,"user3"],["user4,"user5,"user6"],...]
At this point, I'm getting the following runtime error - fatal error: unexpectedly found nil while unwrapping an Optional value
Since each object that is in your "userVotes" is an array and your you've declared
var voters = [String]()
which is not right because you're saying that there will be one element being appended which is not the case.
So, you should declare voters as...
var voters = Array<Array<String>>()
then as you are downloading it,
for object in objects {
let title = object["userVotes"]! as? [String]
self.voters.append(title!)
print("Array: \(self.voters)")
}