Consolidation Day III – Milestone: Projects 4-6

by | March 5, 2019

Consolidation Day III – Milestone: Projects 4-6

Jeff Summers

March 5, 2019

As I have said before, Consolidation Days have become a lot more meaningful since we’ve moved past learning foundational concepts and are instead a chance for us to stretch our newly formed wings to see if we have retained what we are learning. I have to admit, the 100 Days of Swift challenge is stretching my mind more than I had anticipated and I now find myself playing a lot more “what if” games with the challenges. That’s probably what Paul Hudson had in mind when he developed these challenges, so it is kind of funny that it took me this long to catch-on and appreciate the approach.

This Consolidation day was designed to cover concepts and techniques we learned in projects 4 through 6. As I would have expected, each of these projects have gotten slightly more complex as we gain additional exposure to Swift and start to feel more comfortable with the nuances of the language.

I read through the description of the challenge for this Consolidation day: “create an app that lets people create a shopping list by adding items to a table view.” Well, that seems simple enough. Table Views are Swift’s equivalent to sliced bread. Everyone knows what sliced bread is and almost everyone consumes it. Looking over the various apps loaded on my phone, with the exception of games and an occasional special-interest app, all of them contain a Table View. So if I am to get to a point of calling myself an iOS developer I better have Table Views committed to memory.

The remainder of the instructions state that the application should utilize an AlertController when a user taps an “Add” button to pull up a dialog box to allow the user to enter text then add it to an array of shopping list items. The app should also include a way for the user to clear the shopping list and start over. Finally, we need to create a share button to allow the user to share the shopping list with others.

I immediately went to work. In Xcode I created a new project using a Single View App, saved it to my computer and went to work. As I described in my Project 5 – Word Anagrams post, I went a little different route than what Paul describes. I deleted the ViewController Swift file and also removed it from the Interface Builder storyboard. Within Interface Builder I selected a Navigation Controller to place on the blank canvas. Apple knows that if we are adding a Navigation Controller to a storyboard that is empty, we likely are going to want a TableView Controller. They like us know that TableView Controllers are the predominant controller of choice.

Once I added the Navigation Controller and Table View Controller to the storyboard (note doing it this way also automatically connects the two), I created a new Cocoa Touch file named it TableViewController with the class of UITableViewController. Back in storyboard I made sure to point the TableViewController to the new Swift file I just made.

The rest of the application went together fairly well. To create new shopping list items required creating a button in the navigation bar that would call the UIAlertController with a text field. This is similar to what we did in the Word Anagram project so that went pretty well. In the instructions for this project it said we were not interested in data validation, we were to assume that the item entered in the dialog box was accurate. I made an executive decision to deviate right there. I thought the app should check to see if we already entered the item and not enter it again. Nothing worse than creating a shopping list and you wrote down eggs 3 times then your wife yells at you because you now have 3 dozen eggs and it’s not even Easter.

The check for duplicate items was fairly simple, take the item entered (I made it lower case so that I didn’t worry about whether someone wrote “eggs” and “Eggs”) and compare it against the array of shoppingList items. If found notify the user with a dialog that says they already have that item on the list. The code for this looked like:

func submit(_ item: String) {
let lowerItem = item.lowercased()

if onList(word: lowerItem) {
shoppingList.insert(item, at: 0)
let indexPath = IndexPath(row: 0, section:0)
tableView.insertRows(at: [indexPath], with: .automatic)
} else {
showAlert(title: "Already on List", message: "You have already added \(lowerItem).")
}

The code for sharing the shoppingList was likewise pretty straight forward as we had done something similarly allowing pictures or websites to be shared. That code looks like:

// Share your shopping list
@objc func shareList() {
let list = [shoppingList.joined(separator: "\n")]
let vc = UIActivityViewController(activityItems: list, applicationActivities: [])
vc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem
present(vc, animated: true)
}

A couple of things of note on this code; first you need the “@objc” in front of the function definition because you are dealing with things that require an Objective-C call. If you didn’t put this in, Xcode lets you know about it so that’s at least good until you start to feel comfortable knowing when that is required and when it is not. The second part is around calling the UIActivityController to present the user with sharing options. The line with popover is necessary if your code is to run on an iPad since that sharing needs an anchor point. I anchored to the right navigation bar button.

That pretty much completed the assignment. But as I was sitting there looking at it I thought it needed a little more and I needed a stretch goal. What happens if I made a mistake on one of the items. The way the app is now I have two choices. I can either ignore the error (and I don’t ignore errors) or I can somehow allow the user to delete the mistake. As you might could imagine, I chose the latter.

I thought I had set myself up for success above when I chose to delete the ViewController file and replace it with a TableViewController file. That default file had the necessary functions already pre-defined for numberOfRowsInSection and cellForRowAt meaning I just had to tweak those. That file also had commented out functions for canEditRowAt and submitting edit changes. Awesome, all I needed to do is uncomment those and let iOS do the heavy lifting for me. Yeah, not so fast Sparky.

It looks like perhaps it wasn’t quite so simple as uncommenting a few lines of code. In the first place, when I uncommented those lines that Apple put in as a default, Xcode threw errors that functional parameters have changed. It would appear that Apple may not have updated that code to more current versions of Swift. So off I went to do some research and see what the proper code needs to be. I ended up with the following code:

self.navigationItem.leftBarButtonItem = self.editButtonItem

This part I added to the viewDidLoad() function to create the edit button on the navigation bar. The code below is to create the edit functionality.

// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
// Delete the row from the data source
shoppingList.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}

}

As part of UIKit Apple provides the necessary hooks to allow for editing. This is pretty cool code. Not my part, that is fairly simplistic. No, I am talking about what Apple brings to the table with this.

Now when a user enters an item into the Table View, an Edit button is on the navigation bar. If a user clicks that then all of the items in the Table View have a red circle with a minus in them. Clicking that gives a Delete red box on the right-hand side. Clicking that and the code deletes the item from the Table View. The Edit button changes to Done allowing the user to tap that to end editing. Now the app allows you to delete an item if you made a mistake without having to start over with your list. Pretty slick.

If I would have ended there, I would have been pretty happy. But no, I couldn’t leave well enough alone. Having that Edit button in the navigation bar is cool and all but I don’t want it active or showing if there isn’t something to edit. It doesn’t seem right. I created a function that would decide when to allow that button to be active. That code is:

// Is array empty, if so don't show edit button
func showEdit() {
if shoppingList.isEmpty {
self.editButtonItem.isEnabled = false
} else {
self.editButtonItem.isEnabled = true
}

}

That code is straight forward. If our shopping list is empty, then disable the button. If the shopping list contains anything then let the edit button be active and clickable. That seemed like all I needed to do. But it’s not working quite like I thought it would.

Oh, it is making the button active or inactive all right but there is a little bit of functionality missing. If you remember from my description above, the edit function will give all the items in the Table View a red circle with a dash in it allowing for each to be deleted. You do not leave the editing mode until you click Done at the top.

The issue with my code is that if you delete the only row in the array the array is empty, so the Edit/Done button is disabled. You can add a new item with the “+” button but the screen will return to the Editing state it was left in because the user had not hit Done yet. So, I kind of sort of have it working, I just need to figure out how to set the state of that button to done editing before I disable it with the empty array test.

If you have ideas, shoot me a line, I would love to hear them. I am sure this is something simple and if I would have been patient until an upcoming project or lecture this will probably be covered. Despite this not being completely the way I want it, I am still pretty proud of myself for attempting this and adding the stretch goals. Maybe I’m farther along the Learning Curve than I am giving myself credit. Then again, maybe not.

Share this Article

Posted by Jeff Summers

Author, technologist, and baseball aficionado specializing in information technology and developing new and creative ways to interact with the world around us. My goal is to extend the boundaries of what is possible and find ways to make the world a better place while having fun.

0 Comments

Trackbacks/Pingbacks

  1. Project 7 – Petition the US Government – Jeff Blogs - […] marked the beginning of Project 7 in the 100 Days of Swift challenge. After a fairly successful Consolidation Day…

Submit a Comment

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

Stay Connected

Search

More Articles

Pace Layered Architecture Categorization

Change is inevitable. Today’s computing environments are constantly in flux from internal and external forces that put demands on the systems and necessitate changes to maintain its value stream. Technology currency is a constant struggle that organizations must...

TBM, Another Holy Grail?

Technology Business Management or TBM as a term was coined around 2012 although many of the concepts making up this framework have been around for almost as long as there has been IT. In its simplest form, TBM represents the integration of business, technology, and...

Visionary or Dreamer?

Thinking back over your life you’ve met and worked with countless personality types. You have been a part of teams that struggled to complete simple tasks and the work felt meaningless. Perhaps your goal in these times was to just trudge through the mud hoping the...

Archives