After my completion handler completes still unable to re-load tableView data swift 4 - swift

In my model i've got a function to read in data from firebase.
I call completionHandler(true) when that's done.
This is my viewDidLoad function in my controller that extends UITableViewController.
override func viewDidLoad()
{
super.viewDidLoad()
let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.whiteLarge)
tableView.backgroundView = activityIndicatorView
self.activityIndicatorView = activityIndicatorView
activityIndicatorView.startAnimating()
model.readInFirebaseData { (success) in
print("data read in")
activityIndicatorView.stopAnimating()
dataArray = model.firebaseDataArray
self.tableView.reloadData()
}
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
tableView.tableHeaderView = searchController.searchBar
}
But the table remains empty and for whatever reason self.tableView.reloadData() isn't populating the table as i'd like but if I segue from the TableViewController and come back the list is populated.
I can't see exactly where i'm going wrong.
Thanks.
Update:
I still couldn't get it working so instead of a completionHandler i used a delegate. What i did was:
Singleton:
protocol Refresh{
func refreshData()
}
var delegate:Refresh?
func readInFirebaseData()
{
self.ref.child("users").observe(DataEventType.childAdded, with: { (snapshot) in
user.name = value?["name"] as? String ?? ""
self.dict.updateValue(user, forKey: uid)
DispatchQueue.main.async {
self.delegate?.refreshData()
print("main thread dispatch")
}
})
}
The TableViewController:
class ListController: TableViewController, Refresh{
viewDidLoad()
{
model.delegate = self
}
func refreshData() {
print("called")
array = model.array
self.tableView.reloadData()
}
That all works. The only issue really that I don't know the answer to is the DispatchQueue.main.async is getting called everytime firebase reads in a "user". But I put it at the end of the readInFirebase function and nothing was populated on the list. But in any case it works at the moment.

Probably readInFirebaseData method is asynchronous and the callback runs on a thread different from the main one.
Remember that all the UIKit related calls must be run on the main thread.
Try with:
model.readInFirebaseData { (success) in
print("data read in")
dataArray = model.firebaseDataArray
DispatchQueue.main.async {
activityIndicatorView.stopAnimating()
self.tableView.reloadData()
}
}

Be sure to set delegate and dataSource:
tableView.delegate = self
tableView.dataSource = self
And make sure you call reloadData() from Main Thread:
DispatchQueue.main.async {
self.tableView.reloadData()
}
Calling it from background threads would typically not lead to your table reloaded, since this operation is UI-related. Any UI related operations should be performed from main thread.

Related

Background thread Core Data object property changes doesn't reflect on UI

Let say I want to add a new item in Playlist entity of CoreData and put it in background thread and push back it to main thread then reflect it on tableView. Well, that code is working fine without background thread implementation.
But when I apply below background kinda code, after createPlaylist is executed, tableView becomes to empty space(without any items showed up), though print(self?.playlists.count) gives the correct rows count.
When dealing with GCD, I put some heavy code in background queue and push back to main queue for UI update in same closure. But it seems not worked here, I google a quit of time but still cannot anchor the issue.
import UIKit
import CoreData
class PlayListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var songs = [Song]()
var position = 0
let container = (UIApplication.shared.delegate as! AppDelegate).persistentContainer
private var playlists = [Playlist]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 1, alpha: 1)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "playlistCell")
configureLayout()
getAllPlaylists()
}
// MARK: Core data functions
func getAllPlaylists() {
do {
let context = self.container.viewContext
playlists = try context.fetch(Playlist.fetchRequest())
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
print("count: \(playlists.count)")
// printThreadStats()
} catch {
print("getAllPlaylists failed, \(error)")
}
}
func createPlaylist(name: String) {
container.performBackgroundTask { context in
let newPlaylist = Playlist(context: context)
newPlaylist.name = name
do {
try context.save()
self.playlists = try context.fetch(Playlist.fetchRequest())
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
print(self?.playlists.count)
}
} catch {
print("Create playlist failed, \(error)")
}
}
}
// MARK: tableView data source implementation
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return playlists.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let playlist = playlists[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "playlistCell", for: indexPath)
cell.textLabel?.text = playlist.name
// cell.detailTextLabel?.text = "2 songs"
return cell
}
auto generated fetchRequest and Property defining
import Foundation
import CoreData
extension Playlist {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Playlist> {
return NSFetchRequest<Playlist>(entityName: "Playlist")
}
#NSManaged public var name: String?
}
For the first call of func getAllPlaylists(), you are calling this on main thread from viewDidLoad(). So following lines are executed on main thread.
let context = self.container.viewContext
playlists = try context.fetch(Playlist.fetchRequest())
Next time inside the createPlaylist method, you are performing add playlist task in background context (not on main thread). So following lines are executed on background thread.
self.playlists = try context.fetch(Playlist.fetchRequest())
Also note that, first time we are using viewContext to fetch playlists and second time a backgroundContext. This mix up causes the UI to not show expected result.
I think these two methods could be simplified to -
func getAllPlaylists() {
do {
let context = self.container.viewContext
playlists = try context.fetch(Playlist.fetchRequest())
// DispatchQueue.main.async not necessary, we are already on main thread
self.tableView.reloadData()
print("count: \(playlists.count)")
} catch {
print("getAllPlaylists failed, \(error)")
}
}
func createPlaylist(name: String) {
container.performBackgroundTask { context in
let newPlaylist = Playlist(context: context)
newPlaylist.name = name
do {
try context.save()
DispatchQueue.main.async { [weak self] in
self?.getAllPlaylists()
}
} catch {
print("Create playlist failed, \(error)")
}
}
}
After 5 hours' digging today, I found the solution. I'd like put my solution and code below, because the stuff about "How to pass NSManagedObject instances between queues in CoreData" is quite rare && fragmentation, not friendly to newbies of SWIFT.
The thing is we want to do heavy CoreData task on background thread and reflect the changes in UI on foreground(main thread). Generally, we need to create a private queue context(privateMOC) and perform the heavy CoreData task on this private context, see below code.
For reuse purpose, I put CoreData functions separately.
import UIKit
import CoreData
struct CoreDataManager {
let managedObjectContext: NSManagedObjectContext
private let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
let coreDataStack = CoreDataStack()
static let shared = CoreDataManager()
private init() {
self.managedObjectContext = coreDataStack.persistentContainer.viewContext
privateMOC.parent = self.managedObjectContext
}
func fetchAllPlaylists(completion: #escaping ([Playlist]?) -> Void) {
privateMOC.performAndWait {
do {
let playlists: [Playlist] = try privateMOC.fetch(Playlist.fetchRequest())
print("getAllPlaylists")
printThreadStats()
print("count: \(playlists.count)")
completion(playlists)
} catch {
print("fetchAllPlaylists failed, \(error), \(error.localizedDescription)")
completion(nil)
}
}
}
func createPlaylist(name: String) {
privateMOC.performAndWait {
let newPlaylist = Playlist(context: privateMOC)
newPlaylist.name = name
synchronize()
}
}
func deletePlaylist(playlist: Playlist) {
privateMOC.performAndWait {
privateMOC.delete(playlist)
synchronize()
}
}
func updatePlaylist(playlist: Playlist, newName: String) {
...
}
func removeAllFromEntity(entityName: String) {
...
}
func synchronize() {
do {
// We call save on the private context, which moves all of the changes into the main queue context without blocking the main queue.
try privateMOC.save()
managedObjectContext.performAndWait {
do {
try managedObjectContext.save()
} catch {
print("Could not synchonize data. \(error), \(error.localizedDescription)")
}
}
} catch {
print("Could not synchonize data. \(error), \(error.localizedDescription)")
}
}
func printThreadStats() {
if Thread.isMainThread {
print("on the main thread")
} else {
print("off the main thread")
}
}
}
And Apple has a nice template for it Using a Private Queue to Support Concurrency
Another helpful link: Best practice: Core Data Concurrency
The real tricky thing is how to connect it with your view or viewController, the really implementation. See below ViewController code.
// 1
override func viewDidLoad() {
super.viewDidLoad()
// some layout code
// execute on background thread
DispatchQueue.global().async { [weak self] in
self?.fetchAndReload()
}
}
// 2
private func fetchAndReload() {
CoreDataManager.shared.fetchAllPlaylists(completion: { playlists in
guard let playlists = playlists else { return }
self.playlists = playlists
})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
// 3
#objc func createNewPlaylist(_ sender: Any?) {
let ac = UIAlertController(title: "Create New Playlist", message: "", preferredStyle: .alert)
ac.addTextField { textField in
textField.placeholder = "input your desired name"
}
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
ac.addAction(UIAlertAction(title: "Done", style: .default, handler: { [weak self] _ in
guard let textField = ac.textFields?.first, let newName = textField.text, !newName.isEmpty else { return }
// check duplicate
if let playlists = self?.playlists {
if playlists.contains(where: { playlist in
playlist.name == newName
}) {
self?.duplicateNameAlert()
return
}
}
DispatchQueue.global().async { [weak self] in
CoreDataManager.shared.createPlaylist(name: newName)
self?.fetchAndReload()
}
}))
present(ac, animated: true)
}
Let me break down it:
First in viewDidload, we call fetchAndReload on background thread.
In fetchAndReload function, it brings out all the playlist(returns data with completion handler) and refresh the table on main thread.
We call createPlaylist(name: newName) in background thread and reload the table on main thread again.
Well, this is the 1st time I deal with Multi-threading in CoreData, if there is any mistake, please indicate it. Allright, that's it! Hope it could help someone.

Why tableView.reloadData() is not triggered after Core Data container.performBackgroundTask()

I am using Swift 4 to build a single view iOS 11 application that has a UITableViewController that is also defined as a delegate for a NSFetchedResultsController.
class MyTVC: UITableViewController, NSFetchedResultsControllerDeleagate {
var container:NSPersistentContainer? =
(UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
var frc : NSFetchedResultsController<Student>?
override func viewDidLoad() {
container?.performBackgroundTask { context in
// adds 100 dummy records in background
for i in 1...100 {
let student = Student(context: context)
student.name = "student \(i)"
}
try? context.save() // this works because count is printed below
if let count = try? context.count(for: Student.fetchRequest()) {
print("Number of students in core data: \(count)") // prints 100
}
} // end of background inserting.
// now defining frc:
if let context = container?.viewContext {
let request:NSFetchRequest<Student> = Student.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
frc = NSFetchedResultsController<Student> (
fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil )
try? frc?.performFetch() // this works and I get no errors
tableView.reloadData()
frc.delegate = self
} // end of frc definition
}
}
If I add one row of Student using the viewContext, the frc will fire the required methods to show it in the tableView. However, the 100 dummy rows are not shown. In fact, If I try to tell the tableview to reload after the insertion is done, my app starts to behave weirdly and becomes buggy, and does not do what it should do (i.e: does not delete rows, does not edit, etc).
But If I restart my app, without calling the dummy insertion, I can see the 100 rows inserted from the previous run.
The only problem is that I can't call tableView.reloadData() from the background thread, so I tried to do this:
// after printing the count, I did this:
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData() // causes UI to behave weirdly
}
then I tried to call viewContext.perform to reload the table view in the proper thread
func viewDidLoad() {
// code for inserting 100 dummy rows in background thread
// code for defining frc and setting self as delegate
if let context = container?.viewContext {
context.perform { [weak self] in
self?.tableView.reloadData() // that also causes UI to behave weirdly
}
}
}
How can tell my tableview to reload and display the 100 dummy rows in a thread-safe manner?
override func viewDidLoad() {
super.viewDidLoad()
//Always need your delegate for the UI to be set before calling the UI's delegate functions.
frc.delegate = self
//First we can grab any already stored values.
goFetch()
//This chunk just saves. I would consider putting it into a separate function such as "goSave()" and then call that from an event handler.
container?.performBackgroundTask { context in
//We are in a different queue than the main queue, hence "backgroundTask".
for i in 1...100 {
let student = Student(context: context)
student.name = "student \(i)"
}
try? context.save() // this works because count is printed below
if let count = try? context.count(for: Student.fetchRequest()) {
print("Number of students in core data: \(count)") // prints 100
}
//Now that we are done saving its ok to fetch again.
goFetch()
}
//goFetch(); Your other code was running here would start executing before the backgroundTask is done. bad idea.
//The reason it works if you restart the app because that data you didn't let finish saving is persisted
//So the second time Even though its saving another 100 in another queue there were still at least 100 records to fetch at time of fetch.
}
func goFetch() {
if let context = container?.viewContext {
let request:NSFetchRequest<Student> = Student.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
frc = NSFetchedResultsController<Student> (
fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil )
try? frc?.performFetch()
//Now that records are both stored and fetched its safe for our delegate to access the data on the main thread.
//To me it would make sense to do a tableView reload everytime data is fetched so I placed this inside o `goFetch()`
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
}
After a lot of reading about the NSFetchedResultsController and the NSPersistentContainer and finally finding an important piece of information here at SO I think I have a working example.
My code is slightly different since I used a project I had for this. Anyway here is what I did:
In my view controller I had a property for my container
private var persistentContainer = NSPersistentContainer(name: coreDataModelName)
And in viewDidLoad I loaded the persistent store and created my 100 records.
persistentContainer.loadPersistentStores { persistentStoreDescription, error in
if let error = error {
print("Unable to add Persistent Store [\(error)][\(error.localizedDescription)]")
} else {
self.createFakeNotes() // Here 100 elements get created
DispatchQueue.main.async {
self.setupView() // other stuff, not relevant
self.fetchNotes() // fetch using fetch result controller
self.tableView.reloadData()
}
}
}
Below is createFakeNotes() where I use a separate context for inserting the elements in a background thread, this code is pretty much taken from Apple's Core Data programming guide but to make the UI being updated I needed to set automaticallyMergesChangesFromParent to true which I found out in this SO answer
I also delete old notes first to make the testing easier.
private func createFakeNotes() {
let deleteRequest = NSBatchDeleteRequest(fetchRequest: Note.fetchRequest())
do {
try persistentContainer.persistentStoreCoordinator.execute(deleteRequest, with: persistentContainer.viewContext)
} catch {
print("Delete error [\(error)]")
return
}
let privateContext = persistentContainer.newBackgroundContext()
privateContext.automaticallyMergesChangesFromParent = true //Important!!!
privateContext.perform {
let createDate = Date()
for i in 1...100 {
let note = Note(context: privateContext)
note.title = String(format: "Title %2d", i)
note.contents = "Content"
note.createdAt = createDate
note.updatedAt = createDate
}
do {
try privateContext.save()
do {
try self.persistentContainer.viewContext.save()
} catch {
print("Fail saving main context [\(error.localizedDescription)")
}
} catch {
print("Fail saving private context [\(error.localizedDescription)")
}
}
}
You should fetch your data by calling it from viewwillappear and then try to reload your tableview.
override func viewWillAppear(_ animated: Bool) {
getdata()
tableView.reloadData()
}
func getdata() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do{
persons = try context.fetch(Person.fetchRequest())
}
catch {
print("fetching failed")
}
}

Update tableview instead of entire reload when navigating back to tableview View Controller

I have a home UIViewController that contains a UITableView. On this view controller I display all games for the current user by reading the data from firebase in ViewWillAppear. From this view controller a user can press a button to start a new game and this button takes them to the next view controller to select settings, this then updates the data in firebase and adds a new child. Once they navigate back to the home view controller is there anyway to just update the data with the new child added instead of loading all the games for the table view again as I am currently doing?
This is my current code:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let currentUserID = Auth.auth().currentUser?.uid {
let gamesRef = Database.database().reference().child("games").child(currentUserID)
self.games = []
gamesRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let game = child as! DataSnapshot
self.games.append(game)
self.tableView.reloadData()
}
})
}
}
I think you can use observeSingleEvent and .childAdded
You can do the loading of all the data in viewDidLoad and of single child in viewWillAppear since viewDidLoad will be called once initially
Since both methods will be called initially, so we can have a bool flag so we can control which code runs initially and which does not , since viewWillAppear is called after viewDidLoad so we change the value of this flag in viewWillAppear method and then control the execution of code inside viewWillAppear using this flag
class SomeVC: UIViewController {
var flag = false
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if flag {
//do your work here
}else {
flag = true
}
}
}
Edited:
Another solution can be that you dont do anything in viewDidLoad and do the work only in viewWillAppear since in this particular scenario data in both calls are related (fetching the data from Firebase)
class SomeVC: UIViewController {
var flag = false
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if flag {
//fetch only one child
}else {
//fetch all the data initially
flag = true
}
}
}

How to make sure viewDidLoad() has completed before allowing user to refresh page manually?

I have an app which reloads data (by running two queries, appending the queried info to arrays, and the reloading tableview data). Both queries are set up as function which run on viewDidLoad() but are also linked to a refreshing function (which the user can manually activate with a pull to refresh). The queries work fine, however, an issue occasionally arises while the query functions are being run (but not yet completed) by the viewDidLoad() and the user tries to pull to refresh before it is complete... duplicates are added to the array and I would like to avoid that. The idea I had was to set a checking variable initially to false, change it to true after the viewDidLoad() had completed, and only allow the pull to refresh function to work after the variable had been changed to true. Here is the set up of the function:
func myQueryandAppend(completion: (() -> Void)?){
myCreatorArray.removeAll(keepingCapacity: true)
//repeated for all arrays
let myQuery = PFQuery(className: "Posts")
myQuery.whereKey("User", equalTo: currentUserId)
myQuery.findObjectsInBackground { (objects, error) in
if let objectss = objects{
for object in objectss {
//append the arrays
self.tableView.reloadData()
}
}
})
}
The other function is essentially identical to this one but pulls slightly different info and appends different arrays. (Side note... not entirely sure if the self.tableView.reloadData() is in the correct spot...)
This is the viewDidLoad():
var checkerVar = false
override func viewDidLoad() {
super.viewDidLoad()
print("It loaded")
myQueryandAppend(completion: {
self.tableView.reloadData()
print("myQuery was called in viewDidLoad()")//never called
checkerVar = true
})
refreshControl = UIRefreshControl()
refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl.addTarget(self, action: #selector(MainPageVC.refresh(_:)), for: UIControlEvents.valueChanged)
tableView.addSubview(refreshControl)
self.navigationController?.navigationBar.isHidden = true
checkerVar = true //isn't changed on *completion* of the function
}
Then the refresh handler:
func refresh(_ sender: AnyObject){
if checkerVar == true{
myQueryandAppend(completion: {
self.tableView.reloadData()
self.refreshControl.endRefreshing()
})
self.refreshControl.endRefreshing()
}else{
self.refreshControl.endRefreshing()
}
}
The problem is, with the checkerVar, the instance within the completion in the viewDidLoad() closure is never called (the print statement above it is never logged), and I believe the second instance, at the bottom of viewDidLoad() is done immediately, without actually waiting for the function to complete. Also, no print statements within any of the (completion:{}) are ever logged.
So: 1. Why are the print statements within the completions not being called and (more importantly) 2. how do I get the checkerVar to only be changed to true after viewDidLoad() is complete?
(Side note... not entirely sure if the self.tableView.reloadData() is in the correct spot...)
It's not. You are calling reloadData multiple times, once for every object in objects. That's wasteful. You should write this:
if let objectss = objects{
for object in objectss {
//append the arrays
}
self.tableView.reloadData()
}
Also, I wouldn't count on this code running on the main thread — but you must make sure reloadData is called on the main thread. So now we have this:
if let objectss = objects{
for object in objectss {
//append the arrays
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
Finally, as you've already been told, you are failing to call your own completion handler. But wait. You are also calling reloadData in your completion handler. So now we can delete all of that and just call completion:
if let objectss = objects{
for object in objectss {
//append the arrays
}
DispatchQueue.main.async {
completion()
}
}
Now, however, we run into a slight difficulty, as there are two cases to consider. What if the if let fails? We still need to call the completion handler, so we need to move the call to completion so that we are sure it takes place no matter what:
if let objectss = objects{
for object in objectss {
//append the arrays
}
}
DispatchQueue.main.async {
completion()
}
And with that final change, I think the code will do what you wanted it to do.
Your method "myQueryandAppend"'s completion closure was never called, so the completion closure never fired.
So after your for statement:
for object in objectss {
//append the arrays
self.tableView.reloadData()
}
add:
completion()
to trigger the closure.
I believe once you fix this issue, everything would fall in place easily as your check var would be updated as you expect (maybe you should name it to something like isQuerying though.

How to reload TableView in other View?

I have some CoreData base wich I'm used in my TableView.
When I'm tried to clear those base in other View I have a message in my console log.
CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:.
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (0) must be equal to the number of rows contained in that section before the update (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
for deleting CoreData Array I'm used this code
self.historyArray.removeAll()
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "History")
fetchRequest.returnsObjectsAsFaults = false
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
for managedObject in results
{
let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
managedContext.deleteObject(managedObjectData)
}
} catch {
print("Detele all data")
}
I know I need to reload TableView, but how can I do this in other View?
ill tried this, but this code don't work.
var tableViewHistoryClass = HistoryView()
self.tableViewHistoryClass.tableView.reloadData()
Please help me to fix this message.
You can achieve this by using notification.
create observer in viewDidLoad method where you can display your table view data.
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector:"refreshTableView", name: "reloadTable", object: nil)
}
func refreshTableView () {
self.tableView.reloadData()
}
Second view controller
-> In this view controller you can change your data( if you want to do) or send data object
NSNotificationCenter.defaultCenter().postNotificationName("reloadTable", object: nil)
so like this it will reload your table view.
One solution is to notify your tableview when data is removed.
When data is removed your code post notifications :
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
for managedObject in results
{
let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
managedContext.deleteObject(managedObjectData)
NSNotificationCenter
.defaultCenter()
.postNotificationName("dataDeleted", object: self)
}
}
And in controller where is your tableview add an observer for this notification:
override func viewDidLoad() {
NSNotificationCenter
.defaultCenter()
.addObserver(
self,
selector: #selector(viewController.reloadTableView),
name: "dataDeleted",
object: nil)
}
func reloadTableView() {
self.tableview.reloadData
}
Thanks all for answers!
I'm created new method, all my Clear CoreData function i added to my View in which i have TableView for showing all data from CoreData :P
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector:"clearCoreDataArray", name: "clearAllData", object: nil)
}
func clearCoreDataArray() {
historyArray.removeAll()
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "History")
fetchRequest.returnsObjectsAsFaults = false
do
{
let results = try managedContext.executeFetchRequest(fetchRequest)
for managedObject in results
{
let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
managedContext.deleteObject(managedObjectData)
}
} catch {
print("Detele all data")
}
self.tableView.reloadData()
}
and in View when I'm need to use this method i use this code
NSNotificationCenter.defaultCenter().postNotificationName("clearAllData", object: self)
now i don't have any CoreData warnings