神刀安全网

iOS From Scratch With Swift: Building a Shopping List Application 1

In the next two lessons, we’ll put what we learned in this series into practice by creating a shopping list application. Along the way, you’ll also learn a number of new concepts and patterns, such as creating a custom model class and implementing a custom delegate pattern. We’ve got a lot of ground to cover so let’s get started.

Outline

The shopping list application we’re about to create has two features, managing a list of items and creating a shopping list by selecting items from the list.

We’ll build the application with a tab bar controller to make switching between the two views fast and straightforward. In this lesson, we focus on the first feature. In the next lesson, we the finishing touches on this feature and zoom in on the shopping list, the application’s second feature.

Even though the shopping list application isn’t complicated from a user’s perspective, there are several decisions that need to be made during its development. What type of store do we use to store the list of items? Can the user add, edit, and delete items? These are questions that we address in the next two lessons.

In this lesson, I also show you how to seed the shopping list application with dummy data to give new users something to start with. Seeding an application with data is often a good idea to help new users get up to speed quickly.

1. Creating the Project

Launch Xcode and create a new project based on the Single View Application template in the iOS > Application section.

iOS From Scratch With Swift: Building a Shopping List Application 1

Name the project Shopping List and enter an organization name and identifier. Set Language  to Swift and  Devices to  iPhone . Make sure the checkboxes at the bottom are unchecked. Tell Xcode where to save the project and click  Create .

iOS From Scratch With Swift: Building a Shopping List Application 1

2. Creating the List View Controller

As you might have expected, the list view controller is going to be a subclass of UITableViewController . Create a new class by selecting  New > File… from the  File  menu. Select  Cocoa Touch Class from the  iOS > Source section.

iOS From Scratch With Swift: Building a Shopping List Application 1

Name the class ListViewController and make it a subclass of  UITableViewController . Leave the checkbox Also create XIB file unchecked and make sure Languages  is set to  Swift . Tell Xcode where you want to save the class and click  Create .

iOS From Scratch With Swift: Building a Shopping List Application 1

Open Main.storyboard , select the view controller that’s already present, and delete it. Drag a  UITabBarController instance from the  Object Library and delete the two view controllers that are linked to the tab bar controller. Drag a UITableViewController from the  Object Library , set its class to  ListViewController in the  Identity Inspector , and create a relationship segue from the tab bar controller to the list view controller.

Select the tab bar controller, open the Attributes Inspector , and make it the storyboard’s initial view controller by checking the checkbox  Is Initial View Controller .

iOS From Scratch With Swift: Building a Shopping List Application 1

The list view controller needs to be the root view controller of a navigation controller. Select the list view controller and choose Embed In > Navigation Controller from the  Editor menu.

iOS From Scratch With Swift: Building a Shopping List Application 1

Select the table view of the list view controller and set Prototype Cells in the  Attributes Inspector to 0 .

iOS From Scratch With Swift: Building a Shopping List Application 1

Run the application in the simulator to see if everything is set up correctly. You should see an empty table view with a navigation bar at the top and a tab bar at the bottom.

3. Creating the Item Model Class

How are we going to work with items in the shopping list application? In other words, what type of object do we use to store the properties of an item, such as its name, price, and a string that uniquely identifies each item?

The most obvious choice is to store the item’s properties in a dictionary. Even though this would work just fine, it would severely limit and slow us down as the application gains in complexity.

For the shopping list application, we’re going to create a custom model class. It requires a bit more work to set up, but it’ll make development much easier down the road.

Create a new class, Item , and make it a subclass of  NSObject . Tell Xcode where to save the class and click  Create .

Properties

Open Item.swift and declare four properties:

  • uuid  of type  String  to uniquely identify each item
  • name  of type  String
  • price  of type  Float
  • inShoppingList  of type  Bool  to indicate if the item is present in the shopping list

It’s essential that the Item class conforms to the  NSCoding protocol. The reason for this will become clear in a few moments. Take a look at what we’ve got so far. Comments are omitted.

import UIKit  class Item: NSObject {      var uuid: String = NSUUID().UUIDString     var name: String = ""     var price: Float = 0.0     var inShoppingList = false  }

Each property needs to have an initial value. We set name to an empty string, price to 0.0 , and inShoppingList to false . To set the initial value of uuid , we make use a class we haven’t seen before, NSUUID . This class helps us create a unique string or UUID. We initialize an instance of the class and ask it for the UUID as a string by invoking UUIDString() .

Give it a try by adding the following code snippet to the viewDidLoad() method of the ListViewController class.

let item = Item() print(item.uuid)

Run the application and take a look at the output in Xcode’s console to see what the resulting UUID looks like. You should see something like this:

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

Archiving

One strategy to save custom objects to disk, such as instances of the Item class, is through a process known as archiving. We’ll use  NSKeyedArchiver and  NSKeyedUnarchiver to archive and unarchive instances of the  Item class.

The class prefix, NS , indicates that both classes are defined in the Foundation framework. The  NSKeyedArchiver class takes a set of objects and stores them to disk as binary data. An added benefit of this approach is that binary files are generally smaller than plain text files containing the same information.

If we want to use NSKeyedArchiver and  NSKeyedUnarchiver to archive and unarchive instances of the  Item class, Item needs to adopt the  NSCoding protocol. Let’s start by updating the Item class to tell the compiler Item adopts the NSCoding protocol.

import UIKit  class Item: NSObject, NSCoding {      ...  }

Remember from the lesson about the Foundation framework, the NSCoding protocol declares two methods that a class must implement to allow instances of the class to be encoded and decoded. Let’s see how this works.

Encoding

If you create custom classes, then you are responsible for specifying how instances of that class should be encoded, converted to binary data. In encodeWithCoder(_:) , the class conforming to the  NSCoding protocol specifies how instances of the class should be encoded. Take a look at the implementation below. The keys that we use are not that important, but you generally want to use the property names for clarity.

func encodeWithCoder(coder: NSCoder) {     coder.encodeObject(uuid, forKey: "uuid")     coder.encodeObject(name, forKey: "name")     coder.encodeFloat(price, forKey: "price")     coder.encodeBool(inShoppingList, forKey: "inShoppingList") }

Decoding

Whenever an encoded object needs to be converted back to an instance of the respective class, init(coder:) is invoked. The same keys that we used in  encodeWithCoder(_:) are used in  init(coder:) . This is very important.

required init?(coder decoder: NSCoder) {     super.init()          if let archivedUuid = decoder.decodeObjectForKey("uuid") as? String {         uuid = archivedUuid     }          if let archivedName = decoder.decodeObjectForKey("name") as? String {         name = archivedName     }          price = decoder.decodeFloatForKey("price")     inShoppingList = decoder.decodeBoolForKey("inShoppingList") }

Note that we use the required keyword. Remember that we covered the required keyword earlier in this series. Because  decodeObjectForKey(_:) returns an object of type AnyObject? , we cast it to a String object.

You should never directly call init(coder:) and  encodeWithCoder(_:) . They are only called by the operating system. By conforming the  Item class to the  NSCoding protocol, we only tell the operating system how to encode and decode instances of the class.

Creating Instances

To make the creation of new instances of the Item class easier, we create a custom initializer that accepts a name and a price. This is optional, but it will make development easier as you’ll see later in this lesson.

Open Item.swift and add the following initializer. Remember that initializers don’t have the func keyword in front of their name. We first invoke the initializer of the superclass, NSObject . We then set the name and price properties of the Item instance.

init(name: String, price: Float) {     super.init()          self.name = name     self.price = price }

You may be wondering why we use self.name in init(name:price:) and name in init(coder:) to set the name property. In both contexts, self references the Item instance we’re interacting with. In Swift, you can access a property without using the self keyword. However, in init(name:price:) one of the parameters has a name that is identical to one of the properties of the Item class. To avoid confusion, we use the self keyword. In short, you can omit the self keyword to access a property unless there’s cause for confusion.

4. Loading and Saving Items

Data persistence is going to be key in our shopping list application so let’s take a look at how we’re going to implement data persistence. Open ListViewController.swift and declare a variable stored property, items , of type [Item] . Note that the initial value of items is an empty array.

import UIKit  class ListViewController: UITableViewController {      var items = [Item]()      ...  }

The items displayed in the view controller’s table view will be stored in items . It is important that  items is a mutable array hence the var keyword. Why? We will add the ability to add new items a bit later in this lesson.

In the initializer of the class, we load the list of items from disk and store it in the items property that we declared a few moments ago.

// MARK: - // MARK: Initialization required init?(coder decoder: NSCoder) {     super.init(coder: decoder)          // Load Items     loadItems() }

The view controller’s loadItems() method is nothing more than a helper method to keep the init?(coder:) method concise and readable. Let’s take a look at the implementation of  loadItems() .

Loading Items

The loadItems() method starts with fetching the path of the file in which the list of items is stored. We do this by calling  pathForItems() , another helper method that we’ll look at in a few moments. Because pathForItems() returns an optional of type String? , we bind the result to a constant, filePath . We discussed optional binding earlier in this series.

// MARK: - // MARK: Helper Methods private func loadItems() {     if let filePath = pathForItems() where NSFileManager.defaultManager().fileExistsAtPath(filePath) {         if let archivedItems = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as? [Item] {             items = archivedItems         }     } }

What we didn’t cover yet is the where keyword in an if statement. By using the where keyword, we add an additional constraint to the condition of the if statement. In loadItems() , we make sure pathForItems() returns a String . Using a where clause, we also verify that the value of filePath corresponds to a file on disk. We use the NSFileManager class for this.

NSFileManager is a class we haven’t worked with yet. It provides an easy-to-use API for working with the file system. We obtain a reference to an instance of the class by asking it for the default manager.

We then invoke fileExistsAtPath(_:) on the default manager, passing in the file path we obtained in the first line of loadItems() . If a file exists at the location specified by the file path, we load the contents of the file into the  items property. If no file exists at that location, the items property retains its initial value, an empty array.

Loading the contents of the file is done through the NSKeyedUnarchiver class. It can read the binary data contained in the file and convert it to an object graph, an array of  Item instances. This process will become clearer when we look at the  saveItems() method in a minute.

Let’s now take a look at pathForItems() , the helper method we invoked earlier. We first fetch the path of the  Documents directory in the application’s sandbox. This step should be familiar by now.

let item = Item() print(item.uuid)

0

The method returns the path to the file containing the application’s list of items. We do this by fetching the path for the Documents directory of the application’s sandbox and appending "items" to it.

The beauty of using URLByAppendingPathComponent(_:) is that the insertion of path separators is done for us wherever necessary. In other words, the system makes sure that we receive a valid file URL. Note that we invoke path() on the resulting NSURL instance to make sure we return a String object.

Saving Items

Even though we won’t be saving items until later in this lesson, it’s a good idea to implement it while we’re at it. The implementation of saveItems() is very concise thanks to the  pathForItems() helper method.

We first fetch the path to the file that contains the application’s list of items and then write the contents of the items property to that location. Easy. Right?

let item = Item() print(item.uuid)

1

The process of writing an object graph to disk is known as archiving. We use the NSKeyedArchiver class to accomplish this by calling  archiveRootObject(_:toFile:) on NSKeyedArchiver .

During this process, every object in the object graph is sent a message of encodeWithCoder(_:) to convert it to binary data. Remember that there’s rarely a need to directly call  encodeWithCoder(_:) .

To verify that loading the list of items from disk works, add a print statement to the viewDidLoad() method of the  ListViewController class. Run the application in the simulator and check if everything’s working.

let item = Item() print(item.uuid)

2

If you take a look at the output in Xcode’s console, you’ll notice that the items property is equal to en empty array. That’s what we expect at this point. What’s important is that  items isn’t equal to  nil . In the next step, we’ll give the user a few items to work with, a process known as  seeding .

5. Seeding the Data Store

Seeding an application with data can often mean the difference between an engaged user and a user quitting the application after using it for less than a minute. Seeding an application with dummy data not only helps users get up to speed, it also shows new users how the application looks and feels with data in it.

Seeding the shopping list application with an initial list of items isn’t difficult. Because we don’t want to create duplicate items, we check during application launch if the data store has already been seeded with data. If the data store hasn’t been seeded yet, we load a list with seed data and use that list to create the data store of the application.

The logic for seeding the data store can be called from a number of locations in an application, but it’s important to think ahead. We could put the logic for seeding the data store in the ListViewController class, but what if, in a future version of the application, other view controllers also have access to the list of items. A better place to seed the data store is in the  AppDelegate class. Let’s see how this works.

Open AppDelegate.swift and amend the implementation of  application(_:didFinishLaunchingWithOptions:) to look like the one shown below.

let item = Item() print(item.uuid)

3

The only difference with the previous implementation is that we first invoke seedItems() . It’s important that seeding the data store takes place before the initialization of any of the view controllers, because the data store needs to be seeded before any of the view controllers loads the list of items.

The implementation of seedItems() isn’t complicated. We start by storing a reference to the shared user defaults object and we then check if the user defaults database has an entry for a key with name  "UserDefaultsSeedItems" and whether this entry is a boolean with a value of true .

let item = Item() print(item.uuid)

4

The key can be whatever you like as long as you are consistent in naming the keys that you use. The key in the user defaults database tells us whether or not the application has already been seeded with data. This is important since we only want to seed the application once.

If the application hasn’t been seeded yet, we load a property list from the application bundle, seed.plist . This file contains an array of dictionaries with each dictionary representing an item with a name and a price.

Before iterating through the seedItems array, we create a mutable array to store the  Item instances that we’re about to create. For each dictionary in the  seedItems array, we create an  Item instance by invoking the initializer we declared earlier in this lesson. Each item is added to the items array.

Finally, we create the path to the file in which we will store the list of items and we write the contents of the items array to disk like we saw in the  saveItems() method of  ListViewController .

The method archiveRootObject(_:toFile:) returns  true if the operation ended successfully and it’s only then that we update the user defaults database by setting the boolean value for the key  "UserDefaultsSeedItems" to  true . The next time the application launches, the data store won’t be seeded again.

You’ve probably noticed that we used another helper method in seedItems()pathForItems() . Its implementation is identical to that of the  ListViewController class.

let item = Item() print(item.uuid)

5

Before you run the application, make sure to copy the property list, seed.plist , to your project. It doesn’t matter where you store it as long as it’s included in the application’s bundle.

Run the application and inspect the output in the console to see if the data store was successfully seeded with the contents of seed.plist . Note that seeding a data store with data or updating a database takes time. If the operation takes too long, the system may kill your application before it has had a chance to finish launching. Apple refers to this event as the watchdog killing your application.

Your application is given a limited amount of time to launch. If it fails to launch within that timeframe, the operating system kills your application. This means that you have to carefully consider when and where you perform certain operations, such as seeding your application’s data store.

6. Displaying the List of Items

We now have a list of items to work with. Displaying the items in the table view of the list view controller isn’t difficult. Take a look at the implementation of the three methods of the UITableViewDataSource protocol shown below. The implementations should look familiar if you’ve read the tutorial about table views.

let item = Item() print(item.uuid)

6

There are two details we need to take care of before running the application, declaring the constant CellIdentifier and telling the table view which class to use for creating table view cells.

let item = Item() print(item.uuid)

7

While you’re at it, set the title property of the list view controller to "Items" .

let item = Item() print(item.uuid)

8

Run the application in the simulator. This is what you should see in the simulator.

iOS From Scratch With Swift: Building a Shopping List Application 1

7. Adding Items – Part 1

No matter how well we craft the list of seed items, the user will certainly want to add additional items to the list. On iOS, a common approach to add new items to a list is by presenting the user with a modal view controller in which new data can be entered. This means that we’ll need to:

  • add a button to the user interface to add new items
  • create a view controller that manages the view that accepts user input
  • create a new item based on the user’s input
  • add the newly created item to the table view

Step 1: Adding a Button

Adding a button to the navigation bar requires one line of code. Revisit the viewDidLoad() method of the  ListViewController class and update it to reflect the implementation below.

let item = Item() print(item.uuid)

9

In the lesson about tab bar controllers, you learned that every view controller has a tabBarItem property. Similarly, every view controller has a  navigationItem property, a unique instance of  UINavigationItem representing the view controller in the navigation bar of the parent view controller—the navigation controller.

The navigationItem property has a  leftBarButtonItem property, an instance of  UIBarButtonItem , that references the bar button item displayed on the left side of the navigation bar. The  navigationItem property also has a  titleView and a  rightBarButtonItem property.

In viewDidLoad() , we set the  leftBarButtonItem property of the view controller’s  navigationItem to an instance of  UIBarButtonItem by invoking  init(barButtonSystemItem:target:action:) , passing in  .Add as the first argument. The first argument is of type UIBarButtonSystemItem , an enumeration. The result is a system-provided instance of  UIBarButtonItem .

Even though we’ve already encountered the target-action pattern, the second and third parameter of init(barButtonSystemItem:target:action:) needs an explanation. Whenever the button in the navigation bar is tapped, a message of  addItem(_:) is sent to  target , that is,  self or the  ListViewController instance.

As I said, we’ve already encountered the target-action pattern when we connected the touch event of a button to an action in the storyboard. This is very similar, the only difference being that the connection is made programmatically.

The target-action pattern is a common pattern in Cocoa. The idea is simple. An object keeps a reference to the message that needs to be sent and the target, an object that acts as the receiver of that message.

The message is stored as a selector. Wait a minute. What is a selector? A selector is the name or the unique identifier that is used to select a method that an object is expected to execute. You can read more about selectors in Apple’s Cocoa Core Competencies guide.

Before running the application in the simulator, we need to create the corresponding addItem(_:) method in the list view controller. If we don’t do this, the view controller isn’t able to respond to the message it receives when the button is tapped and an exception is thrown, crashing the application.

Take a look at the format of the method definition in the next code snippet. As we saw earlier in this series, the action accepts one argument, the object that sends the message to the view controller (target). In this example, the sender is the button in the navigation bar.

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

0

I’ve added a print statement to the method’s implementation to test if everything works correctly. Build the project and run the application to test the button in the navigation bar.

Step 2: Creating a View Controller

Create a new UIViewController subclass and name it  AddItemViewController . In AddItemViewController.swift , we declare two outlets for two text fields, which we’ll create in a few moments.

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

1

We also need to declare two actions in AddItemViewController.swift . The first action,  cancel(_:) , cancels the creation of a new item. The second action,  save(_:) , uses the user’s input to create and save a new item.

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

2

Open Main.storyboard , drag a  UIViewController instance from the  Object Library  to the workspace , and set its class to  AddItemViewController in the  Identity Inspector .

iOS From Scratch With Swift: Building a Shopping List Application 1

Create a manual segue by pressing Control and dragging from the  List View Controller object to the  Add Item View Controller  object. Select  Present Modally from the menu that pops up.

iOS From Scratch With Swift: Building a Shopping List Application 1
iOS From Scratch With Swift: Building a Shopping List Application 1

Select the segue you just created, open the Attributes Inspector , and set its  Identifier to  AddItemViewController .

iOS From Scratch With Swift: Building a Shopping List Application 1

Before we add the text fields, select the add item view controller and embed it in a navigation controller by selecting Embed In > Navigation Controller from the  Editor menu.

Earlier in this lesson, we programmatically added a UIBarButtonItem to the list view controller’s navigation item. Let’s find out how this works in a storyboard. Zoom in on the add item view controller and add two  UIBarButtonItem instances to its navigation bar, positioning one on each side. Select the left bar button item, open the  Attributes Inspector , and set  Identifier to  Cancel . Do the same for the right bar button item, setting its Identifier to  Save .

iOS From Scratch With Swift: Building a Shopping List Application 1

Select the Add Item View Controller  object, open the  Connections Inspector on the right, and connect the  cancel(_:) action with the left bar button item and the  save(_:) action with the right bar button item.

iOS From Scratch With Swift: Building a Shopping List Application 1

Drag two UITextField instances from the  Object Library to the view of the add item view controller. Position the text fields as shown below. Don’t forget to add the necessary constraints to the text fields.

iOS From Scratch With Swift: Building a Shopping List Application 1

Select the top text field, open the Attributes Inspector , and enter  Name in the  Placeholder field. Select the bottom text field and, in the  Attributes Inspector , set its placeholder text to  Price and  Keyboard to  Number Pad . This ensures that users can only enter numbers in the bottom text field. Select the  Add Item View Controller object, open the  Connections Inspector , and connect the  nameTextField and  priceTextField outlets with the corresponding text field in the view controller’s view.

That was quite a bit of work. Everything we’ve done in the storyboard can also be accomplished programmatically. Some developers don’t even use storyboards and create the entire application’s user interface programmatically. That’s exactly what happens under the hood anyway.

Step 3: Implementing addItem(_:)

With AddItemViewController ready to use, let’s revisit the  addItem(_:) action in  ListViewController . The implementation of addItem(_:) is short as you can see below. We invoke  performSegueWithIdentifier(_:sender:) , passing in the  AddItemViewController identifier we set in the storyboard and  self , the view controller.

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

3

Step 4: Dismissing the View Controller

The user should also be able to dismiss the view controller by tapping the cancel or save button of the add item view controller. Revisit the cancel(_:) and  save(_:) actions in  AddItemViewController and update their implementations as shown below. We will revisit the  save(_:) action a bit later in this tutorial.

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

4

When we call dismissViewControllerAnimated(_:completion:) on the view controller whose view is presented modally, the modal view controller forwards that message to the view controller that presented the view controller. In our example, this means that the add item view controller forwards the message to the navigation controller, which, on its turn, forwards it to the list view controller. The second argument of dismissViewControllerAnimated(_:completion:) is a closure that’s executed when the animation is completed.

Run the application in the simulator to see the AddItemViewController class in action. When you tap the name or price text field, the keyboard should automatically pop up from the bottom.

iOS From Scratch With Swift: Building a Shopping List Application 1

7. Adding Items – Part 2

How will the list view controller know when a new item has been added by the add item view controller? Should we keep a reference to the list view controller that presented the add item view controller? This would introduce tight coupling, which isn’t a good idea, because it makes our code less independent and less reusable.

The problem that we’re faced with can be solved by implementing a custom delegate protocol. Let’s see how this works.

Delegation

The idea is simple. Whenever the user taps the save button, the add item view controller collects the information from the text fields and notifies its delegate that a new item was saved.

The delegate object should be an object conforming to a custom delegate protocol that we define. It’s up to the delegate object to decide what needs to be done with the information the add item view controller sends. The add item view controller is only responsible for capturing the input of the user and notifying its delegate.

Open AddItemViewController.swift and declare the  AddItemViewControllerDelegate protocol at the top. The protocol defines one method, to notify the delegate that an item was saved. It passes along the name and the price of the item.

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

5

As a reminder, a protocol declaration defines or declares the methods and properties that objects conforming to the protocol should implement. Every method and property in a Swift protocol is required.

We also need to declare a property for the delegate. The delegate is of type AddItemViewControllerDelegate? . Note the question mark, indicating it is an optional type.

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

6

As I mentioned in the lesson about table views , it’s good practice to pass along the sender of the message, the object notifying the delegate object, as the first argument of each delegate method. This makes it easy for the delegate object to communicate with the sender without the strict requirement to keep a reference to the delegate.

Notifying the Delegate

It’s time to use the delegate protocol that we declared a moment ago. Revisit the save(_:) method in the  AddItemViewController class and update its implementation as shown below.

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

7

We use optional binding to safely extract the values of the name and price text fields. We notify the delegate by invoking the delegate method we declared earlier. At the end, we dismiss the view controller.

Two details are worth pointing out. Did you spot the question mark after the delegate property ? In Swift, this construct is known as optional chaining. Because the delegate property is an optional, it is not guaranteed to have a value. By appending a question mark to the delegate property when invoking the delegate method, the method is only invoked if the delegate property has a value. Optional chaining makes your code much safer.

Also notice that we create a Float from the value stored in  priceAsString . This is necessary, because the delegate method expects a float as its third parameter, not a string.

Responding to Save Events

The final piece of the puzzle is to make ListViewController conform to the  AddItemViewControllerDelegate protocol. Open  ListViewController.swift and update the interface declaration of  ListViewController to make the class conform to the new protocol.

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

8

We now need to implement the methods defined in the AddItemViewControllerDelegate protocol. In  ListViewController.swift , add the following implementation of  controller(_:didSaveItemWithName:andPrice:) .

C6B81D40-0528-4D2C-BB58-6EF78D3D3DEF

9

We create a new Item instance by invoking init(name:price:) , passing in the name and price that we receive from the add item view controller. In the next step, the  items property is updated by adding the newly created item. Of course, the table view doesn’t automagically reflect the addition of a new item. We manually insert a new row into the table view. To save the changes to disk, we call  saveItems() on the view controller, which we implemented earlier in this tutorial.

Setting the Delegate

The final piece of this somewhat complex puzzle is to set the delegate of the add item view controller when presenting it to the user. We do this in prepareForSegue(_:sender:) as we saw earlier in this series.

import UIKit  class Item: NSObject, NSCoding {      ...  }

0

If the segue’s identifier is equal to AddItemViewController , we ask the segue for its  destinationViewController . You may think the destination view controller is the add item view controller, but remember that the add item view controller is embedded in a navigation controller.

This means that we need to fetch the first item in the navigation controller’s navigation stack, which gives us the root view controller or the add item view controller object we’re looking for. We then set the delegate property of the add item view controller to  self , the list view controller.

Run the application one more time to see how everything works together—as if by magic.

Conclusion

That was a lot to take in, but we’ve accomplished quite a bit already. In the next lesson, we make some changes to the list view controller to edit and remove items from the list. In that lesson, we also add the ability to create a shopping list from the list of items.

If you have any questions or comments, you can leave them in the comments below or reach out to me on Twitter .

原文  http://code.tutsplus.com/tutorials/ios-from-scratch-with-swift-building-a-shopping-list-application-1–cms-25515

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » iOS From Scratch With Swift: Building a Shopping List Application 1

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮