swipe to delete UITabelViewCell - ios

i tried to do swipe to delete on table view cell, but after swiping that cell several times then only it is getting deleted. how to make it work smoothly(in one attempt swipe and delete)? my code is here :
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
var bId = Helper.businessId as String
let param = ["id" : bId] as! [String : AnyObject]
if editingStyle == .delete{
print(Helper.memId)
print("Deleted")
var myDictionary = self.List[indexPath.row] as! NSDictionary
var bId : String!
if myDictionary.allKeys.count>0
{
bId = myDictionary["id"] as! String
}
var bb = bId as String
if editingStyle == .delete{
print(Helper.memId)
print("Deleted")
Alamofire.request("http://api.noattabeta.com/api/v1/user/current/memberships/\(bb)", method: .delete, headers: ["Authorization":Helper.token]).responseString
{response in
print("In Response")
switch response.result {
case .success:
DispatchQueue.main.async {
let myAlert = UIAlertController(title:"Alert", message: "Do You want to delete",preferredStyle:UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title:"Ok",style:UIAlertActionStyle.default,handler:{ (action: UIAlertAction!) in
self.tableView.reloadData()
})
myAlert.addAction(okAction)
let cancelAction = UIAlertAction(title:"Cancel",style:UIAlertActionStyle.default,handler:{ (action: UIAlertAction!) in
self.tableView.reloadData()
})
myAlert.addAction(okAction)
myAlert.addAction(cancelAction)
self.present(myAlert, animated: true, completion: nil)
}
case .failure(let error):
print(error)
}}}

First I wanted to point out that you're presenting the alert in the wrong place. You make a delete call on the API after it success you're asking if he or she want's to confirm. you have to ask it before you call delete
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
var bId = Helper.businessId as String
let param = ["id" : bId] as! [String : AnyObject]
if editingStyle == .delete{
print(Helper.memId)
print("Deleted")
var myDictionary = self.List[indexPath.row] as! NSDictionary
var bId : String!
if myDictionary.allKeys.count>0
{
bId = myDictionary["id"] as! String
}
var bb = bId as String
if editingStyle == .delete{
print(Helper.memId)
print("Deleted")
let myAlert = UIAlertController(title:"Alert", message: "Do You want to delete",preferredStyle:UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title:"Ok",style:UIAlertActionStyle.default,handler:{ (action: UIAlertAction!) in
// here you can call the delete if user accepts
Alamofire.request("http://api.noattabeta.com/api/v1/user/current/memberships/\(bb)", method: .delete, headers: ["Authorization":Helper.token]).responseString
{response in
print("In Response")
switch response.result {
case .success:
DispatchQueue.main.async {
// remove from your local array e.g
self.dataModel.remove(at: indexPath.row)
// Delete the row before you reload the tableView
self.tableView.deleteRows(at: [indexPath], with: .automatic)
// Now reload your tableView
self.tableView.reloadData()
}
case .failure(let error):
print(error)
})
myAlert.addAction(okAction)
let cancelAction = UIAlertAction(title:"Cancel",style:UIAlertActionStyle.default,handler:{ (action: UIAlertAction!) in
self.tableView.reloadData()
})
myAlert.addAction(okAction)
myAlert.addAction(cancelAction)
self.present(myAlert, animated: true, completion: nil)
}

Related

My iOS app crashes trying to display a UIActivityViewController [duplicate]

This question already has answers here:
ActionSheet not working iPad
(8 answers)
Closed 3 years ago.
Basically I have an app that takes in user input (in 5 textfields in an alertcontroller) that are all concatenated in to a string to be in a form that looks like it is a row in a csv file and stored to coredata as an entity. This entity will then be displayed in a tableview. I would like to be able to export the csv file to email it elsewhere. All this will be done in the iOS device itself.
I have referred to the code in (How to Export Core Data to CSV in Swift 3?) to do the code for the exporting of coredata entries. My app is able to be built successfully. Adding and removing items from the tableview all work fine. It is only until I click on my export button that the app crashes.
class ViewController: UITableViewController, NSFetchedResultsControllerDelegate {
let cellId = "cellId"
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<AlarmItem> = {
//create fetch request
let fetchRequest: NSFetchRequest<AlarmItem> = AlarmItem.fetchRequest()
//configure fetch request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "alarmAttributes", ascending: true)]
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
fetchedResultsController.delegate = self
return fetchedResultsController
}()
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch(type) {
case .insert:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break;
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break;
case .update:
if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath) {
configureCell(cell, at: indexPath)
}
break;
case .move:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath], with: .fade)
}
break;
#unknown default:
print("Something odd is happening")
}
}
override func viewDidLoad() {
super.viewDidLoad()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
do {
try fetchedResultsController.performFetch()
} catch let err as NSError {
print("Failed to fetch items", err)
}
}
#objc func addAlarmItem(_ sender: AnyObject) {
let alertController = UIAlertController(title: "Add New Item", message: "Please fill in the blanks", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default) { [unowned self] action in
//combined string of attributes
let myStrings: [String] = alertController.textFields!.compactMap { $0.text }
let myText = myStrings.joined(separator: ", ")
self.save(myText)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
alertController.addTextField { (textField) in
textField.placeholder = "Enter Name of Engineer"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Date of Alarm in DD/MM/YYYY"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Time of Alarm in 24h (eg: 2300)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Please indicate True/False (type True or False)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Insert comments (if any), or NIL"
}
func save(_ itemName: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "AlarmItem", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
item.setValue(itemName, forKey: "alarmAttributes")
do {
try managedContext.save()
tableView.reloadData()
} catch let err as NSError {
print("Failed to save an item", err)
}
}
#objc func exportCSV(_ sender: AnyObject) {
exportDatabase()
}
func exportDatabase() {
let exportString = createExportString()
saveAndExport(exportString: exportString)
}
func saveAndExport(exportString: String) {
let exportFilePath = NSTemporaryDirectory() + "itemlist.csv"
let exportFileUrl = NSURL(fileURLWithPath: exportFilePath)
FileManager.default.createFile(atPath: exportFilePath, contents: NSData() as Data, attributes: nil)
var fileHandle: FileHandle? = nil
do {
fileHandle = try FileHandle(forUpdating: exportFileUrl as URL)
} catch {
print("filehandle has error")
}
if fileHandle != nil {
fileHandle!.seekToEndOfFile()
let csvData = exportString.data(using: String.Encoding.utf8, allowLossyConversion: false)
fileHandle!.write(csvData!)
fileHandle!.closeFile()
let firstActivityItem = NSURL(fileURLWithPath: exportFilePath)
let activityViewController : UIActivityViewController = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)
activityViewController.excludedActivityTypes = [
UIActivity.ActivityType.assignToContact,
UIActivity.ActivityType.saveToCameraRoll,
UIActivity.ActivityType.postToFlickr,
UIActivity.ActivityType.postToVimeo,
UIActivity.ActivityType.postToTencentWeibo
]
self.present(activityViewController, animated: true, completion: nil)
}
}
func createExportString() -> String {
var alarmAttributes: String?
var export: String = NSLocalizedString("Engineer Name,Date of Alarm,Time of Alarm,True or False,Engineer Comments \n", comment: "")
for (index, AlarmItem) in fetchedStatsArray.enumerated() {
if index <= fetchedStatsArray.count - 1 {
alarmAttributes = AlarmItem.value(forKey: "alarmAttributes") as! String?
let alarmAttributeStrings = alarmAttributes
export += "\(alarmAttributeStrings ?? "0") \n"
}
}
print("the app will now print: \(export) ")
return export
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let alarmItem = fetchedResultsController.object(at: indexPath) as NSManagedObject
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
return cell
}
func tableView(_ tableView: UITableView!, canEditRowAtIndexPath indexPath: NSIndexPath!) -> Bool {
return true
}
overr ide func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else { return }
//fetch
let toBeDeleted = fetchedResultsController.object(at: indexPath)
//delete
fetchedResultsController.managedObjectContext.delete(toBeDeleted)
do {
try fetchedResultsController.managedObjectContext.save()
} catch let err as NSError {
print("failed to save item", err)
}
}
func tableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmItem", for: indexPath)
configureCell(cell, at: indexPath)
return cell
}
func configureCell(_ cell: UITableViewCell, at indexPath: IndexPath) {
let alarmItem = fetchedResultsController.object(at: indexPath)
//configure cell
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
}
}
It results in a "Thread 1: signal SIGABRT" error but I cannot find any typos in my code.
Here's the first call throw stack:
The full error code:
2019-06-26 10:04:33.843955+0800 TrueFalseAlarmV3[913:13287] libMobileGestalt MobileGestalt.c:890: MGIsDeviceOneOfType is not supported on this platform.
the app will now print: Engineer Name,Date of Alarm,Time of Alarm,True or False,Engineer Comments
2019-06-26 10:31:56.087370+0800 TrueFalseAlarmV3[913:13287] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /Users/danialaqil/Library/Developer/CoreSimulator/Devices/BF0A3A59-A660-4F1D-B0FE-F0D226479D8D/data/Containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2019-06-26 10:31:56.087928+0800 TrueFalseAlarmV3[913:13287] [MC] Reading from private effective user settings.
2019-06-26 10:31:56.088526+0800 TrueFalseAlarmV3[913:18176] [MC] Filtering mail sheet accounts for bundle ID: imdadsl.TrueFalseAlarmV3, source account management: 1
2019-06-26 10:31:56.100398+0800 TrueFalseAlarmV3[913:18176] [MC] Filtering mail sheet accounts for bundle ID: imdadsl.TrueFalseAlarmV3, source account management: 2
2019-06-26 10:31:56.445312+0800 TrueFalseAlarmV3[913:13287] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Your application has presented a UIActivityViewController (). In its current trait environment, the modalPresentationStyle of a UIActivityViewController with this style is UIModalPresentationPopover. You must provide location information for this popover through the view controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem. If this information is not known when you present the view controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'
*** First throw call stack:
(
the throw call stack is in the image above
On iPad, UIActivityViewController needs to be presented in a popover or the app will crash, see this answer for details.

How to export csv with coredata entries?

I have an app that takes in user input that is stored in core data. To simplify things, I combined 5 strings from the user inputs, separated them with a comma and then this long string is stored in core data as 1 entity. The comma is separating the individual 'strings' is what makes it a CSV.
The tableview does what it's supposed to do, but the problem lies when I try to export the coredata entries as a csv. I realise that I am not making use of the fetchedStatsArray as I should and thus it is empty. I tried appending the array with NSManagedObject but none of that work. Is there a way to fix this, such that my fetchedStatsArray is filled? If not, is there another way to make sure that when the csv is generated, it will contain all the data from coredata?
Here is most of my code:
import UIKit
import CoreData
import MessageUI
class HistoryController: UITableViewController, NSFetchedResultsControllerDelegate {
let cellId = "cellId"
var fetchedStatsArray: [NSManagedObject] = []
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<AlarmItem> = {
//create fetch request
let fetchRequest: NSFetchRequest<AlarmItem> = AlarmItem.fetchRequest()
//configure fetch request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "alarmAttributes", ascending: true)]
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
fetchedResultsController.delegate = self
return fetchedResultsController
}()
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch(type) {
case .insert:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break;
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break;
case .update:
//this is mostlikely where the problem lies
//self.tableView.reloadData()
if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath) {
configureCell(cell, at: indexPath)
}
break;
case .move:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath], with: .fade)
}
break;
#unknown default:
print("Something odd is happening")
}
}
override func viewDidLoad() {
super.viewDidLoad()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
do {
try fetchedResultsController.performFetch()
} catch let err as NSError {
print("Failed to fetch items", err)
}
}
#objc func addAlarmItem(_ sender: AnyObject) {
let alertController = UIAlertController(title: "Add New Item", message: "Please fill in the blanks", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default) { [unowned self] action in
//combined string of attributes
let myStrings: [String] = alertController.textFields!.compactMap { $0.text }
let myText = myStrings.joined(separator: ",")
self.save(myText)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
alertController.addTextField { (textField) in
textField.placeholder = "Enter Name of Engineer"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Date of Alarm in DD/MM/YYYY"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Time of Alarm in 24h (eg: 2300)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Please indicate True/False (type True or False)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Insert comments (if any), or NIL"
}
if let popoverController = alertController.popoverPresentationController {
popoverController.sourceView = self.view;
popoverController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0.0, height: 0.0)
popoverController.permittedArrowDirections = []
popoverController.barButtonItem = self.navigationItem.leftBarButtonItem
}
self.present(alertController, animated: true, completion: nil)
}
func save(_ itemName: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "AlarmItem", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
item.setValue(itemName, forKey: "alarmAttributes")
do {
try managedContext.save()
tableView.reloadData()
} catch let err as NSError {
print("Failed to save an item", err)
}
}
#objc func exportCSV(_ sender: AnyObject) {
exportDatabase()
}
func exportDatabase() {
let exportString = createExportString()
saveAndExport(exportString: exportString)
}
func saveAndExport(exportString: String) {
let exportFilePath = NSTemporaryDirectory() + "itemlist.csv"
let exportFileUrl = NSURL(fileURLWithPath: exportFilePath)
FileManager.default.createFile(atPath: exportFilePath, contents: NSData() as Data, attributes: nil)
var fileHandle: FileHandle? = nil
do {
fileHandle = try FileHandle(forUpdating: exportFileUrl as URL)
} catch {
print("filehandle has error")
}
if fileHandle != nil {
fileHandle!.seekToEndOfFile()
let csvData = exportString.data(using: String.Encoding.utf8, allowLossyConversion: false)
fileHandle!.write(csvData!)
fileHandle!.closeFile()
let firstActivityItem = NSURL(fileURLWithPath: exportFilePath)
let activityViewController : UIActivityViewController = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)
activityViewController.excludedActivityTypes = [
UIActivity.ActivityType.assignToContact,
UIActivity.ActivityType.saveToCameraRoll,
UIActivity.ActivityType.postToFlickr,
UIActivity.ActivityType.postToVimeo,
UIActivity.ActivityType.postToTencentWeibo
]
activityViewController.popoverPresentationController?.sourceView = self.view
activityViewController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection()
activityViewController.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
self.present(activityViewController, animated: true, completion: nil)
}
}
func createExportString() -> String {
var alarmAttributes: String?
var export: String = NSLocalizedString("Engineer Name,Date of Alarm,Time of Alarm,True or False,Engineer Comments \n", comment: "")
for (index, AlarmItem) in fetchedStatsArray.enumerated() {
if index <= fetchedStatsArray.count - 1 {
alarmAttributes = AlarmItem.value(forKey: "alarmAttributes") as! String?
let alarmAttributeStrings = alarmAttributes
export += "\(alarmAttributeStrings ?? "0") \n"
}
}
print("the app will now print: \(export) ")
return export
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let alarmItem = fetchedResultsController.object(at: indexPath) as NSManagedObject
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
return cell
}
func tableView(_ tableView: UITableView!, canEditRowAtIndexPath indexPath: NSIndexPath!) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else { return }
//fetch
let toBeDeleted = fetchedResultsController.object(at: indexPath)
//delete
fetchedResultsController.managedObjectContext.delete(toBeDeleted)
do {
try fetchedResultsController.managedObjectContext.save()
} catch let err as NSError {
print("failed to save item", err)
}
}
func tableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmItem", for: indexPath)
configureCell(cell, at: indexPath)
return cell
}
func configureCell(_ cell: UITableViewCell, at indexPath: IndexPath) {
let alarmItem = fetchedResultsController.object(at: indexPath)
//configure cell
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
}
}

How does one export entries from coredata into a csv file to be emailed?

Basically I have an app that takes in user input (in 5 textfields in an alertcontroller) that are all concatenated in to a string to be in a form that looks like it is a row in a csv file and stored to coredata as a entity. This entity will then be displayed in a tableview. I would like to be able to export the csv file to email it elsewhere. All this will be done in the iOS device itself.
I have looked on this website and other websites but to no avail. A large majority of the example code makes use of outdated versions of swift and xcode.
class ViewController: UITableViewController, NSFetchedResultsControllerDelegate {
let cellId = "cellId"
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<AlarmItem> = {
//create fetch request
let fetchRequest: NSFetchRequest<AlarmItem> = AlarmItem.fetchRequest()
//configure fetch request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "alarmAttributes", ascending: true)]
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
fetchedResultsController.delegate = self
return fetchedResultsController
}()
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch(type) {
case .insert:
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break;
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break;
case .update:
if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath) {
configureCell(cell, at: indexPath)
}
break;
case .move:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath], with: .fade)
}
break;
#unknown default:
print("Something odd is happening")
}
}
override func viewDidLoad() {
super.viewDidLoad()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
do {
try fetchedResultsController.performFetch()
} catch let err as NSError {
print("Failed to fetch items", err)
}
}
#objc func addAlarmItem(_ sender: AnyObject) {
let alertController = UIAlertController(title: "Add New Item", message: "Please fill in the blanks", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default) { [unowned self] action in
//combined string of attributes
let myStrings: [String] = alertController.textFields!.compactMap { $0.text }
let myText = myStrings.joined(separator: ", ")
self.save(myText)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
alertController.addTextField { (textField) in
textField.placeholder = "Enter Name of Engineer"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Date of Alarm in DD/MM/YYYY"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Time of Alarm in 24h (eg: 2300)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Please indicate True/False (type True or False)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Insert comments (if any), or NIL"
}
func save(_ itemName: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "AlarmItem", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
item.setValue(itemName, forKey: "alarmAttributes")
do {
try managedContext.save()
tableView.reloadData()
} catch let err as NSError {
print("Failed to save an item", err)
}
}
#objc func exportCSV(_ sender: AnyObject) {
exportDatabase()
}
func exportDatabase() {
let exportString = createExportString()
saveAndExport(exportString: exportString)
}
func saveAndExport(exportString: String) {
let exportFilePath = NSTemporaryDirectory() + "itemlist.csv"
let exportFileUrl = NSURL(fileURLWithPath: exportFilePath)
FileManager.default.createFile(atPath: exportFilePath, contents: NSData() as Data, attributes: nil)
var fileHandle: FileHandle? = nil
do {
fileHandle = try FileHandle(forUpdating: exportFileUrl as URL)
} catch {
print("filehandle has error")
}
if fileHandle != nil {
fileHandle!.seekToEndOfFile()
let csvData = exportString.data(using: String.Encoding.utf8, allowLossyConversion: false)
fileHandle!.write(csvData!)
fileHandle!.closeFile()
let firstActivityItem = NSURL(fileURLWithPath: exportFilePath)
let activityViewController : UIActivityViewController = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)
activityViewController.excludedActivityTypes = [
UIActivity.ActivityType.assignToContact,
UIActivity.ActivityType.saveToCameraRoll,
UIActivity.ActivityType.postToFlickr,
UIActivity.ActivityType.postToVimeo,
UIActivity.ActivityType.postToTencentWeibo
]
self.present(activityViewController, animated: true, completion: nil)
}
}
func createExportString() -> String {
var alarmAttributes: String?
var export: String = NSLocalizedString("Engineer Name,Date of Alarm,Time of Alarm,True or False,Engineer Comments \n", comment: "")
for (index, AlarmItem) in fetchedStatsArray.enumerated() {
if index <= fetchedStatsArray.count - 1 {
alarmAttributes = AlarmItem.value(forKey: "alarmAttributes") as! String?
let alarmAttributeStrings = alarmAttributes
export += "\(alarmAttributeStrings ?? "0") \n"
}
}
print("the app will now print: \(export) ")
return export
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
let sectionInfo = fetchedResultsController.sections![section]
return sectionInfo.numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let alarmItem = fetchedResultsController.object(at: indexPath) as NSManagedObject
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
return cell
}
func tableView(_ tableView: UITableView!, canEditRowAtIndexPath indexPath: NSIndexPath!) -> Bool {
return true
}
overr ide func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else { return }
//fetch
let toBeDeleted = fetchedResultsController.object(at: indexPath)
//delete
fetchedResultsController.managedObjectContext.delete(toBeDeleted)
do {
try fetchedResultsController.managedObjectContext.save()
} catch let err as NSError {
print("failed to save item", err)
}
}
func tableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmItem", for: indexPath)
configureCell(cell, at: indexPath)
return cell
}
func configureCell(_ cell: UITableViewCell, at indexPath: IndexPath) {
let alarmItem = fetchedResultsController.object(at: indexPath)
//configure cell
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
}
}

Allowing specefic users to delete post Swift 4 Firebase

i have just created so the users can comment on other users post and delete the comments if they want to delete them.
but with this code everyone can delete everyone´s comments
func getKeysValue() {
// let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("posts").child(postsKey).child("comments").observe( .value) { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let commentKey = snap.key
self.keyArray.insert(commentKey, at: 0)
// print(self.keyArray)
// print("Here is the specific Key\(self.commentKey)")
}
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
getKeysValue()
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when, execute: {
Database.database().reference().child("posts").child(postsKey).child("comments").child(self.keyArray[indexPath.row]).removeValue()
self.commentsTableView.reloadData()
})
}
}
then i tryed this code to only let the user to only delete its own comments:
func getKeysValue() {
// let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("posts").child(postsKey).child("comments").observe( .value) { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let commentKey = snap.key
self.keyArray.insert(commentKey, at: 0)
// print(self.keyArray)
// print("Here is the specific Key\(self.commentKey)")
}
}
}
var uid = Auth.auth().currentUser?.uid
var userID = String()
var idUser = String()
var userUID = String()
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
getKeysValue()
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when, execute: {
self.userID = self.keyArray[indexPath.row]
self.idUser = self.userID
print(" Here is the indexPath key: \(self.idUser)")
Database.database().reference().child("posts").child(postsKey).child("comments").child(self.idUser).observeSingleEvent(of: .value, with: { (snapshot) in
if let postsDictionary = snapshot.value as? [String: AnyObject] {
self.uidArray.insert(postsDictionary as NSDictionary, at: 0)
let usersArray = self.uidArray[indexPath.row] as! [String: AnyObject]
let usersKey = usersArray["uid"] as? String
self.userUID = usersKey!
print("Here is the Comment:\(self.uidArray)")
print("And here is the UID::: \(self.userUID)")
if self.uid != self.userUID {
let logInAlert = UIAlertController(title: "Failed to delete", message: "You can only delete your own comment(s)", preferredStyle: .actionSheet)
logInAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(logInAlert, animated: true, completion: nil)
}else {
let commentSuccess = self.storyboard?.instantiateViewController(withIdentifier: "CommentReloadVC")
self.present(commentSuccess!, animated: false, completion: nil)
Database.database().reference().child("posts").child(postsKey).child("comments").child(self.keyArray[indexPath.row]).removeValue()
self.commentsTableView.reloadData()
}
}
})
})
}
}
soo whats wrong with this code? the thing is that this work perfect but only on the first row off the tableview that will say that this authorazation code only works with the first comment of the post and if you try to delete any other comment below the first one then i get an error that the index is out of range on this line
let usersArray = self.uidArray[indexPath.row] as! [String: AnyObject]
and this is how it looks in the database:
this looks like it should work but it does not it's probably something really easy but i just cant figure this out so help would be much appreciated
Thanks for your time. :)

Add more data when tableview reached end

I am getting some data from server using Alamofire like this :
func getNewestFeed(order:String , page:Int) {
Alamofire.request("https://blahblah.com/api/front/\(page)/\(order)", method: .post, parameters: ["foo":"bar"], encoding: JSONEncoding.default, headers: nil).responseJSON(completionHandler: { respone in
if respone.response?.statusCode == 200 {
let result = respone.result.value as! [String:Any]
self.newestArray = result["records"] as! [Any]
self.tableView.reloadData()
} else {
//Show error
let alertController = UIAlertController(title: "Error", message: "", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
OperationQueue.main.addOperation { self.present(alertController, animated: true, completion: nil) }
}
})
}
by default server gives me page 1, now I need to add more data too self.newestArray to load more contents. So here is the code :
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let lastElement = newestArray.count - 1
if indexPath.row == lastElement {
//add more data
}
}
Now I am trying to add result to self.newestArray , like this :
let result = respone.result.value as! [String:Any]
self.newestArray.append(result["records"] as! [Any])
self.tableView.reloadData()
but crashes with this error :
Could not cast value of type 'Swift.Array' (0x109c3c828) to
'Swift.Dictionary' (0x109c3c798).
How should I addd more array to self.newestArray and show it again in table view ?
append(_:) is used to add a single element to the end of the array.
There is append(contentsOf:) if you want to add a collection of items:
self.newestArray.append(contentsOf: result["records"] as! [Any])

Resources