Tag Archives: Xcode

This Old App: 2013 Code, Meet Xcode 8

A few months ago my app Interval RunCalc was removed from the App Store for being too old and not updated. In this series of articles I’ll document what I did on rebuilding and improving the app. In our first article I analyzed the changes necessary. In the second, I built the model in a Swift playground. Now it is time to place that model into the old app, and make a temporary view and controller to test it.

Open the App in Xcode

I made a copy of the original app development files, then opened them in Xcode. The issue navigator started complaining right away:

Clicking on the issue, I find several updates Xcode wanted to make.

I select Perform Changes resolving the issues. I find in the target settings for the project, there’s more issues: provisioning profiles for debug and release.

>

Above the issue, I’ll find a checkbox Automatically Manage Signing. I check that and enable the automatic signing.

I find provisioning one of the most irritating parts of working with Xcode. Anything to get me away from it is a good thing.

Of course that’s not the end of the issues. I get another error message.

If I manage singing automatically, I have to pick the team. I only have me so I pick myself from the drop down. The target is set up, and Xcode is happy again.

Configuring the New Version

This is a major version update for the app. I want to change the version number to version 2.0. Originally I wanted the app to be called runner’s calc, but that name was taken. I settled on the mouthful Interval RunCalc. That’s too long for an icon, I want to change the Display Name to a shorter name, Run Calc to show better on an icon.

The next change I’ll make is the deployment info. Currently, the deployment is iOS 8.0 and only on iPhone in portrait.

Which iOS version to set to is a good question. In most cases, you want to deploy a version or two back for compatibility. Here’s where getting user data comes in handy. I’ll use iTunes Connect’s analytics, and look at the analytics for 2016.

Two active devices per month means there’s almost no one but me using this. Usage data strongly hints me that the biggest user is me. The large peaks occur at races I either ran or was a spectator (marathons are my 2018 goal). While a second user might be at the same races, I find it unlikely.

I’m looking at a sample that’s less than a third of total apps out there. Still, upping the version to 10.3 is not tragic since it looks like nobody but me uses this. I’ll bump it up all the way for the revision. I’m also going to make this a Universal app instead of a iPhone app. My new settings look like this:

Later, I’ll set the iPad and iPhone interface to the same initial storyboard.

The Old Structure

Opening the project navigator, You can see how I put together this app:

You’ll see the root files are the RunnersCalc files. The top level contains the initial view controller and the help controller. There was a help controller, though no one knows how to get to it — I unfortunately hid it too well. If you got to it, it was just written instructions, and very badly written. It’s another thing to go on the list of changes.

The folders under this root level has one folder for the model, and three folders for the view controllers: Intervals, Input controls and Splits.

For those from a completely Swift background, those .h and .m files are Objective-C files. The .h files contain a header which indicates what properties and methods are sharable to the rest of the app. Your code is in the .m files.

Opening up the Model folder and clicking the PCPace.h file, I find the declarations for the model, the properties of the model were:

//properties of the object

@property NSTimeInterval pace; // seconds per mile

@property float speed; // miles per hour

@property float distance; //miles

@property NSTimeInterval time; //seconds of the run or split

@property NSTimeInterval splitPace; //seconds per mile or km

@property int splitCount; //total number of splits

@property int splitNumber; //current split -- necessary?

@property int fullSplitCount; //number of splits for the full mile/kilometer

@property NSTimeInterval elapsedTime; //elapsed time in a split

@property float totalDistance; //totalDistance to this point

enum PCVariable {

  kcPCSpeed,kcPCDistance,kcPCPace,kcPCTime

};

@property enum PCVariable finding;


Add the Playground Code to the App

I’m not too worried about any of this code, since I’m about to replace it all. I open up the RunStats playground so I can copy my code. I have the code stored in four files:

In the App project, I’ll make four new files in the model folder. I’ll select the Model Folder first to keep the group correct as I add files. I’ll hit Command-N to make a new file. There’s several ways I can make these files, since I’m cutting and pasting everything into the file. I’ll select Swift File for the template, though I could have just as easily picked a Cocoa Touch Class subclassing NSObject.

I’ll be asked to save with a file name. I’ll call this one RunStats.

On the bottom, I’ll make sure the target is correct and the Group is Model

We click Create, and get a new Message:

I usually create the bridging header. As the message says the bridging header opens up the classes, methods and properties in the Objective-C code to the the newer Swift code if you want to use it. In this application I won’t use it. However, it is not a lot of overhead. I’m planning to scrap all the Objective-C, but I never know in an intermediate step if I’ll need it, so I’ll create it anyway.

I’ll repeat this three more times for the classes DistanceStat, PaceStat and TimeStat. I have two classes in the RunStats playground page. I’ll add one more for RunStatsIntervals, so every class has its own file.

I’ll organize this a little better. I’ll select my Objective-C files PCPace.h, PCPace.m, and the bridging header. I’ll right click and make New Group From Selection, naming the group Objective-C. I’ll do the same for the Swift code, selecting it all and making a Swift group for it.

I’ll select RunStats, and see this:

//

// RunStats.swift

// RunnersCalc

//

// Created by Steven Lipton on 4/6/17.

// Copyright © 2017 Steven Lipton. All rights reserved.

//



import Foundation


I’ll delete the import foundation, and then copy and paste my RunStats class from the playground to the App, leaving the intervals class and the test data in the playground. I’ll make sure I copy the import UIKit when I do. It will of course have lots of errors for the moment. We have the three classes it refers to yet to add. I’ll do the same copy and paste to the other four files, adding the classes and import UIKit in each case.

Once I’m done with this there are seven issues to solve. When transferring a playground, you’ll probably get these errors.

If you click on the first error, it will show you this in the editor:

The time.seconds() and the distance.meters() lines Xcode wants to assign to something. I used them to get a quicklook for debugging the playground. All the errors are like this. The simple solution is delete that code. The fatal error is the same thing. it’s on the line

 self.pace

It was to get a quicklook for the object in the playground. I deleted it to clear the error.

A New View and Controller

The new model is in the app, and I’d like to test it. Right now, this code is ignored by the app. What I’ll do is set up a new storyboard to check the model. In the Storyboards group, I have a blank iPad storyboard and the Main_iPhone.storyboard file, which has the current storyboard for the app:

This is a big mess. This is one of those times when doing an update project like this you just shake your head and wonder what you were thinking back then. With a little investigation, you’ll find some of those answers. I iterated this model with a inadequate model to start, and I needed a lot of UI to cover up my mistakes. The major reason this app never had an iPad version was I’d have to replicate this storyboard on an iPad.

The new app had a completly new Storyboard. I’m using auto layout to need one storyboard for all devices. I Press Command-N, selecting a Storyboard object and name it Main, making sure my Group is Storyboards.

The storyboard I get is completely blank. I’ll drag a view controller onto the storyboard

I’ll change the attribute to give this a name and set this view controller to Is Initial view controller.

I’ll need a ViewController file for this scene. I’ll press Command-N again, click a Cocoa Touch Class for the template and make the Class RootViewController, subclassing UIViewController and Swift as the language.

When I save the file I’ll save it to the RunnersCalc root folder, where the root and help view controllers are. Going back to the main storyboard, I’ll set the Custom Class to RootViewController. Now I’m ready to add some controls.

This first layout is only for testing purposes. No auto layout just yet, just drag and drop. I’ll add a label for a result and a button to calculate the result. I’ll use text fields for input, so I’ll need room for the keyboard.  I’ll set the button at the center of the iPhone screen, giving the keyboard I hope plenty of room, and the result at the top. I’ll make the label read Result and the button Calculate

I Drop two text fields between the label and the button, which I’ll call variable 1 and 2. That will be for decimal values. For time values I’ll add three textfields under those two text fields. The layout looks like this, using placeholder text for titles.

Next, I’ll wire up my view controller. I’ll open the assistant editor. All but the button are outlets, So I’ll do those first, control dragging into the view controller.

@IBOutlet weak var result: UILabel!

  @IBOutlet weak var variable1: UITextField!

  @IBOutlet weak var variable2: UITextField!

  @IBOutlet weak var hours: UITextField!

  @IBOutlet weak var minutes: UITextField!

  @IBOutlet weak var seconds: UITextField!

I’m keeping this very generic for a reason. This is a test bed. I’ll use these same fields to check different parts of the model. I’ll calculate the values in code in the calculate button, which I now wire up as an action.

@IBAction func calculate(_ sender: UIButton) { 

  }

There’s a function I’ll need in the view controller. It will take a TextField and convert the text in it to a Double. I can close up the storyboard and assistant editor, and then add the function to RootViewController

  func double(textField:UITextField!)->Double{

    guard let textField = textField 

       else {return 0.0} //uwrap textfield

    guard let value = Double(textField.text!) 

       else {return 0.0} //find double

    return value

  }

This will give me a value of 0 for anything that isn’t a true Double, and return a Double if it is. There’s three optionals, and I deal with all the unwrapping here giving me a value or a zero. This is not something I’ll need in the finished code. The input system (which we cover next time) will restrict the user to only valid values.

With the structure of the test bed done, I’ll add the model to the code.

 let runStats = RunStats()

I’ll test the function the IBAction calculate. I’ll start with a simple test to make sure everything works, converting kilometers to miles.

@IBAction func calculate(_ sender: UIButton) {

    let distance = double(textField: variable1)

    runStats.distance.distance(kilometers: distance)

    let resultText = String(format: "%02.2d", runStats.distance.miles())

    result.text = resultText

  }


I use the double function to turn the variable1 text field into a double, then add it to the model with a kilometer measure. I change the distance to miles in a string, placing the result in result.

I’m almost ready to run this code, but there’s one more thing I need to do: Change the initial storyboard. In the General tab of the Target Properties, you’ll find this:

When run, the app goes to the old Main_iPHone storyboard, not the new Main. I also restricted the layout to portrait in the old version, and I’ll use all orientations for the new version. I set both the iPad and iPhone to the same storyboard Main:

Setting my simulator to an iPhone 7 I run the app. Two things happen. I get 44 warnings, but the app runs. I try putting in a value. I find I didn’t leave enough room for the keyboard

I’ll stop the app and move the calculate button up. I try running again. I’ll type in 5 when the calculator appears, and get rewarded with a value of…Zero instead of 3.11.

I first check to see if the model is working right with a few print statements

@IBAction func calculate(_ sender: UIButton) {

    let distance = double(textField: variable1)

    print("distance in km \(distance)")

    runStats.distance.distance(kilometers: distance)

    print("distance in km \(runStats.distance.kilometers())")

    print("distance in km \(runStats.distance.miles())")

    let resultText = String(format: "%02.2d", runStats.distance.miles())

    result.text = resultText

  }

When run, I get output on the console:

distance in km 5.0

distance in km 5.0

distance in km 3.10685596118667

The model is working fine. My bug is a silly one of setting the format wrong in the string. I change the format to this:

let resultText = String(format: "%6.2f", runStats.distance.miles())

When I run again, the test works.

I go back to the storyboard. That alphanumeric keyboard is annoying. For all the TextFields, I set the keyboard type to decimal pad.

I’m ready to try another test. Let’s take distance in miles and a speed in miles per hour and calculate time. I’ll convert distance and speed to Doubles, then set them to the runstats model. I’ll try this code.

@IBAction func calculate(_ sender: UIButton) {

    let distance = double(textField: variable1)

    let speed = double(textField: variable2)

    runStats.distance.distance(miles: distance)

    let pace = PaceStat(milesPerHour: speed)

    runStats.time(changedBy: pace)

    result.text = runStats.time.hoursMinutesSeconds()

  }  

I try a simple case where I run five miles at five miles an hour, And yes, I get the expected one hour.

I try a function with a time variable using the three textfields on the bottom. I’ll compute the pace in minutes per mile for a given distance.

@IBAction func calculate(_ sender: UIButton) {

    runStats.distance = DistanceStat(miles: double(textField: variable1))

    let seconds = Int(double(textField: self.seconds))

    let minutes = Int(double(textField: self.minutes))

    let hours = Int(double(textField: self.hours))

    let time = TimeStat(hours: hours, minutes: minutes, seconds: seconds)

    runStats.pace(changedBy: time)

    result.text = runStats.pace.minutesSecondsPerMile()

  }

I build and run this. A simple test is a one hour run of five miles, which should give us a twelve minute mile.

And my target time for my first marathon of five hours 45 minutes ( yeah, I’m a bit slow)

As expected, I get the 13:10 pace. I’d do a lot of tests like this to make sure the components work with the model.

A Model Change

During the testing, I get the feel for using the code in the model. There’s one thing I don’t like. Consider these two statements

runStats.distance.distance(miles: 5.0)

print(runStats.distance.miles())

I use different identifiers for the setter and getter. I like this better:

runStats.distance.miles(5.0)

print(runStats.distance.miles())

It is easer to read and shorter to type. It is not a huge change in the model code, and it works better. I can change the setters to this:

//set distance from miles

  public func meters(_ meters:Double){

    distanceStat = meters

  }

  public func miles(_ miles:Double){

    distanceStat = miles * 1609.344

  }

  public func kilometers(_ kilometers:Double){

    distanceStat = kilometers * 1000.0

  }


I change the resulting code using these functions in the rest of the model. I’ll do the same for the other stat types. The current code only requires changes to the stat types DistanceStat, PaceStat and TimeStat. Pace and time add one wrinkle in two and three parameter setters. The first parameter becomes the name of the setter, and the rest are parameters like this:

public func hours(_ hours:Int,minutes:Int,seconds:Int) {

    timeStat = Double(seconds + minutes * 60 + hours * 3600)

  }

I didn’t do it intially, but some uses of these functions in initializers will need to specify self to work properly.

public init(minutes:Int,seconds:Int){

    self.hours(0, minutes: minutes, seconds: seconds)

  }

Running the code as written I get the same results, and no problems.

The 44 Warnings 

There’s one last thing to address: those 44 warnings. Closing the folders they are in, I find they fall into the following categories:

Those warnings are why Apple wants me to change this code, and threw me out of the app store to do it. This is all old stuff that needs updating. Sometimes living in the Swift world, I forget the Objective-C is improving over time too. The deprecations for example are all the same issue: there’s no more UIActionSheets in iOS.  Many of the others are number conversions which changed in newer versions of iOS in Objective-C

I have choices here: I can fix this code and remove the errors, I can ignore the errors for now, or I can delete the code now. While the errors are annoying, they are not affecting the code. I go for the last one. I will delete all of the Objective-C code before shipping, replacing it with the new Swift code. It doesn’t need to be there, but I use it as documentation for the old version until I don’t need it anymore.

So I got the new model into the old code. I started replacing components, and got the old code to actually function in the latest Xcode. My next steps are to get the views and controllers working properly. I”ll start that process not at the root, but by replacing those text views with UIPickers in the next installment.

I’m a LinkedIn Learning author, with five courses up in the LinkedInLearning library.  My latest is a trilogy for iOS notifications: Learning iOS NotificationsiOS App Development: Notifications, and watchOS App Development: Notifications If you a re interested in Notifications, I’d Check them out. 

Tab Bar Controllers in Xcode 8 Storyboards

In this lesson, we’ll take a look at tab bar controllers and how to add them in the storyboard.  For more on implementing them completely in code see Swift Swift: Using Tab Bar Controllers in Swift. For more on data sharing once set up in the storyboard, see Passing Data in Tab Bar controllers

The Real Difference between Navigation and Tab Bar Controllers

Before we begin, It’s important to remember the difference between  tab bar and navigation controllers. Navigation controllers are stack based, while tab controllers are parallel in execution. Think of a navigation controller as a stack of cards. You can only play the visible card. If you put a card on top of another card, only the top card is active.

2016-07-06_06-20-22

Now imagine you take several cards and deal them face up. You can play any face up card. They all are active. This is a tab bar controller.

2016-07-06_06-19-45

As we go through lessons on tab bar controllers, it is very important to remember this difference.

Creating From the Template

The easiest way to make a tab bar controller is through Xcode’s template. In Xcode, press Command-Shift-N or File>New>Project. Select the tabbed application.
This will give you a window with two tabs installed with two labels each and a tab item on the bottom of each controller. The template tends to make things more complicated instead of less, so it is rarely used.

Creating From A Single View

The most common way to make a tab bar controller is to start from a single view and embed it in a controller. We’ll try an example of this one in Xcode. Press Command-Shift-N and select Single View. Make a file named SwiftTabBarEmbed, using Swift as the language and with a Universal device. Once created, go over to the Storyboard.  Make sure the Preview setting is  View as: iPhone6s(wC hR) in the lower left corner of the storyboard:

2016-07-05_06-18-15

Change the color of the view controller’s background.

2016-07-05_06-08-41

Select the view controller by clicking the view controller icon or title, and select Editor>Embed in>Tab Bar Controller.

2016-07-05_06-06-10

This will turn the single view controller into the first view controller of the tab bar.

2016-07-05_06-12-22

Add another view controller by dragging out a view controller then control-dragging from the tab bar controller to the new controller. In the pop-up, select under Relationship Segue the view controllers option.

2016-07-05_06-14-57

Another segue appears on the story board.

2016-07-05_06-16-22

You’ll see a second tab in the tab bar controller.

2016-07-05_06-21-14

Adding More Tabs with System Icons

To configure the tab bar icon, go to the Item icon in the  Yellow View Controller (not on the tab view controller) we just made and click it.

2016-07-05_06-23-33

This will bring up in the properties inspector the Tab Bar Item and Bar Item properties. In the Tab Bar Item properties is the System Item drop down. This is where we configure the tab bar button.

2016-07-05_06-25-14

In the System Item menu, Click the drop-down for System Item. you will see a  list of system icons.

2016-07-05_06-26-37

Select Favorites. The Icon changes both on the view controller and the tab bar controller.

2016-07-05_06-28-51

2016-07-05_06-28-26

Click the other view controller’s tab bar, and change the system Icon to Recents.

2016-07-05_06-31-06

2016-07-05_06-31-27

Using  the More Feature

Click on the Preview size Button which reads iPhone 6s(wC hR). A new toolbar appears underneath it. Select the iPad 9.7″.

2016-07-05_06-36-26

The view controllers in the storyboard change to iPads, so you may need to change the zoom level.

2016-07-05_06-40-45

Drag out  six more view controllers, so we have a total of eight controllers.  Change their background colors to make then easily distinguishable.

2016-07-05_06-47-05

Control-drag from the tab bar controller to each new view controller and assign ContactsBookmarksSearch, Downloads, and Most Viewed to the controllers, leaving one more as a custom item. The tab bar controller should look like this:

2016-07-05_06-52-27

Change the Preview mode back to to an iPhone 6s. While your icons may be in a  different order, you will get something like this for the tab bar at the bottom of the phone:

2016-07-05_06-56-32

Compact widths, the width on most phones, cannot handle more than five tabs. If you add more than five tabs, it places a More tab as the fifth tab. What does the More tab do? Build and run as an iPhone 6s in the simulator.

2016-07-05_07-04-24

Tap on the More icon in the lower right. A table appears with the missing tabs.

2016-07-05_07-07-21

 More gives you access to all your tabs. If you tap the tab in the table’s cell, the tab’s controller will appear. Select Downloads, and you get the downloads tab.

2016-07-05_07-10-13

Tabs selected by More act slightly differently than ones in a regular tab. They are embedded in a navigation controller. You’ll see at the top a navigation bar.

2016-07-05_07-14-57

The navigation bar goes back to the More table.  Tap Bookmarks, and notice the navigation bar is not there, then tap More again and the Downloads controller is still there.

   2016-07-05_07-18-21    2016-07-05_07-10-13

Select More  in the navigation bar to go back to the Table view

2016-07-05_07-07-21

User Editing the Tab Bar with More

Without any extra code, tab bar controllers let users customize their tab bars. Tap the Edit button, and you get a configuration view:

2016-07-05_07-23-00

Looking closer, you’ll see that the hidden tabs are highlighted and the visible tabs are dimmed

2016-07-05_07-23-20

Drag the Item tab down to the tab bar. Drop it on top  of Recents. It replaces Recents.

2016-07-05_07-27-25

You can also drag tabs in the tab bar to change the order. Drag Downloads on top of Contacts. Drag  Downloads to the right, swapping it with Bookmarks.  Your tab bar now looks like this:

2016-07-05_07-37-20

I want to add two cautions to this: One of the system icons you can use is a More icon. Be careful when using the More icon in a tab bar. It can cause user confusion between the More tab and your own use for More. Make sure it is clear what you are doing.

The second caution is more insidious. When programming for tab controllers, you can get the tabs from an array. However, the order and visible tabs can change by user request with  the More tab. Avoid hardwiring the tabs by their location in the array since users can change them.

Close the simulator. In the storyboard, delete the Bookmarks, Recents and Favorites view controllers (or their segues if you don’t mind the warnings)  so you get a tab bar like this:

2016-07-05_07-45-54

As we now have five icons, this will keep off the More icon, which requires six tabs to show up. You can reorder the tabs in the tab view controller by drag and drop.   Reorder using drag and drop to this:

2016-07-05_07-50-02

Custom Tab Bar Items

Click on the tab bar for the Item view  controller. We can see all the tab bar properties.

2016-07-05_06-25-14

The lower half has bar item properties, which directly control the title and icon for the tab. We can easily change the text. Change the Bar Item Title to Pie.

2016-07-05_07-55-00

The title changes on the tab bar:

2016-07-05_07-55-36

If we change a System items’s title, it becomes a custom item, and the icon disappears. Do the same to change Downloads to Pizza. Also change Contact to Square.  SquarePizza and Pie have squares for icons.

2016-07-05_08-00-30

Icon Images

Under Title in the properties inspector,  we have a drop down for Image.

2016-07-05_07-55-00

For a custom bar item we can supply the icon. Image icons are not like other images. They are monochrome images,  unlike anything else you have ever seen. They are set not by color but by the alpha value , or transparency of the image. You can make any image here any color, but it will show up as a solid tint color if you select an alpha value of 100% and background color if you select 0% alpha. For example I created the following icons in both high and low resolutions:

gradient bar Pizza and Pie Bat items pizza bar icon pizza bar icon@2x pie bar icon pie bar icon@2

Icons are 30 points by 30 points, which means for retina images make the icon a 60 pixel by 60 pixel image and for low resolution images 30 pixel by 30 pixel. You can download them individually by right clicking the images above, and saving them to a folder.  Use these file names:

Screenshot 2015-01-27 07.37.51

You can also download a.zip file Of these here:Tab Bar Assets

For those not familiar with Apple image naming conventions, low resolution images have a file name. Retina Images have @2x appended to that file name. This tells Xcode to know what resolution it is. We have both 60×60 pixel retina and 30×30 pixel standard images. By loading both into Xcode, the system will use the correct one automatically for the device.
In Xcode’s navigator panel, open up Assets.xcassets. Select all the icon files. Drag them into the center panel in Xcode, which will highlight. Release the mouse button, and you will see a new collection in the image assets.

2016-07-05_08-06-31

Return to the storyboard. On the tab we titled Square,click anywhere on the tab bar.  Select the image drop-down in the tab bar properties and select the gradient bar icon.

2016-07-05_08-09-01

The Gradient bar now replaces the place marker  square:

2016-07-05_08-11-16

Find the Pizza and Pie tab views on the storyboard, and change them the same way. The Tab bar controller now has three custom icons.

2016-07-05_08-12-21

Build and run with the iPhone 6 simulator, and you will see our three icons, with  Pie  highlighted as selected.

Using View Controllers in Tab Bar Controllers

As mentioned earlier, tab bar controllers make a set of single view controllers running parallel to each other. The tab bar switches the view, but the view we leave does not get dismissed, just hidden. there are two uses for such a controller. The first is a series of small independent applications related to each other. Apple’s clock app is an exmaple. The timer, alarm clock, world clock, and stopwatch all are related by a theme of time. All run independently of each other. The second use of Tab bar controllers is to use a shrared data set in different ways. Social media apps like Twitter or Instagram for example use tab bars to have different functions of displaying posts, searches, or displaying notifications that all use the same database. For this second type of use, see see Passing Data in Tab Bar controllers. We’ll look at the first type here.

Basic View Controllers

Connecting a view controller in tab bars is the same as hooking up any other view controller. Make a new view controller class by pressing Command-N. Make a new Cocoa Touch Class view controller named SearchViewController. Change the class code to this.

class SearchViewController: UIViewController {
    var count = 0
    @IBOutlet weak var myLabel: UILabel!
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        myLabel.text = "Count \(count)"
        count += 1
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        count = 1
    }
}

Note how I used viewDidLoad and viewWillAppear here. In tab bar controllers, ViewDidLoad only fires once, when we first load the view controller. Use it for first initializations only. The code above initializes the count to 1. The method viewWillAppear fires every time the view appears, and so we use that to do any updating. This code assumes that this view controller will remain active even when it is not visible.

Go to the storyboard and select the Search view controller on your storyboard. In the identity inspector set the class to SearchViewController.

2016-07-06_05-43-22

On the search view controller  in the storyboard drag a label to the center of the view.

2016-07-06_05-34-14

Set the label font size to 32 points. Click the auto layout align button alignment icon  at the bottom right of the storyboard. In the menu that appears, check Horizontally in Container and Vertically in Container  with values of 0 points. Set Update Frame to Items of New Constraints.

2016-07-06_05-36-05

Add the two constraints. Open up the assistant editor. Drag from the circle next to the outlet to the label to connect it.

2016-07-06_05-49-06

Build and run. Select the search tab, then another tab a few times. You will see the count increase every time you return to the search tab.

2016-07-06_05-51-50     2016-07-05_08-15-11     2016-07-06_05-52-13

Embedding Navigation Controllers in Tab Bar Controllers

You can also embed navigation controllers into tab controllers. Stop the simulator and go back to the storyboard. Close the Assistant editor if it is open. Select the Pizza View Controller’s icon on the storyboard. In the drop down menu, select Editor>Embed in>Navigation Controller

2016-07-06_05-58-46

The view controller changes into a Navigation controller with the tab bar icon. Attached to the navigation controller is a view controller.

2016-07-06_06-05-43

 On the view controller, Double click the center of the navigation controller bar. Change the title to Pizza!!!. Drag a bar button item to the upper left of the controller on the storyboard. Title it More Pizza.

2016-07-06_06-08-10

Add another view controller, next to  the one we just configured. Control-Drag from the bar button item to the new view controller. Set up a show segue. In the new view controller, Drag a Navigation item to the navigation bar. Change the item’s title to More Pizza!!!

2016-07-06_06-10-09

Build and run. Select the Pizza tab and we have our navigation controller. Tap the More Pizza Button. The More Pizza!!! View Appears. Now go to another tab and then back to the Pizza tab. We remain on the More Pizza!!! view.

2016-07-06_06-13-41

You can use navigation controllers inside a tab bar controller to your heart’s content. However, tab bar controllers can only be used once in a project, as the top level controller. Embedding more tab bar controllers does not work.

This is the basics of tab bar controllers. For more information on sharing between the controllers and a more extensive coding example, see Passing Data in Tab Bar controllers.

Using The Navigation Bar Title and Back Button in Swift 3.0

In writing the Swift Swift View Controllers book, it came to my attention many people don’t understand the functionality of the navigation toolbar’s title and Back button. In an early part of writing, I planned to skip the topic as a minor detail  so I could get the book done and published. However, the built-in features of the navigation toolbar make it a powerful and useful feature that can’t be missed.

Setting Up the Storyboard

Open up a new single view project named NavBarDemo using Swift as the language and Universal device. Go to the storyboard and click the view controller icon in the scene. In the drop down menu, select Editor>Embed in>Navigation controller. Drag two buttons, one labeled Pizza and the other labeled Pasta out to the scene. Set the font size on both to 26 Point. Make the Pizza button White(#FFFFFF) text on a Red(#FF0000) background. Make the Pasta button White(#FFFFFF) text on a Blue(#0000FF) background. Arrange them like this on the storyboard:

2016-06-22_06-17-09

Select the Pizza button. Click the  auto layout pin button pinMenuButton. Pin the Pizza button 0 points up, 0 left, 0 right, and 0 down like this:

2016-06-22_06-22-30

Add the four constraints without updating. Select the Pasta button. Click the pin button pinMenuButtonand repeat the constraints of   0 up, 0 left, 0 right, and 0 down. Add the four constraints. Now Control-drag from the Pizza button to the Pasta button. Select Equal Widths in the menu that appears.

2016-06-22_06-30-19

On the auto layout resolver menu resolver button, select Update Frame in the All Frames in View section.

2016-06-22_06-31-40

Your controller should look like this:

2016-06-22_06-34-58

Drag two more view controllers on to the storyboard. Make the background of one Blue(#0000FF) and the background of the other Red(#FF0000).

Control drag from the Pizza button to the red scene. Select a show segue.

2016-06-22_06-38-52

In the attributes inspector, set the segue identifier to pizza. Control-drag from the Pasta button to the blue scene. Select a show segue. In the properties inspector, set the segue identifier to pasta. Your storyboard should look like this:

2016-06-22_06-41-09

In this lesson we will do all the coding in ViewController, so there is no need of code in the two new controllers.

Setting the Navigation Title Bar

There is a property on UIViewController called navigationItem. When used with navigation controllers, this controls the navigation bar at the top of the view. The navigationItem property is an instance of UINavigationItem, which has four major properties: a title, a left bar button, a right bar button and a prompt. To set the title on the toolbar , you set the string for the title property. For example add this to the ViewController class

override func viewWillAppear(_ animated: Bool) {
        navigationItem.title = "One"
    }

Build and run. The root view now has One as a title.

2016-06-22_06-59-59

We used viewWillAppear and not viewDidLoad. ViewController is the root view controller in the navigation stack. It only loads once and stays active in the background. It will execute viewDidload only once. To make sure we update, we use viewWillAppear instead.
The title is dynamic. As a simple example, We’ll place a count in the title of the navigation bar. Add the following code:

var vcCount:Int = 0{
    didSet{
      navigationItem.title = "Count: \(vcCount)"
    }
  }

We used the  didSet property observer feature of Swift. Any time vcCount changes, the title changes with it. We change the count on any segue so we can increment in prepare for segue:. Add this:

override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
        vcCount+=1
    }

It’s rare to not have a statement within an if clause in a prepare for segue:. We want any segue to increment the count, so we don’t need the if.

We want to show the count when we load. Change the viewWillAppear to this:

 override func viewWillAppear(_ animated: Bool) {
        // navigationItem.title = "One"
        navigationItem.title = "Count: \(vcCount)"
    }

Build and run. Go back and forth in the views. The title changes every time you come back to it.

2016-06-22_07-04-28

Programming the Back Button

You’ll notice once you have a title, the navigation Back button disappears, to be replaced by the previous view’s title.

2016-06-22_07-08-42

The Back button reads the controller underneath the current controller for its title. You cannot set the back button title directly. Instead, you set the title of the current controller before you leave for the destination controller. If the previous view controller’s title is nil, the button titles itself Back. Change the prepare for segue: to this:

override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
        vcCount += 1
        navigationItem.title = nil
    }

Build and run.
You have the count on the root controller

2016-06-22_07-14-16

You have a Back button on the child controllers

2016-06-22_07-14-26

If you wanted to add your own text to the Back button, you have to change the title of the controller directly under it on the navigation stack. The simplest way is change the title just before you segue to the new controller. Change prepare for segue: to this:

override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
        vcCount += 1
        navigationItem.title = nil

        if segue.identifier == "pizza"{
            navigationItem.title ="Pizza to One"
        }
        if segue.identifier == "pasta"{
            navigationItem.title = "Pasta to One"
        }
    }

Before we segue to the pizza and pasta controller, we change the title of the current controller’s navigationItem. The Back button reads the title of navigationItem and sets the button’s title accordingly. The viewWillAppear method will reset the title to the count in the first view controller when we pop the Pizza or Pasta view controller. Build and Run. Select to the Pizza and Pasta buttons:

2016-06-22_07-30-59

2016-06-22_07-30-05

To see this happening, you can comment out this in viewWillAppear.

//navigationItem.title = "Count: \(vcCount)"

Build and Run. When you go back, the title remains:

2016-06-22_07-33-30

Uncomment the line before you go on.

The Size Sensitive Back Button

The Back button is sensitive to the space around it. The button’s title responds to not having enough space to place itself. Change prepare for segue: to this:

    override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
        vcCount += 1
        navigationItem.title = nil
        if segue.identifier == "pizza"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Pizza One"
            navigationItem.title = "Pizza to One"
        }
        if segue.identifier == "pasta"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Linguine all’arrabbiata"
            navigationItem.title = "Pasta to One"
        }
    }

We get the destination view controller and assign it to vc. We then send the title for the destination view controller  to vc.navigationItem.title. We’ve picked some long labels to test size restrictions.

Set the simulator to iPad Air 2. Build and run. Select the Pizza button.

2016-06-22_07-58-45

Go back to select the Pasta button.

2016-06-22_07-45-03

That works as we expected. Now try an iPhone 6 in the simulator. Run the demo, select the Pasta controller and you get this:

2016-06-22_07-57-14

The navigation controller title gets replaced with Back. Go back and try the Pasta Button.

2016-06-22_07-51-03

There is only the  2016-06-22_08-10-17 icon and no text for the back button. Rotate the device by pressing Command-Left Arrow:

2016-06-22_08-08-46

The text re-appears with more space to place the text.

The back button is intelligent. If it has enough space, it displays the title of the previous controller. If there is not enough space, it displays Back. If there is not enough space for the word Back, it displays only the 2016-06-22_08-10-17 icon.
There is an important design rule in play here that I am intentionally breaking to make the point: Keep your titles short in a navigation bar. If you keep titles short, you will have the full functionality of the back button.

The Whole Code

ViewController.swift

//
//  ViewController.swift
//  NavBarDemo for Swift 3.0
//
//  Created by Steven Lipton on 6/22/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    var vcCount:Int = 0{
        didSet{
            navigationItem.title = "Count: \(vcCount)"
        }
    }
    
    override func viewWillAppear(_ animated: Bool) {
        //navigationItem.title = "One"
        navigationItem.title = "Count: \(vcCount)"
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
        vcCount += 1
        navigationItem.title = nil
        if segue.identifier == "pizza"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Pizza One"
            navigationItem.title = "Pizza to One"
        }
        if segue.identifier == "pasta"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Linguine all’arrabbiata"
            navigationItem.title = "Pasta to One"
        }
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

How to Use Property Lists (.plist) in Swift

This will be the first in a series of lessons on persistence in iOS applications. Persistence is keep data around even if your app ends. As we’ll see in this series there are many techniques to save and read data. One Common one Apple uses frequently in Xcode is the Property list, or plist, named for its extension. In this first lesson we’ll look at how to make, modify and read property lists, and when to use them.

Understanding Property Lists

Open a new project in Xcode with a Single View template using Swift as the language. Save the project as SwiftPropertyListDemo. In the Project inspector, you will find a file info.plist. Select this file.

2016-02-10_05-52-25

You will find a screen looking like this:

2016-02-10_05-53-23

Many of the settings about your app the compiler needs to know is here. That’s one of the uses for property lists: to keep settings between runs of your application. Right click on the info.plist file name. In the menu that appears select Open as>Source Code

2016-02-10_06-01-03

You will get a very different looking screen of XML code starting like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>

In most cases, a .plist file is a XML file stored as a dictionary. We can edit property lists either in the property list editor or in the plists. You’ll notice there are key tags and string tags and one dict tag. This makes a dictionary of [String:String]

Understanding Basic XML

If that didn’t make sense to you, Lets discuss some basic XML. XML like HTML is a text file with start and end tags indicated by the characters < and > with an ending indicator of /. Unlike HTML, there are no reserved tags. The author makes up their own. In some program those tags indicate data. You’ll find XML in things like RSS feeds and podcast metadata. As we’ll discuss in a later lesson, some databases can be XML files. Microsoft Word’s .xdoc file format is actually a XML document.

All XML documents start with a header stating they are a XML document and what kind of document. In the above property list and all property lists we have the following header:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

In a property list, we have the following tags available

property list XML tags

Apple’s internal property list is a dictionary with key:value pairings. You can see here most of the entries are of the form of a key and a string, but there are Bool and array as well:

<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
...
        <key>LSRequiresIPhoneOS</key>
	<true/>
...
        <key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>

</dict>

Making Your Own Property Lists

You can make your own property lists. Within Xcode, there is a file template. Press Command-N to get a new file. Under iOS Resource select Property list

2016-02-10_07-06-42

Name the property list data, and store it in the SwiftPropertyListDemo group folder. You will see a blank property list in a dictionary.

2016-02-10_07-10-41

Hover over the root entry and you will find a + button to add an entry

2016-02-10_07-13-00

Click the button and a new entry appears, with a default type of String

2016-02-10_07-15-25

Click the type selector indicating String, and you will find the following selections

2016-02-10_07-15-26

Select Number. Change the value to 0.3, and the key to Red

2016-02-10_07-15-27

Press root’s add button two more times and add a Green value of 0 and Blue as a blank string.

2016-02-11_06-55-24

We’ll use these three values to save a color. While there is a better way to save colors that will be covered in another lesson, this is a simpler way for us to set a color from a property list.

Click the + button on Root. Make an entry with the key Pies and a type array. You’ll get an arrow next to the key Pie . Press the + button on the Pies key. You’ll get another entry.

2016-02-11_07-06-39

The + on any entry makes another entry below it. Since we do not want this entry, click the on the key to delete it. Notice the triangle next to Pies click it so it points down. In order to get elements in a dictionary or array you must click this to point down. Click the + on the pie entry four times to get four elements to your array.

2016-02-11_07-11-58

Keeping the array elements as strings add four pies to the property list.

2016-02-11_07-14-08

Click the Root + button. Add a boolean entry with the key Inverse Colors set to NO.

You can re-arrange your items by click and dragging up and down. You’ll see a blue line indicating where the item will be inserted. Re-arrange the keys like this. As far as the compiler is concerned you don’t have to do this, but it is easier for humans to read and understand.

2016-02-11_07-20-32

Editing the XML Property List

Let’s look at the XML for our property list. Right Click data.plist and select Open As>Source Code. We get the following code

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Inverse Color</key>
	<false/>
	<key>Red</key>
	<real>0.3</real>
	<key>Green</key>
	<key>Green</key>
	<integer>0</integer>
	<key>Blue</key>
	<string></string>
	<key>Pies</key>
	<array>
		<string>Cherry</string>
		<string>Apple</string>
		<string>French Silk</string>
		<string>Coconut Cream</string>
	</array>
</dict>
</plist>

Green is an integer and Blue is a string. The editor assumes an integer if you do not include a decimal point. Let’s change both real and add a value of 0.3 to the Blue entry. Change this

<key>Green</key>
<integer>0</integer>
<key>Blue</key>
<string></string>

to this

<key>Green</key>
<real>0.0</real>
<key>Blue</key>
<real>0.3</real>

Let’s add two more pies to our array, under the entry for Coconut Cream add the following:

<string>Blueberry</string>
<string>Boston Cream</string>

Switch back to the editor by selecting Open As> Property list and you will see your changes to the property list.

2016-02-11_07-55-22

Setting up a Demo Application

Let’s set up a simple table view application to use our new property list. If you are not familiar wth table view controllers I’d suggest reading this first. Go to the story board and delete the current view controller. Drag a table view controller to the storyboard. In the attribute inspector, click on Is Initial View Controller. Select the table view cell and change the Style to Basic and the Identifier to cell.

2016-02-11_07-26-20

Now go to the View Controller. Change the view controller to subclass UITableViewController instead of View Controller

class ViewController: UITableViewController {

Go back to the storyboard and select the table view controller. In the Identity inspector, set the Class to View Controller.

Go to your view controller code. Add the following properties with some test data:

var isColorInverted = true
var color1 = UIColor(
    red: 0.3,
    green: 0.3,
    blue: 0.0,
    alpha: 1.0)
let color2 = UIColor(
    white: 0.9,
    alpha: 1.0)
var pies = [
    "Pizza",
    "Chicken Pot",
     "Shepherd's"
]    

Add the delegates and data sources. At the bottom of the class add this:

 //MARK: - Table View Data sources and Delegates
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return pies.count
    }
    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "Pies"
    }
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
        let row = indexPath.row
        cell.textLabel?.text = pies[row]
        if isColorInverted { //invert colors by flag status
            cell.backgroundColor = color2
            cell.textLabel?.textColor = color1
        }else{
            cell.backgroundColor = color1
            cell.textLabel?.textColor = color2
        }
        return cell
        
    }


We’ll display the value of the array in the cells and change the color of the table view cell depending on the value of isColorInverted. Now build and run this to make sure the app works with the test data.

2016-02-11_07-29-52

Reading a .plist in Swift

Add the following above the viewDidLoad method.

func readPropertyList(){
    
}

We’re going to make this a function that we’ll call in our viewDidLoad to set the properties for the class according to the property list. Add the following property to the new function:

var format = NSPropertyListFormat.XMLFormat_v1_0 //format of the property list

The variable format is the format of the property list. While XML is the most common there are a few other types available all listed in the enumeration NSPropertyListFormat. This will be used in a parameter later on, but needs an initial value. The method we’ll run later may change format to a different format during the conversion process so it will be a pass through variable. Add under this property

var plistData:[String:AnyObject] = [:]  //our data

We’ll use plistData to store the dictionary entries of the property list. I’m making an assumption here you may not want to make. For almost all cases a property list will be a dictionary, so I’m setting this up for a dictionary. That might be a bad assumption and you can alternately write var plistData:AnyObject! = nil. or whatever format you are expecting from the property list. Now add this line

let plistPath:String? = NSBundle.mainBundle().pathForResource("data", ofType: "plist")!

This finds and returns the path of our list from the main bundle. I’m assuming it is in the main bundle here or there is only one data.plist in the bundle. Both could cause errors. For simplicity I made sure it was. You should add more robust code to find it where it may be hiding or if there are duplicates. Now add this

 let plistXML = NSFileManager.defaultManager().contentsAtPath(plistPath!)!

The constant plistXML is the XML data as type NSData format. The function may return nil if the file is not found. Now add this under it

do{
plistData = try NSPropertyListSerialization.propertyListWithData(plistXML,
    options: .MutableContainersAndLeaves, 
    format: &format) 
    as! [String:AnyObject]
}
catch{
    print("Error reading plist: \(error), format: \(format)")
}        

Here we convert the XML data to a dictionary. There is a class, NSPropertyListSerialization which along with the class method propertyListWithData does the conversions. It contains several parameters to discuss. The first parameter is the data we will convert with is of Type NSData, which we will discuss in more detail in the next lesson in this series. The second parameter is a constant from the enum NSPropertyListMutabilityOptions. This parameter has three possible options which affect the mutability of the dictionary created. It’s a feature for Objective-C, not Swift. In Swift it’s meaningless. The parameter format is a pass through variable, so needs the &format. If we did not use it in the print statement, we could also put

While I’ve been fast and loose with error checking for this demo, this is the one place I’m careful. If you edit your plist in XML on a text editor, you have the chance of introducing syntax errors which will mess up everything. For that reason, I use Swift’s error control do...try...catch clause to catch syntax errors in the XML.

If this step is successful, we have in plistData a dictionary of our property list. Add the following code to assign the Bool value in the do block

isColorInverted = plistData["Inverse Color"] as! Bool

The value of dictionary is of type AnyObject. For each value we need to downcast it to assign it properly. For isColorInverted, that is a Bool. For the XML tags real and integer, it can be any real or integer type. Add this code :

let red = plistData["Red"] as! CGFloat
let green = plistData["Green"] as! CGFloat
let blue = plistData["Blue"] as! CGFloat
color1 = UIColor(red: red, green: green, blue: blue, alpha: 1.0)

We did not have to downcast to Float and then convert to a CGFloat. We can Downcast directly to CGFloat. Add this to assign the array pie

pies = plistData["Pies"] as! [String]

The function is now complete and will assign to the properties of the class values from the the property list. Change viewDidLoad to use it.

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        readPropertyList()
    }

Build and run. You will get different results than your first run

2016-02-11_07-42-07

When to Use plists

I’m conservative with using plists. While you can write plists, I never do. I’m of the thought that property lists should be a read-only at run time. While there are several reasons for that, my biggest is there are much better ways to save data than plists. I use them for two things only. Instead of setting global constants, I’ll make a plist and load it into the classes that need it. A small data file is a lot easier to change than finding a constant in a mass of code. Secondly, I use it as I did here to load tables and pickers with static data. Instead of making a huge amount of code to set arrays, I make it an external file. These both are read-only operations. In the lessons the follow, we’ll look at some of the ways to both read and write data.

If you liked this lesson and would like to be notified about more lessons, subscribe to the Make App Pie NewsletterWhich sends a new iOS lesson every Thursday and news and sneak previews of upcoming works every Monday

The Whole Code

ViewController.swift

//
//  ViewController.swift
//  SwiftPropertyListDemo
//
//  Created by Steven Lipton on 2/10/16.
//  Copyright © 2016 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UITableViewController {
    var isColorInverted = true
    var color1 = UIColor(red: 0.3, green: 0.3, blue: 0.0, alpha: 1.0)
    let color2 = UIColor(white: 0.9, alpha: 1.0)
    var pies = ["Pizza","Chicken Pot","Shepherd's"]
    
    func readPropertyList(){
        
        var format = NSPropertyListFormat.XMLFormat_v1_0 //format of the property list
        var plistData:[String:AnyObject] = [:]  //our data
        let plistPath:String? = NSBundle.mainBundle().pathForResource("data", ofType: "plist")! //the path of the data
        let plistXML = NSFileManager.defaultManager().contentsAtPath(plistPath!)! //the data in XML format
        do{ //convert the data to a dictionary and handle errors.
            plistData = try NSPropertyListSerialization.propertyListWithData(plistXML,options: .MutableContainersAndLeaves,format: &format)as! [String:AnyObject]
            
            //assign the values in the dictionary to the properties
            isColorInverted = plistData["Inverse Color"] as! Bool
            
            let red = plistData["Red"] as! CGFloat
            let green = plistData["Green"] as! CGFloat
            let blue = plistData["Blue"] as! CGFloat
            color1 = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
            
            pies = plistData["Pies"] as! [String]
        }
        catch{ // error condition
            print("Error reading plist: \(error), format: \(format)")
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        readPropertyList()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    //MARK: - Table View Data sources and Delegates
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return pies.count
    }
    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "Pies"
    }
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
        let row = indexPath.row
        cell.textLabel?.text = pies[row]
        if isColorInverted { //invert colors by flag status
            cell.backgroundColor = color2
            cell.textLabel?.textColor = color1
        }else{
            cell.backgroundColor = color1
            cell.textLabel?.textColor = color2
        }
        return cell
        
    }

}


data.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Inverse Color</key>
	<false/>
	<key>Red</key>
	<real>0.3</real>
	<key>Green</key>
	<real>0</real>
	<key>Blue</key>
	<real>0.3</real>
	<key>Pies</key>
	<array>
		<string>Cherry</string>
		<string>Apple</string>
		<string>French Silk</string>
		<string>Coconut Cream</string>
		<string>Blueberry</string>
		<string>Boston Cream</string>
	</array>
</dict>
</plist>

Swift Swift: Using NSTimer to Make a Timer or Alarm

frame 0730 042315With the launch of the Apple Watch it’s time for timers in iOS and WatchKit. Learning to use timers is a very important skill for most developers. There are many places we need to schedule regular intervals of time to do things. In my WatchKit series I’m writing a workout interval timer which tells the user when to switch intervals. Timers can tell the system to do more than just alarms. If you are not using SpriteKit, Games and animations need the use of  timers to control the frames per second of animation. In this lesson, we’ll look at NSTimer, and what we can do with the timer. There are two ways most people use it: for a single event or for repeated events. We’ll discuss both.

Set up the Storyboard

Create a new Project named SwiftPizzaTimer in a single view template with Swift as the language. Drag three labels, three buttons and a switch to the storyboard. Arrange the storyboard like this.

Screenshot 2015-04-23 05.30.11

You can use Auto layout in the Width:any Height:any size class, or set you size class to Width:compact, height:any and just drag and drop your controls. To use auto layout, do the following:
Drag one label to the storyboard. Title it Pizza Timer. Give it a light gray background with black letters, center alignment and a font size of 46 point. Pin the label 0 points up 0 left and 0 right. Set the height to 64 points, and select to Update the frame of new Constraints.
Drag two buttons under the label and next to each other. On the button to the left, title it Start, make the font black with a size of 46 points and a green background. On the right title the label Stop with a size of 46 points, white lettering and a red background. Pin the Start label 15 points up, and 0 left. Do not update the frames. Pin the Stop label 0 left and 0 right. Select the Pizza Timer label and the Start button. In the pin menu select Equal Heights. Select the Start button and the Stop button. In the pin menu, select Equal widths and Equal Heights. In the align menu with both Start and Stop still selected, select Top edges, and make sure the value is 0. Also select the Update Frames Items of New Constraints.

Drag another button to the storyboard. Label it Reset, with a blue background, white lettering, and a font size of 46 points. Pin the Reset button 0 left,0,right, 20 down, but do not update constraints yet. Select both the Pizza Timer label and the Reset button and in the pin menu select Equal Heights and set Update Frames to Items of New Constraints.

Drag a switch to the storyboard. Set its state to off in the attributes inspector. In the align menu, Align the switch to Horizontal Center in Container. Pin the Switch 15 up and select for Update Frames Items of new Constraints.

Drag two more labels to the storyboard position one to the left of the switch labeled Up and one to the right Down. Give both a font color of White and a font of 36 points. Right justify the Up label. pin the Up Label 0 left and 15 right. Select the label and the switch, and in the align menu Align to centers, and Update Frames of new constraints. Select the down label. Pint the down label 15 left and 0 right. Select both the Down label and the switch. In the align menu Align to centers, and Update Frames of new constraints. Your finished layout should be this:

 Screenshot 2015-04-23 06.17.07

Open the assistant editor and control-drag the outlets and actions. Start with the Pizza Timer label. Control drag and make an outlet timerLabel.

@IBOutlet weak var timerLabel: UILabel!   //label on top of the view

From the switch make an outlet countingDown

@IBOutlet weak var countingDown: UISwitch! //for use in iteration 2

From the three buttons control drag to make three actions: startTimer, stopTimer and resetTimer respectively:

@IBAction func startTimer(sender: UIButton) {
}
@IBAction func stopTimer(sender: UIButton) {
}
@IBAction func resetTimer(sender: UIButton) {
}

Make an Alarm

The most useful class method for NSTimer is scheduledTimerWithTimeInterval. It has a form like this:

class func scheduledTimerWithTimeInterval(
     ti: NSTimeInterval, 
     target aTarget: AnyObject, 
     selector aSelector: Selector, 
     userInfo: AnyObject?,  
     repeats yesOrNo: Bool) -> NSTimer 

The first parameter gives a time in seconds that the timer will run for. At the end of that time, it will call a method set by the second two parameters. If there is information that needs passing to that method, we do so in the userInfo parameter, which stores the information in the timer.  Often this will be a dictionary of several values, though it can be any object. The last parameter tells us if we will repeat the timer or just end.

Let’s add a timer to our code, change startTimer to this:

    @IBAction func startTimer(sender: AnyObject) {
        timerLabel.text = "Timer Started"
        timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
            target: self,
            selector: "timerDidEnd:",
            userInfo: "Pizza Done!!",
            repeats: false)
    }

Our timer schedules a time of timeInterval, and when done runs a method timerDidEnd, then stops. This is the classic alarm clock method of doing something. You set an alarm and it goes off at some time with a given message, then dies. Xcode complains we have not defined  interval or timer. Just below your outlet declarations add this

var timer = NSTimer() //make a timer variable, but don't do anything yet
let timeInterval:NSTimeInterval = 10.0

Our next step is declaring a method timerDidEnd. Add this to the code.

//MARK: - Instance Methods
    func timerDidEnd(timer:NSTimer){
        //first iteration of timer
          timerLabel.text = timer.userInfo as? String
    }

When the timer fires, It calls this method. We tell the user that the timer completed  with the Pizza Ready!! message. Because we set repeat to false, the timer will dismiss itself after this call. If we need any information about the timer, including whatever we stored in userInfo, we have a parameter in our method to access it, which we have our completion message. Since userInfo is of type AnyObject, we will  need to downcast it correctly before use.

Add the following to the stopTimer method:

@IBAction func stopTimer(sender: AnyObject) {
        timerLabel.text = "Timer Stopped"
        timer.invalidate()
    }

In our stopTimer method, we use the NSTimer method invalidate to stop and destroy the timer.  Once stopped, the app gives the user feedback to tell them the timer stopped.

Build and run. When the app loads, tap the start button. nothing happens for ten seconds, when you get this:

Screenshot 2015-04-23 06.53.52

Using Repeating Timers

Timers only show that a time event happened. In the example above that was pretty boring. Let’s change the code for more user feedback and have the timer count up or down the ten seconds of our timer. To do this, we’ll use the most common way of using a timer with repeat set to true. The strategy here is having lots of short timing events. At each time event we do some updating and check if we are at our target time, when we will shut down our timer and do our exit activities like we did with our first example.

Change the variables to this:

var timer = NSTimer() //make a timer variable, but don't do anything yet
let timeInterval:NSTimeInterval = 0.05 //smaller interval
let timerEnd:NSTimeInterval = 10.0 //seconds to end the timer
var timeCount:NSTimeInterval = 0.0 // counter for the timer

The timer is the same, but we changed our interval to 0.05 seconds. every 0.05 seconds we will update the timerLabel with a new time. There is a balance here, which you might want to experiment with. If you make too big an interval, there will be inaccuracy with your visible timer. If you make too small an interval, you use a large amount of resources. If you use too many, you might have an internal inaccuracy to your timer as well and the app will be unresponsive. My general rule is to set my interval for a half of the last digit of accuracy you are displaying. I am going to 0.1 seconds accuracy on my label, so I set the interval to 0.05.

Since interval is no longer telling me when ten seconds is up we need another constant to tell us that,  assigned to timerEnd. I also assigned a variable timeCount. Every time timer fires, the app will either add or subtract the timeInterval from timeCount. When I reach zero on a countdown timer or 10 on a count up timer I stop. Change your timerDidEnd method to this:

func timerDidEnd(timer:NSTimer){
        if countingDown.on{
            //timer that counts down
            timeCount = timeCount - timeInterval
            if timeCount <= 0 {  //test for target time reached.
                timerLabel.text = "Pizza Ready!!"
                timer.invalidate()
            } else { //update the time on the clock if not reached
                timerLabel.text = timeString(timeCount)
            }
        } else {
            //timer that counts up
            timeCount = timeCount + timeInterval
            if timeCount >= timerEnd{  //test for target time reached.
                timerLabel.text = "Pizza Ready!!"
                timer.invalidate()
            } else { //update the time on the clock if not reached
                timerLabel.text = timeString(timeCount)
            }

        }

We use the switch countingDown to decide if we count up or down. If we count up, we add the time interval. If counting down, we subtract the time interval. We look for our ending condition. If true, we exit as we defined it in the last example. If not at the ending condition, we update the time display.

The time is in seconds, We need to format it to a form usable by most humans, which we do in the function timeString. Add this code:

  func timeString(time:NSTimeInterval) -> String {
    let minutes = Int(time) / 60
    let seconds = time - Double(minutes) * 60
    let secondsFraction = seconds - Double(Int(seconds))
    return String(format:"%02i:%02i.%01i",minutes,Int(seconds),Int(secondsFraction * 10.0))
}

We use the truncation power of integers to get minutes and seconds we can use in our return string of mm:ss.s. minutes takes a timeinterval of seconds and makes it an integer then divides by 60 seconds. seconds subtracts out that many seconds. We get a digit for the tenth of a second by subtracting the number of seconds from the integer number of seconds.

We now have a display when the timer fires. We have not yet implemented a reset for the timer. Add this to your code:

    //MARK: - Instance Methods
    func resetTimeCount(){
        if countingDown.on{
            timeCount = timerEnd
        } else {
            timeCount = 0.0
        }
    }

Depending on the switch, we have a different starting point. Counting down, we count down from timerEnd to 0. We do the reverse for counting up, starting at 0. Now that we have a reset function, we can define the action for the reset button.

@IBAction func resetTimer(sender: AnyObject) {
        timer.invalidate()
        resetTimeCount()
        timerLabel.text = timeString(timeCount)
    }

Whenever we reset, we will stop the timer first. For a reset we clear the count on the timer, the update timerLabel with the reset time. Our final change is to startTimer. Change the code to this:

     @IBAction func startTimer(sender: AnyObject) {
        if !timer.valid{ //prevent more than one timer on the thread
            timerLabel.text = timeString(timeCount) //change to show clock instead of message
            timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
                target: self,
                selector: "timerDidEnd:",
                userInfo: nil,
                repeats: true) //repeating timer in the second iteration
        }
    }
 

We changed the timer to be a repeating timer by changing repeats to false. We can build and run.

timerDemo

We have a working timer. You can change the countdown or count up by tapping the switch.

One more bug — Using Flags in Timers

It’s important for debugging to check all possibilities of use. If we reset the timer, then change our count direction, the timer fails. The timer believes it is already done. When we reset, we clear the counter. When we change the switch, we change the terminating value to be the same as the start value and the timer thinks it’s finished. We’ll need to change our start value when the switch changes and the timer is reset.

Go to the storyboard and open the assistant editor.  Control drag from the switch to the code. Make a new action countingDown. Add the following code to the action:


    @IBAction func countingDown(sender: UISwitch) {
        if !isTiming {
            resetTimeCount()
            timerLabel.text = timeString(timeCount)
        }
    }

We use a flag isTiming to decide if the timer is in the middle of a count. If it is not, which will happen after the timer is done or a reset and when we change the switch we reset the count. Otherwise we leave the count alone. Flags in timer are common. they are good ways of indicating the state of the timer while it is running repetitively, much in the same way valid works for a single timer. We still need a little setup for this to work. Add the variable to our properties at the top of the class:

var isTiming = false

In starTimer, add isTiming = true to the if clause code block and isTiming = false to resetTimer, and just under the two timer.invalidate() in timerDidEnd
Build and run. Now the timer works right.

The Whole Code

Iteration 1 of timer

//
//  ViewController.swift
//  SwiftPizzaTimer
//
//  Created by Steven Lipton on 4/22/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//
//  Iteration 1 of the timer - a single time
//

import UIKit

class ViewController: UIViewController {
    //iteration 1 of the timer

    //MARK: - Outlets and properties

    @IBOutlet weak var timerLabel: UILabel!
    @IBOutlet weak var countingDown: UISwitch! //for use in iteration 2

    var timer = NSTimer() //make a timer variable, but do do anything yet
    let timeInterval:NSTimeInterval = 10.0

    //MARK: - Actions
    @IBAction func startTimer(sender: UIButton) {
        timerLabel.text = "Timer Started"
        timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
            target: self,
            selector: "timerDidEnd:",
            userInfo: "Pizza Done!!",
            repeats: false)
    }
    @IBAction func stopTimer(sender: UIButton) {
        timerLabel.text = "Timer Stopped"
        timer.invalidate()
    }
    @IBAction func resetTimer(sender: UIButton) {
    }

    //MARK: - Instance Methods
    func timerDidEnd(timer:NSTimer){
        //first iteration of timer
        timerLabel.text = "Pizza Ready!!"
    }
   }

Iteration 2 count up and down timer

//
//  ViewController.swift
//  SwiftPizzaTimer
//
//  Created by Steven Lipton on 4/22/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//
//  Iteration 1 of the timer - a single time
//

import UIKit

class ViewController: UIViewController {
    //iteration 2 of the timer

    //MARK: - Outlets and properties

    @IBOutlet weak var timerLabel: UILabel!

    @IBOutlet weak var countingDown: UISwitch!

    var timer = NSTimer() //make a timer variable, but do do anything yet
    let timeInterval:NSTimeInterval = 0.05
    let timerEnd:NSTimeInterval = 10.0
    var timeCount:NSTimeInterval = 0.0
    //MARK: - Actions

    @IBAction func startTimer(sender: UIButton) {

        if !timer.valid{ //prevent more than one timer on the thread
            timerLabel.text = timeString(timeCount)
            timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
                target: self,
                selector: "timerDidEnd:",
                userInfo: "Pizza Done!!",
                repeats: true) //repeating timer in the second iteration
        }
    }

    @IBAction func countingDown(sender: UISwitch) {
        if !timer.valid{ //if we stopped an
            resetTimeCount()
        }
    }
    @IBAction func stopTimer(sender: UIButton) {
        //timerLabel.text = "Timer Stopped"
        timer.invalidate()
    }

    @IBAction func resetTimer(sender: UIButton) {
        timer.invalidate()
        resetTimeCount()
        timerLabel.text = timeString(timeCount)
    }

    //MARK: - Instance Methods
    func resetTimeCount(){
        if countingDown.on{
            timeCount = timerEnd
        } else {
            timeCount = 0.0
        }
    }

    func timeString(time:NSTimeInterval) -> String {
    let minutes = Int(time) / 60
    //let seconds = Int(time) % 60
    let seconds = time - Double(minutes) * 60
    let secondsFraction = seconds - Double(Int(seconds))
    return String(format:"%02i:%02i.%01i",minutes,Int(seconds),Int(secondsFraction * 10.0))
}

    func timerDidEnd(timer:NSTimer){
        //timerLabel.text = timer.userInfo as? String

        if countingDown.on{
            //timer that counts down
            timeCount = timeCount - timeInterval
            if timeCount <= 0 {  //test for target time reached.
                timerLabel.text = "Pizza Ready!!"
                timer.invalidate()
            } else { //update the time on the clock if not reached
                timerLabel.text = timeString(timeCount)
            }

        } else {
            //timer that counts up
            timeCount = timeCount + timeInterval
            if timeCount >= timerEnd{  //test for target time reached.
                timerLabel.text = "Pizza Ready!!"
                timer.invalidate()
            } else { //update the time on the clock if not reached
                timerLabel.text = timeString(timeCount)
            }

        }

    }

   }

Swift Swift: Using The Navigation Bar Title and Back button.

[Updated for Xcode 7.2/Swift 2.1 2/5/16]

Note: Click here for the  Swift 3.0/Xcode 8 version of this tutorial 

In writing the Swift Swift View Controllers book, it came to my attention many people don’t understand the functionality of the navigation toolbar’s title and Back button. In an early part of writing, I planned to skip the topic as a minor detail  so I could get the book done and published. However, the built-in features of the navigation toolbar make it a powerful and useful feature that can’t be missed.

Setting Up the Storyboard

Open up a new single view project named NavBarDemo using Swift as the language and Universal device. Go to the storyboard and click the view controller icon in the scene. In the drop down menu, select Editor>Embed in>Navigation controller. Drag two buttons, one labeled Pizza and the other labeled Pasta out to the scene. Make the Pizza button white text on a red background. Make the Pasta button white text on a blue background. Arrange them like this on the storyboard:

Screenshot 2015-03-21 09.22.36

Select the Pizza button. Using auto layout, pin the button 0 points up, 0 left, 0 right, and 0 down like this:

Screenshot 2015-03-21 09.27.51

Select the Pasta button and again pin the button button 0 up,0 left, 0 right, and 0 down. Now Control-drag from the Pizza button to the Pasta button. Select Equal Widths in the menu that appears.

Screenshot 2015-03-21 09.29.26

On the resolver menu select Update Frame in the All Frames in View section. Your controller should look like this:

Screenshot 2015-03-21 09.32.34

Drag two more view controllers on to the storyboard. Make the background of one blue and the background of the other red.
Control drag from the Pizza button to the red scene. Select a show segue. In the attributes inspector, set the segue identifier to pizza. Control-drag from the Pasta button to the blue scene. Select a show segue. In the properties inspector, set the segue identifier to pasta.

In this lesson we will do all the coding in ViewController, so there is no need of code in the two new controllers.

Setting the Navigation Title Bar

There is a property on UIViewController called navigationItem. When used with navigation controllers, this controls the navigation bar at the top of the view. The navigationItem property is an instance of UINavigationItem, which contains four major properties: a title, a left bar button, a right bar button and a prompt. To set the title on the toolbar , you set the string for the title property. For example add this to the ViewController class

override func viewWillAppear(animated: Bool) {
        navigationItem.title = "One"
    }

Build and run . The first view now has One as a title.

Screenshot 2015-03-21 09.43.19

We used viewWillAppear and not viewDidLoad. ViewController is the root view controller in the navigation stack. It only loads once and stays active in the background. It will execute viewDidload only once. To make sure we update, we use viewWillAppear instead.
The title can be dynamic. As a simple example, We’ll place a count in the title of the navigation bar. Add the following code:

var vcCount:Int = 0{
    didSet{
      navigationItem.title = "Count: \(vcCount)"
    }
  }

We used the  didSet property observer feature of Swift. Any time vcCount changes, the title changes with it. We change the count on any segue so we can increment in prepareForSegue. Add this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        ++vcCount
    }

It’s rare to not have a statement within an if clause in a prepareForSegue. We want any segue to increment the count, so we don’t need the if.

We want to show the count when we load. Change the viewWillAppear to this:

 override func viewWillAppear(animated: Bool) {
        // navigationItem.title = "One"
        navigationItem.title = "Count: \(vcCount)"
    }

Build and run. Go back and forth in the views. The title changes every time you come back to it.

Screenshot 2015-03-21 10.55.57 Screenshot 2015-03-21 10.56.01

Programming the Back Button

You’ll notice once you have a title, the navigation Back button disappears, to be replaced by the previous view’s title.

The Back button reads the controller underneath the current controller for its title. You cannot set the back button title directly. Instead, you set the title of the current controller before you leave for the destination controller. If the previous view controller’s title is nil, the button titles itself Back. Change the prepareForSegue to this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        ++vcCount
        navigationItem.title = nil
    }

Build and run. You have a Back button with the count:

Screenshot 2015-03-21 10.53.15  Screenshot 2015-03-21 10.53.27

If you wanted to add your own text to the Back button, you have to change the title of the controller below it on the navigation stack. Change prepareForSegue to this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        ++vcCount
        navigationItem.title = nil

        if segue.identifier == "pizza"{
            navigationItem.title = "Pizza to One"
        }
        if segue.identifier == "pasta"{
            navigationItem.title = "Pasta to One"
        }
    }

Before we segue to the pizza and pasta controller, we change the title of the current controller’s navigationItem. The Back button reads the title of the and sets the button’s title accordingly. The viewWillAppear method will reset the title to the count in the first view controller when we pop the Pizza or Pasta view controller. Build and Run. Select to the Pizza and Pasta buttons:

Screenshot 2015-03-21 11.00.26 Screenshot 2015-03-21 11.00.32

To see this happening, you can comment out this in viewWillAppear.

//navigationItem.title = "Count: \(vcCount)"

Build and Run. When you go back, the title remains:

Screenshot 2015-03-21 11.02.02 Screenshot 2015-03-21 11.02.08

Uncomment the line before you go on.

The Size-sensitive Back Button.

The Back button is sensitive to the space around it. The button’s title responds to not having enough space to place itself. Change the prepareForSegue above to this:

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        ++vcCount
        navigationItem.title = nil
        if segue.identifier == "pizza"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Pizza"
            navigationItem.title = "Pizza to One"
        }
        if segue.identifier == "pasta"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Linguine all’arrabbiata"
            navigationItem.title = "Pasta to One"
        }
    }

Lines 5 and 10 get the destination view controller and assign it to vc. We dynamically send the title by vc.navigationItem.title in lines 6 and 11. We’ve picked some long labels to test size restrictions. Set the simulator to iPad 2. Build and run. Select the Pizza button.

 Screenshot 2015-03-21 11.09.25

Go back, change the switch and select the pasta button.

Screenshot 2015-03-21 11.09.19

In both cases, we get the title we wanted in the back button. Stop the simulator and change to an iPhone 6. Run again, change the switch for Pasta and you get something different.

Screenshot 2015-03-21 15.25.37

We lose our custom title and we have a back button again. Now try an iPhone 4s in the simulator. Run the demo, switch to the Pasta controller and you get this:

Screenshot 2015-03-21 15.26.52

There is only the <  icon and no text for the back button. Rotate the device by pressing Command-Left Arrow:

Screenshot 2015-03-21 15.26.57

The text re-appears with more space to place the text.

The back button is intelligent. If it has enough space, it displays the title of the previous controller. If there is not enough space, it displays Back. If there is not enough space for the word Back, it displays only the  < icon.
There is an important design rule in play here that I am intentionally breaking to make the point: Keep your titles short in a navigation bar. If you keep titles short, you will have the full functionality of the back button.

The Whole Code

Downloading Code

I’m trying an new way of sharing sourcecode. The will be a .zip file for download at the the end of the lesson. It will contain sourcecode and assets only.  You will have to build your own application.

Here is the source code for the lesson: NavBarDemo_Source.

Download it and unzip it. You will find two files. ViewController.Swift and Main.Storyboard. In Xcode, Open up a new single view project named NavBarDemo using Swift as the language and Universal device. Delete the ViewController.Swift and Main.Storyboard and send them to the trash can. Drag the files you unzipped to the project’s navigator. In the dialog box that appears, make sure  that copy files is checked on, and finish the dialog box.  You now have a working copy.

ViewController.swift

//
//  ViewController.swift
//  NavBarDemo
//
//  Created by Steven Lipton on 3/21/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    var vcCount:Int = 0{
        didSet{
            navigationItem.title = "Count: \(vcCount)"
        }
    }

    override func viewWillAppear(animated: Bool) {
        // navigationItem.title = "One"
        navigationItem.title = "Count: \(vcCount)"
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        ++vcCount
        navigationItem.title = nil

        if segue.identifier == "pizza"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Pizza"
            navigationItem.title = "Pizza to One"
        }
        if segue.identifier == "pasta"{
            let vc = segue.destinationViewController as UIViewController
            vc.navigationItem.title = "View Controller Linguine all’arrabbiata"
            navigationItem.title = "Pasta to One"
        }
    }
}

The Joys of Beta Swift: as Versus as! and Why I Avoid Betas

It came to my attention this week there’s a big change to Swift code on the way. It doesn’t look big though: it is the difference between as and as!.

The keyword as is the conversion operator. Usually type or class are easily defined like this:

var myColor = UIColor()  //class UIColor
var myNumber:Integer = 0 //type Integer

But there are times you get things like this in table views and collection views:

 let cell = tableView.dequeueReusableCellWithIdentifier(&quot;cell&quot;, forIndexPath: indexPath) 

The method of dequeueReusableCellWithIdentifier returns AnyObject, which is so generic it is worthless. We need to downcast it to UITableViewCell to use it.

let cell = tableView.dequeueReusableCellWithIdentifier(&quot;cell&quot;, forIndexPath: indexPath) as UITableViewCell

If the class does not exist or the class is incompatible with the cast, you get an error. For documentation purposes, Apple changed as to as! for these cases so anyone reading your code knows an error could occur. In Swift 1.2 Beta this should be

let cell = tableView.dequeueReusableCellWithIdentifier(&quot;cell&quot;, forIndexPath: indexPath) as! UITableViewCell

This does not work before 1.2 though. Don’t try it unless you have a Xcode 6.3 beta.

Speaking of betas, I want to be clear about my policy about betas:

Unless there is no production version, I always write to the production version. I suggest unless you seriously know what you are doing and don’t care to actually get something in the App Store quickly to avoid beta versions. There are four reasons for this:

  1. Apple does not accept code from beta versions. For most cases where there is a production version or GM seed, you write an app in Beta you have to wait till they release it as a production version before they even accept your code. If Apple holds up release of XCode due to hardware issues with the Apple Watch for example your iPhone app will be stuck for months without a production release and months of lost sales.
  2. Betas change often. What you write and what will be the production version is different. That might mean several versions of coding — and a lot of wasted effort and time.
  3. Betas are poorly documented. Trying to understand release notes which often are the only place that explains a change is not easy.
  4. You don’t talk about betas. Apple has been very loose about their usual non-disclosure on Swift betas( I’m hoping that the case above), but that said, if they change their mind at any time, they can yank your license for disclosing features of a beta version. Many betas come with a Non-disclosure agreement. Break it and you could be punished.

So I make it a policy not to use betas unless I have to. I did between June 2014 and October 2014 because there was nothing else for Swift. And I had to change this entire website several times because of that. I could delay my book launch for months due to this change, or use the production version and get it out in March. Those working on Apple watch projects as of this writing have to use a beta. They have no choice. If you have a choice of beta and a production version, Pick the production.