Create a Note App with Swift and Core Data – II

Within the first a part of this text, we had been in a position to design the UI for our Observe App and likewise carried out the Create and Learn operations of our app.

Let’s take it a step additional by implementing the Replace and Delete operations.

Let’s get began!

Create a brand new view controller and identify it NoteDetailViewController; Once more, make certain to not examine “Additionally create XIB file” possibility.

This may show the be aware and likewise permit us edit it.

Now let’s give our view controller’s essential view a color of .systemBackground as follows.

view.backgroundColor = .systemBackground

Now let’s add the UI Views for our be aware. The NoteDetailViewController will look so much just like the AddNoteViewController however with some further parts.
Let’s add the title textual content area as an nameless closure:

    personal var titleField: UITextField = {
        let area = UITextField()
        area.textColor = .label
        area.font = UIFont.systemFont(
                      ofSize: 22, weight: .medium)
        return area
     }() 
Enter fullscreen mode

Exit fullscreen mode

Additionally add the physique textual content view as an nameless closure:

    personal var bodyTextView: UITextView = {
        let view = UITextView()
        view.font = UIFont.systemFont(ofSize: 18)
        view.textColor = .label
        view.clipsToBounds = true
        return view
    }()
Enter fullscreen mode

Exit fullscreen mode

Now let’s add the views as subviews to the controller’s essential view and outline their frames by overriding the viewWillLayoutSubviews.

    override func viewWillLayoutSubviews() {
        tremendous.viewWillLayoutSubviews()
        view.addSubViews(views: titleField, bodyTextView)

        titleField.body = CGRect(
                           x: 12, 
                           y: 120, 
                           width: view.width - 24, 
                           peak: 44)
        bodyTextView.body = CGRect(
                           x: 8, 
                           y: titleField.backside + 8, 
                           width: view.width - 16, 
                           peak: view.backside - 220)
    }
Enter fullscreen mode

Exit fullscreen mode

Observe let’s create a public be aware property in our NoteDetailViewController as follows:

var be aware: Observe?

Bear in mind the NoteDetailViewController shows the element of a specific be aware within the NotesViewController, meaning it expects {that a} be aware object might be handed to it whenever you faucet on a be aware from the listing of notes within the NotesViewController.

Now, in our viewDidLoad, let’s set worth to our titleTextField and bodyTextView as follows:

    if let be aware = be aware {
        titleField.textual content = be aware.title
        bodyTextView.textual content = be aware.physique
    }
Enter fullscreen mode

Exit fullscreen mode

As a result of the be aware property is nullable, we examine if it is not null to make use of it.

Nice job!

Now let’s return to our NotesViewController class to deal with choosing a be aware to view it is element.

Now we’ll implement the collectionView delegate methodology didSelectItemAt indexPath as follows:

    func collectionView(_ collectionView: UICollectionView, 
           didSelectItemAt indexPath: IndexPath) {

        guard let be aware = self.dataSource.itemIdentifier(for: 
               indexPath) else {
            collectionView.deselectItem(
               at: indexPath, animated: true)
            return
        }

        let noteVC = NoteDetailViewController()
        noteVC.be aware = be aware

        navigationController?.pushViewController(
               noteVC, animated: true)
    } 
Enter fullscreen mode

Exit fullscreen mode

As a result of we’ve carried out our assortment view with a diffable datasource, to get the component at indexPath, we name the itemIdentifier of the dataSource and go it the indexPath.

Now after we construct and run, we should always see one thing like the next:

Nice Job!



UPDATE MANAGED OBJECT

Not that we’ve been in a position to create and skim from Core information, let’s proceed to replace.

Do not forget that our NoteDetailViewController, just like the AddNoteViewController additionally makes use of UITextField for Title and UITextView for the physique of our notes.

Thus, let’s set the NoteDetailViewController because the delegate for the titleTextField and the bodyTextView. This may permit us to have the ability to look ahead to adjustments.
Add the next snippet to viewDidLoad within the NoteDetailViewController:

    bodyTextView.delegate = self
    titleField.delegate = self
Enter fullscreen mode

Exit fullscreen mode

This may instantly inform us to adapt to the UITextViewDelegate and the UITextFieldDelegate protocols

Add the next on the backside exterior of the category definition:

extension NoteDetailViewController: UITextViewDelegate, 
     UITextFieldDelegate {
}
Enter fullscreen mode

Exit fullscreen mode

Bear in mind we created some comfort properties and methodology as extensions to the UIView, let do the identical for String.
Add the next snippet to your Extensions.swift file:

extension String {
   func trim() -> String {
      self.trimmingCharacters(in: .whitespacesAndNewlines)
   }
}
Enter fullscreen mode

Exit fullscreen mode

This can be a shorthand model of the usual trimmingCharaters(:) String methodology.

Due to Swift, we will pass over the return key phrase for a single line assertion.

In a be aware app, we would like the app to set off and replace when the title area detects that the title has modified and the physique textual content view to replace the physique content material when the that adjustments as effectively.

To assist us obtain this, we’re going to be implementing the textFieldDidEndEditing delegate methodology of the UITextFieldDelegate protocol and the textViewDidEndEditing methodology of the UITextViewDelegate.

Add the next contained in the NoteDetailViewController extension we simply added on the backside:

extension NoteDetailViewController: UITextViewDelegate, 
    UITextFieldDelegate {

    func textFieldDidEndEditing(_ textField: UITextField) {

    }

    func textViewDidEndEditing(_ textView: UITextView) {

    }       
}
Enter fullscreen mode

Exit fullscreen mode

Observe let’s add a property identify appDelegate to the NoteDetailViewController slightly below the be aware declaration as follows:

let appDelegate = UIApplication.shared.delegate as! AppDelegate

Now, inside our textFieldDidEndEditing delegate methodology, we would like core information to replace the title when the title textual content area detect a change and the consumer has ended modifying.
Add the next snippet:

        resignFirstResponder()
        guard let be aware = self.be aware else { return }
        if textField == titleField && 
               titleField.textual content!.trim() != be aware.title {

            let managedContext = 
                 appDelegate.persistentContainer.viewContext
            be aware.title = titleField.textual content!.trim()

            do {
                attempt managedContext.save()
            } catch let error as NSError {
                fatalError("(error.userInfo)")
            }
        }
Enter fullscreen mode

Exit fullscreen mode

Additionally contained in the textViewDidEndEditing delegate methodology, to replace the physique, add the next:

        resignFirstResponder()
        guard let be aware = self.be aware else { return }
        if textView == bodyTextView && 
                 bodyTextView.textual content.trim() != be aware.physique {

            let managedContext = 
                 appDelegate.persistentContainer.viewContext
            be aware.physique = bodyTextView.textual content

            do {
                attempt managedContext.save()
            } catch let error as NSError {
                fatalError("(error.userInfo)")
            }
        }
Enter fullscreen mode

Exit fullscreen mode

Discover that we’ve got to first examine if the adjustments is just not the identical with the content material of the be aware title or the physique earlier than persisting adjustments.

Observe let’s construct and run.

We’ve now carried out Replace.



DELETING MANAGED OBJECT

Now let’s implement the Delete operation. To take action, let’s return to our NotesViewController.

Do not forget that we used UICollectionLayoutListConfiguration (i.e Listing Structure) which provides us an analogous view as a UITableView.
So as to have the ability to swipe to delete as within the case of UITableView, we have to add Swipe motion to our Listing format configuration. Now go contained in the personal methodology named createLayout and add the next snippet simply earlier than the return assertion:

config.trailingSwipeActionsConfigurationProvider = { 
        indexPath in

    let deleteAction = UIContextualAction(
           model: .damaging, title: "Delete") {              
         [weak self] motion, view, completion in

       self?.deleteItem(at: indexPath)
       completion(true)

    }
    return UISwipeActionsConfiguration(
                          actions: [deleteAction])
}

Enter fullscreen mode

Exit fullscreen mode

You might additionally create one for the leadingSwipeActionsConfigurationProvider if there’s want for that.

Now we have to create a way known as deleteItem(at:). So add the next snippet:

personal func deleteItem(at indexPath: IndexPath) {
   guard let appDelegate = UIApplication.shared.delegate 
                  as? AppDelegate else { return }

   let managedContext = 
             appDelegate.persistentContainer.viewContext

   let be aware = self.dataSource.itemIdentifier(for: indexPath)        
   guard let be aware = be aware else { return }

   managedContext.delete(be aware)

   do {
       attempt managedContext.save()
       var snapshot = dataSource.snapshot()

       snapshot.deleteAllItems()
       snapshot.appendSections([.main])
       dataSource.apply(snapshot)

       fetchNotes()
       updateCollectionView()

   } catch let error as NSError {
       fatalError("(error.userInfo)")
   }    
}
Enter fullscreen mode

Exit fullscreen mode

Within the methodology above, first we get a managedContext from the persistent container within the AppDelegate. Then we discover the be aware on the swiped indexPath.
Then we handed the be aware to the delete methodology of the managed context. Lastly and importantly, we known as the save() methodology on the managedContext to commit the precise deletion.

Now to replace our UI (i.e take away the deleted from snapshot), we needed to clear the snapshot, reassigned the primary part earlier than fetching and updating the CollectionView.

Now let’s construct and run once more. If all the things is ok, we should always have the ability to swipe a cell and see it deleted when the delete motion button is tapped.

We have come to finish of the challenge. The whole supply code might be discovered here.

Add a Comment

Your email address will not be published. Required fields are marked *