What Do you do with Any?

There’s been a major change to Swift 3.0 That many people might find a little disturbing in code, and others may have no idea what it is. In one sense that’s the idea. Apple changed AnyObject to Any in Swift 3.0. This three-letter word is an extremely powerful feature,a backbone of many functions and classes, and the core of watchOS’s way of transmitting data from one controller to another.

The class Any is the object that is anything. It is generic. You learned early in your coding lessons that everything has a type and everything gets assigned a type like this:

let radius = 5.0   // a Double
var diameter:Int = 10  //an Int
var pizzaTopping = "Pepperoni"  //a string

Functions need a type in their parameters too. For example, there is this function

func pizzaArea(radius:Double)-> Double {
let area = radius * radius * M_PI
return area
}

Since everything needs a type to work right, a function assigning something like this might seem a little odd.

func pizzaSize(context:Any) -> Double {
    var size = 0.0
    let radius = context as! Double
    size = radius * radius * M_PI
    return size
}

If you tried

print(pizzaSize(context: 5.0))

You would get a result around 78.54.

Any really isnt totally generic. It’s a place holder because there are times when writing code, you have to be vague in how you add a parameter. Look closely at pizzaSize(context:Any). You’ll see this is identical to pizzaArea(radius:Double) but I set the type in my code instead of in the parameter. To set the type, I downcast the type Any to Double using as!. Another example

let myAnyType:Any = 12.0
let myDoubleType:Double = myAnyType as! Double
print(myDoubleType)

Why would you want to delay typing? Because you, or some other developer such as Apple wrote some code and has no idea how you are going to use that code. Consider the following class

class PizzaGeometry{
init(context:Any){
self.awake(context: context)
}
func awake(context:Any){}
}

This code by itself does nothing but call an empty function awake(context:) and have an empty function pizzaArea. But we could subclass this to do areas of pizzas for both integers and doubles by awake(context:).

class PizzaAreaInt:PizzaGeometry{
    var radius:Int = 0
    override func awake(context: Any) {
        radius = context as! Int
    }
    func pizzaArea() {
        print (radius * radius * 3)
    }
}

class PizzaAreaDouble:PizzaGeometry{
    var radius:Double = 0.0
    override func awake(context:Any){
        radius = context as! Double
    }
    func pizzaArea(){
        print(M_PI * radius * radius )
    }
}

I couldn’t have done that had I set the type in PizzaGeometery. Most of you are probably iOS developers and haven’t touched watchOS yet. However in Apple’s obsession about keeping things simple in watchOS, PizzaGeometery is the skeletal definition of a view controller , named WKInterfaceController class in watchOS. watchOS passes data between view controllers directly without, like iOS,  referencing the destination in prepare(forSegue:) or initializing the destination class and setting properties. It sends those values as a context, and lets the awake(withContext context:any) deal with it. Developers unpack the context for the controller they are currently writing by overriding awake. However Any is not just a watchOS thing. iOS uses it too, and one form of this shows up all over the place: Dictionaries

Area for circles is a single property. What if we wanted to make a class for more than one property in PizzaGeometery?  That’s where dictionaries come in. In Swift, you can define a dictionary of type [String:Any].  What does that do? It means you can have an identifier name as a key and place its value — no matter the type — as a the value. for example:

let rectanglePizzaDictionary:[String:Any] = 
    ["width":10,"length":12,"height":1.2]

I can unpack a dictionary like that in my Subclass

class PizzaRectangle:PizzaGeometry{
    var height:Double = 0.0
    var width:Int = 0
    var length:Int = 0
    override func awake(context: Any) {
        let contextDictionary = context as! [String:Any]
        height = contextDictionary["height"] as! Double
        width = contextDictionary["width"] as! Int
        length = contextDictionary["length"] as! Int
    }
    func pizzaArea(){
        print(width * length)
    }
    func pizzaVolume(){
        print (width * length * Int(height))
    }
}

I unpacked twice. First I unpacked to the type [String:Any]. Then I unpacked the entries in the dictionary.  Here’s the one issue you’ll need to be careful with here. For simplicity I didn’t error check, but if any of these values are not what I expect for the dictionary the app will crash. You need error checking here and maybe even more importantly, good documentation of the possible dictionary entries. As iOS developers this is going to be a big problem since a lot of methods in UIKit use this dictionary format to send information back to you as a developer. Apple used to be worse at this, but their current documentation system seems to have solved a lot of this. For example one of the excruciating examples of this is the UIImagePickerControllerDelegate method  imagePickerController(_:didFinishPickingMediaWithInfo:). There’s now a link in the documentation, but for a while it was a very hard search to find the keys and value in the info dictionary.

Any is very helpful. Apple uses it often once you start looking for it. Any means “I don’t know what supposed to go in here, could you set it up for me” when it comes to values. Dictionaries of [String:Any] exist all over the API, often in delegates and closures. watchOS makes it the way to pass values between interface controllers. What at first glance looks like an empty generic container is really a powerful way of deferring type until necessary, making for a more flexible programming environment.

Why do we need Delegates in iOS and WatchOS?

About two years ago someone asked me a very good question: Why do we need delegates for UIViewControllers?  He thought Swift  made things easier, but this delegate stuff seems very complicated. Shouldn’t we be able to send a message or initializer between classes?

When I first learned iOS, I’ll admit it took me months to understand what happened with delegation. I found lots of confusing code and little explanation. AS I was researching some more preferences to send people to, I found the results lacking. Most often, tutorials refer to how to use an Apple factory delegate, not making your own callback. Its these callbacks which require full knowledge of delegates.

I decided it was time to update this, and to include two examples developers might run into: the iOS and watchOS versions. With the maturing of watchOS in watchOS 3 I think more developers might begin to look at developing watch apps, and there’s some twists there that might cause some confusion.

Let’s start at the beginning, so everyone understands the problem.

What is a Class?

Let’s start at the beginning, so everyone understands the problem. While we use classes in object-oriented programming, it’s good to review what they actually are. A class is a collection of data, which we call properties, and actions we can do to those properties, which we call methods.

Properties and methods are either public or private. A public method is one that classes other than the defining class can see and can use. Private means that the property or method is only usable and visible within the defining class. Other classes cannot see or use it. In Swift the private keyword makes properties and methods private. Swift’s calculated properties feature is another way to make properties private. In Swift, there is also a default state which makes a method or class public to the current target, but not other targets.

We tend not to like other people messing with our insides, and that is a good programming practice too. In general, it is best to leave what is public by other classes to a necessary minimum. Keeping properties and methods private and not exposing all of our class is known as encapsulation.

Encapsulation allows us to make code as modular as building blocks. Just as a few stubs come out of an otherwise standard sized brick, only a few usable methods come out of a class. Then they can attach to a lot of other bricks.

What is  Model –  View – Controller  or MVC?

MVC schematic blankA term heard often  when working with Xcode, is MVC. MVC stands for Model-View-Controller. It is not an exclusive term to Xcode projects. It is a pattern of programming, a good organization of any program or application in a graphics-rich environment, and arguably any environment that interacts with the user. MVC separates the major parts of an application. First it separates the data and the user interaction then adds an intermediary between them. Why is this important? You might write and publish an application for an iPhone, then decide an iPad version would be a good idea, then decide to make a watch version. With MVC, you only change one part completely, the view and possibly some of the controller. The code handling your data never changes between the versions saving a lot of time and effort.

What is a Model?

MVC schematic modelThere are parts of our program that deal with information we want to process. A pizza ordering system has a list of data giving us information about each person’s order. There may be other links to that data with more data about each customer, and about each pizza. In a pizza ordering system this is our model: the collection of all the data we will use in our ordering system. It does not in any way interact with the user. It does not display anything or does it ask for input. It is just data. Here is an example of very simple model:

class switchState{
    var state:Bool
    func textState()->String{
        if state {
            return "On"
        } 
        return "Off"
    }
    func overLoadSwitch(users:Int,load:Int){
      let totalLoad = users * load 
      if totalLoad > 100{
          switch = false
      }
}

This model is the state of a switch.  That’s the data. the model has two methods. textState(), describes the state of the switch as a string,  overLoadSwitch() turns off the switch if users multiplied by load is greater than 100 . There is a lot more methods I should add to describe the switch, but any method is changing or describing data only. There is no user input or output here. Models might make calculations but again there is no user interaction here.

What is a View?

MVC schematic viewWhere all the user interaction happens is in the view. In Xcode, most people use Interface Builder either as a scene in a storyboard or a .xib file to build their views.  A developer can programmatically create a view class to hold the different controls.

2016-08-01_07-11-48As the model never interacts with the user,  the view never interacts directly with the data. The view doesn’t do much but sit there. It might respond to a user touch with feedback such as a notifying a method somewhere, a color change when a button gets tapped or a scrolling motion at times, but that is all it does. The view does contain a lot of properties and methods and that tell us the state of the view. We can change the appearance and behavior of the view through methods and properties. The view can tell the controller that there was a change in the view, such as a button getting pressed, or a character typed. it can’t do anything about it, but it can broadcast something.

What is a Controller?

MVC schematicThe heart of MVC connects these two. Called the controller or view controller, it coordinates what happens in the model and what happens in the view. If a user presses a button on the view, the controller responds to that event. If that response means sending messages to the model, the view controller does that. If the response requires getting information from the model, the controller does that too. in Xcode, @IBOutlet and @IBAction connect Interface Builder files containing views to the view controller.

The key to MVC is communication. To be more accurate, the lack of communication. MVC takes encapsulation very seriously.  The view and the model never directly talk to each other. The controller can send messages to the view and the controller. The view and controller may do an internal action to the message sent as a method call or it may return a value to the controller. The controller never directly changes anything in either the view or the model.

So to summarize, a view, a model and a controller cannot directly change a property in each other. A view and a model may not talk to each other at all. A view can tell the controller there is a change in the model. A controller can send messages in the form of method calls to the view and the model and get the responses back through that method.

MVC schematic full annotated

How Does MVC Connect to Other MVC’s

What we’ve discussed so far is for only one scene in a much larger application. Suppose I have the following  watchOS storyboard:

2016-09-21_05-01-58

I have an a button Switch which loads a second face that has a switch.  When I decide which way I want the switch, I press done.

The Easy Direction

In Xcode we have what are known as segues. Segues are a convenience to point from one view controller to another.  When segues keep track of some things that would become cumbersome to control ourselves in a simple way. When we move from one controller to the next, the segue tells the system to open this particular view controller, which then opens up a view and model. The model and view in the new MVC setup is different then the calling one. Apple includes a method prepare(for segue:) for  iOS which give us a chance to set values in the new view controller,  and subsequently the new view controller’s view and model.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "switch"{
         let vc = segue.destination as! SwitchViewController
         vc.switchState = false
        }

With the introduction of WatchOS came a slightly different approach.  instead of having the full model available,  watchOS sends to new controllers a single value called context. As this is type Any?,  you can put whatever you want in this value. Most often developers send dictionaries of values to other controllers through the contextForSegue method. When the destination application wakes up. the awake method  converts the context to the proper type and assigns it correctly.

override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? {
        return self
    }

For both a identical  phone or watch app I could press the Switch button.  It launches  the switch face and  pass a value to the switch to be false.

The Problem Direction

We can turn the switch off and on easily enough.  But when we press Done to send it back to the original controller  is when problems show up. By the rules of MVC, we need a method to return a value. Where in a called instance can we go back to the class calling it? With an encapsulated class we can’t. There is no way to send that revised model back to the original controller without breaking encapsulation or MVC. The new view controller does not know anything about the class that called it. We look stuck. If we try to make a reference directly to the calling controller, we may cause a reference loop that will kill our memory. Simply put, we can’t send things backwards.

This is the problem that delegates and protocols solve by being a little sneaky. Imagine another class, one that is really a skeleton of a class. This class contains only methods. It declares that certain methods are in this class, but never implements them. In Swift they are protocols. We make a protocol class that has one method. That method is what you do when you are done  with the switch, and want to go back to the calling controller. It has a few parameters, things you want to pass back to the calling view controller.  So it might look like this:

protocol SwitchDelegate {
    func didFinishSwitch(switchState:Bool)
}

I passed back the state of the switch in this case.

In the controller with the switch, we make an instance of this protocol, calling it delegate.

delegate:SwitchDelegate! = nil

Since we have a property of type SwitchDelegate, we can use the methods of the SwitchDelegate type, In our example, that is our method didFinishSwitch. We can stick that method call in a action for a Done button:

@IBAction func doneButtonPressed(sender:UIButton!){
    delegate.didFinishSwitch(switchState: switchState)
    dismiss(animated: true, completion: nil)
}

or for WatchOS

@IBAction func submitSwitchStatus() {
    delegate.didFinishSwitch(switchState: switchState)
    pop()      
}

Since protocols are skeletons, it means any other class can adopt them. A class makes the protocol methods part of its own class with a stipulation. As soon as a protocol gets adopted, you need to flesh out the skeleton. The developer has to code the required methods in the adopted class’ code. We adopt a protocol by placing it after the name of the class and superclass. For iOS you might have

class OrderPizzaViewController:UIViewController,PizzaEditDelegate

and for watchOS, you might have

class InterfaceController: WKInterfaceController,SwitchDelegate {

As soon as you do that, you will get a compiler error since the protocol’s method does not exist in the class. In the code for the adopting class, in our example OrderPizzaViewController, we would implement the method

func didFinishSwitch(switchState: Bool) {
        if switchState {
            textState = "Switch is On"
        } else {
            textState = "Switch is Off"
        }        
    }

We get the data back, and what we need with it, in this case a string that we’ll print to the label.
One more step. While back in the destination controller, I said the delegate was an instance of the protocol, I didn’t say where the delegate was. In prepare(for Segue) I add one more line vc.delegate = self saying the protocol is your controller

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "switch"{
         let vc = segue.destination as! SwitchViewController
         vc.switchState = false
         vc.delegate = self
        }

In WatchOS this gets a bit trickier. I have only one context to pass both the switchState and the delegate. For multiple values generally developers use a dictionary like this:

override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? {
        let context:[String:Any] = ["switchState":false,"delegate":self]
        return context
    }

the awake method would have code to unwrap the dictionary and assign the values.

When we tap Done in a watch or phone application, the method runs, it knows it is located in the calling controller, and calls it there where we added to the original class.The data is a parameter so the program can easily transfer into the controller and to the model. Delegates and protocols are bit sneaky but it works, and is one of the most important techniques when working with view controllers.

mvc-schematic

The original question asked why didn’t Swift make this simpler. As I hope I’ve shown here with a Swift context, it doesn’t matter what object oriented language you use. Delegates are part of the MVC pattern, which is a good programming practice.

Understand and Use Closures in Swift

Have you ever seen some factory method in UIKit and see this strange parameter called completion or completionHandler? Usually you put nil there and leave it alone. These strange things like (Double,Double) -> Bool things are closures. Closures are functions you use as types, often in parameters of other functions. In this lesson we’ll explore closures and get under the hood to explain what they are and what they are really doing.

Set Up a Playground

Set up a playground in Xcode, the iPad or on the web at the IBM Swift Sandbox. IF using the IBM Swift Sandbox, make sure you have Foundation imported.

import Foundation

The Basics of Closures

Suppose I want to calculate the volume of a deep dish pizza. Add these functions to the playground:

 //area of a round pizza
    func roundArea(radius:Double) -> Double{
        return M_PI * radius * radius
    }
    //volume of a round pizza
    func roundVolume(height:Double, radius:Double ) -> Double{
        return roundArea(radius: radius) * height
    }

Add a variable to compute the volume of a pizza of radius 6 and height 2, then print it

var volume = roundVolume(height:2,radius:6)
print( String(format:"Round volume %4.2f",volume))

Run this and get the volume from the area on the console. Which is fine if I had only round pizzas. What if I had rectangle, oval or triangle pizzas? I’d have to write two new functions each: one for the area and one for the volume. However in each case I’m only changing the area calculation. What if I could pass just the calculation to the volume function? That’s what a closure is: a way of writing something like a function but assigning it as a type. Add the function to the class.

func volume(
    height:Double, 
    dim1:Double,
    dim2:Double, 
    area:(Double,Double) -> Double) -> Double
    {
        return area(dim1,dim2) * height
    }

Look at the parameter area. I’ve declared it (Double,Double) -> Double. That looks a lot like a function that returns Double, and two Double parameters. That’s what a closure does as a type. A closure gives a skeletal structure like a function you can pass as a parameter. Below the volume function add the following line of code.

let rectangleArea = { (length:Double,width:Double) -> Double in
    return length * width
}

When I declare the contents of the identifier, I flesh out something like a function inside of curly braces. Instead of func before the parameters, I put in after the return type. There are parameters (length:Double,width:Double). I have the return type as a Double. For closures, there must always be a return value. If you don’t have one mark it Void or ().

Since I declared the type in the closure declaration, I often can remove it from the declaration

        
let  rightTriangleArea = {(length,width) -> Double in
    return length * width / 2.0
}  
let ovalArea = {(length,width)->Double in
    return length/2.0 * width/2.0 * M_PI
}

It doesn’t work in all cases though. This give an error if you do not specify the type

let roundSliceArea = { (radius:Double, slices:Double) ->Double in
    return M_PI * radius * radius / slices
}

I can use the same volume function and change how I compute the area.

print (volume(height: 2,dim1: 10, dim2: 12, area:rectangleArea))
print (volume(height: 2,dim1: 10,dim2: 12, area:roundSliceArea))
print (volume(height: 2,dim1: 10, dim2: 12, area:rightTriangleArea))
print (volume(height: 2,dim1: 10, dim2: 12, area:ovalArea))

I do have a problem with a circle. I could define it like this in my storyboard:

 let circleArea = {(radius)->Double in
            return radius * radius * M_PI
 }
print (self.volume(height: 2,dim1: 10, dim2: 12, area:circleArea))        

This doesn’t work for the circle, because the number of parameters are different. You’d get this error message
Cannot convert value of type '(Double) -> Double' to expected argument type '(Double, Double) -> Double'

You have choices: Use the oval with the same length and width or a placemarker.  I tend to choose the place marker. Change circleArea to add the extra parameter. Fix the above code to this:

let circleArea = {(radius:Double, placeMarker:Double) -> Double in
    return M_PI * radius * radius 
}
print (volume(height: 2,dim1: 10, dim2: 12, area:circleArea))

Literal Closure in Parameters

You don’t have to assign the closure to a constant. You can write it explictly in the function. For a rectangle area, you can write this in the playground.

 
volume = volume(
    height: 2,
    dim1: 10,
    dim2: 12,
    area: {(width,length)->Double in
        return width * length
    }
)
        print(volume)

You’ll find this a very common way of specifying the closure. When the closure is the trailing parameter, you can place it after the function like this:

volume =  volume(
    height: 2,
    dim1: 10,
    dim2: 12) 
    {(radius,placemarker)->Double in
        return M_PI * radius * radius
    }
print(volume)

Closures as Completion Handlers

One very common use of closures is completion handlers. Make a new function like this:

func volume(
    height:Double,
    dim1:Double,
    dim2:Double,
    completionHandler:(Double)->()
){
    let result = dim1 * dim2 * height
    completionHandler(result) 
}

Neither the completionHandler closure nor the volume function returns anything. Instead the result is the parameter of the completion handler.
Why would anyone do this? The reason is asynchronous processing. There are methods which Apple or the developer don’t want to run in the main thread. If you are querying a database, or opening a file any results appear in a closure. The database query might take some time, and you don’t want the rest of your application to freeze while you wait for the result. Similarly, while loading a file you don’t want to freeze the system waiting to learn if the load was successful.
Instead of return in these functions, the method runs on another thread at its own pace and lets the main thread go on its merry way. When done, the method calls something like completionHandler, handler, or completion with the results it returns as the completion parameter.

The developer uses the result their implementation of the completion handler. This is why most often closures happen completely within a function call. Usually they are specific to the code around it. For example, the volume function with the completionHandler would code like this to save the result to the volume property and output the result.

volume(height: 2,dim1: 10,dim2: 12)
{(result)->() in
    print(result)
    volume = result  //in a class use self
    resultLabel.text = String(format:"Completion %5.2f")
}

Completion handlers often pass off the value to a property in a class, like the code above assigns the value of  result to volume. If you run the code above within a class, be sure to include the class identifier or self. volume = result should be self.volume = result. The identifiers within a closure have no scope to the class. They must be explicitly stated.

A Real Example of a Completion Handler

As a general rule, you’ll find completion handlers when you don’t know when you will complete a task. One good example of this is presenting alert views.

Set up a new single view project named ClosureDemo, with Swift as the language. Go to the storyboard and drag a label and button.

Set the font on both to Title 1.  Select the button. Title the button Alert. Click the alignment iconalignment icon in the auto layout menu bar. Check Horizontally in Container and Vertically in container.  Change Update from to Items of new constraints like this:

center alignment

Click Add 2 constraints.  Select the Label. Title it Results.  Click the pin icon pinMenuButton in the auto layout toolbar.  In the dialog box that appears type 10 then tab, 10 then tab and 10 then tab. Click down to the bottom and Update frames with Items of New Constraints.

2016-09-02_07-07-01

Click Add 3 constraints.

Open the assistant editor and control-drag the label to the code. Name the outlet resultsLabel. Control-Drag from the button to the code. Change to an action. Name the action presentAlert.

Close the Assistant editor.  Open the Viewcontroller.swift code.

In the presentAlert action, add the following code:

 @IBAction func presentAlert(_ sender: UIButton) {
     volume = 240.0
 //example of a real completion handler.
     let alert = UIAlertController(
         title: "Alert",
         message: "Volume is \(volume)",
         preferredStyle: .alert)
   

Alert actions only activate after the alert shows and the action’s button gets tapped by the user. The UIAlertAction initializer uses handlers to describe the action when pressed. Add this to the compute action:

   
let clearAction = UIAlertAction(
    title: "Clear", style: .destructive,
    handler: {(action)->() in
        self.volume = 0
        self.resultLabel.text = "Cleared"
    }
)
 

In clearAction, the user sets the volume property to 0 and reflects that in the Label.  Again, the closure is independent of the scope of its class. You must specify self in order to use the ViewController classes’ properties and methods.

Add two more examples of actions:

       
let doubleAction = UIAlertAction(
    title: "Double",
    style: .default,
    handler: {(action)->() in
        self.volume *= 2
        self.resultLabel.text = "\(self.volume)"
    })
let cancelAction = UIAlertAction(
    title: "Cancel",
    style: .cancel,
    handler:{(action)->() in
       self.resultLabel.text = "\(self.volume)"
    })
   

Add the actions to the alert

alert.addAction(clearAction)
alert.addAction(doubleAction)
alert.addAction(cancelAction)

Finally present the alert controller. present has a completion handler. Add a print statement to it to see it in action, then a print after the present.

 
present(alert, animated: true, completion: 
{()->Void in
    print ("present Completed")
 })
 print ("Outside Handler")

Build and run. Tap the Alert button. You get the alert. The console printed this:

Outside Handler
present Completed

After calling present, The system executed the next step, and did not wait for it to finish printing  Outside Handler. Once the presentation completed, the system printed present Completed. The alert shows:

2016-09-12_06-03-57

Tap the double button. The label reads 240. Try the other buttons.

2016-09-12_06-04-38

You’ll find closures in most often in these handlers. Anything that takes and unpredictable amount of time will use a handler in a closure.

The Whole Code

You can find the code for this tutorial on the IBM Swift Sandbox. For cutting and pasting into an Apple Playground on iPad or Xcode, Use the code below.

//
// Closure Demo file 
// For MakeAppPie.com  closure tutorial 
// Sep 2016 by Steven Lipton 
// 
//
import Foundation

 //area of a round pizza
    func roundArea(radius:Double) -> Double{
        return M_PI * radius * radius
    }
    //volume of a round pizza
    func roundVolume(height:Double, radius:Double ) -> Double{
        return roundArea(radius: radius) * height
    }
    var volume = roundVolume(height:2,radius:6)
print( String(format:"Round volume %4.2f",volume))

//Closure to change the area formula
func volume(
    height:Double, 
    dim1:Double,
    dim2:Double, 
    area:(Double,Double) -> Double) -> Double
    {
        return area(dim1,dim2) * height
    }
    
    //Assigning type (Double,Double) -> Double to Classes
    
    let rectangleArea = { (length:Double,width:Double) -> Double in
    return length * width
}
let  rightTriangleArea = {(length,width) -> Double in
    return length * width / 2.0
}  
let ovalArea = {(length,width)->Double in
    return length/2.0 * width/2.0 * M_PI
}
let roundSliceArea = { (radius:Double, slices:Double) ->Double in
    return M_PI * radius * radius / slices
}
//Trying out the volume function

print (volume(height: 2,dim1: 10, dim2: 12, area:rectangleArea))
print (volume(height: 2,dim1: 10,dim2: 12, area:roundSliceArea))
print (volume(height: 2,dim1: 10, dim2: 12, area:rightTriangleArea))
print (volume(height: 2,dim1: 10, dim2: 12, area:ovalArea))

//Fitting a single parameter formula with a placemarker
let circleArea = {(radius:Double, placeMarker:Double) -> Double in return M_PI * radius * radius }
print (volume(height: 2,dim1: 10, dim2: 12, area:circleArea))
// closure within parameters
        volume = volume(height: 2, dim1: 10, dim2: 12, area: {(width,length)->Double in return width * length})
        print(volume)
// Trailing closure outside parentheses
volume =  volume(height: 2, dim1: 10, dim2: 12) {
     (radius,placemarker)->Double in
          return M_PI * radius * radius
}

//Closures as Completion Handlers
func volume(
    height:Double,dim1:Double,dim2:Double,
    completionHandler:(Double)->()
){
    let result = dim1 * dim2 * height
    completionHandler(result) 
}

// For asynchronous processing, use the closure instead of returning the result. 
volume(height: 2,dim1: 10,dim2: 12)
{(result)->() in
         print(result)
         volume = result
         
}

The ClosureDemo Alert project

This is the code for the ClosureDemo Alert project. Add the label and button as described above than connect to this code in ViewController.

//
//  ViewController.swift
//  ClosurePlay
//
//  Created by Steven Lipton on 9/9/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    
    var volume = 0.0
    
    @IBOutlet weak var resultLabel: UILabel!
    @IBAction func compute(_ sender: UIButton) {
        volume = 240.0
        //example of a real completion handler.
        let alert = UIAlertController(
            title: "Alert",
            message: "Volume is \(volume)",
            preferredStyle: .alert)
        
        let clearAction = UIAlertAction(
            title: "Clear", style: .destructive,
            handler: {(action)->() in
                self.volume = 0
                self.resultLabel.text = "\(self.volume)"
        })
        
        let doubleAction = UIAlertAction(
            title: "Double",
            style: .default,
            handler: {(action)->() in
                self.volume *= 2
                self.resultLabel.text = "\(self.volume)"
        })
        let cancelAction = UIAlertAction(
            title: "Cancel",
            style: .cancel,
            handler:{(action)->() in
                self.resultLabel.text = "\(self.volume)"
        })
        
        
        alert.addAction(clearAction)
        alert.addAction(doubleAction)
        alert.addAction(cancelAction)
        present(alert, animated: true, completion: {()->Void in
            print ("In present Handler")
        })
        print ("Outside Handler")
        
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    
}

An introduction to Size Classes for Xcode 8

A few years ago, were was only two devices: an iPad and an iPhone.  Storyboards were separate for iPads and iPhones. For any device, there was extra code necessary to use the same app in landscape and portrait. With new phone sizes, like the iPhone 6 plus, the iPad Pro or the retro-sized iPhone SE this became a bit more of a problem. Developers  could design a different storyboard for every size and orientation. That would be an excessive amount of work. Every new version of the phone or tablet would need to be different, with a separate storyboard. For all devices running iOS 10, that is four iPhones in portrait and landscape, three iPads in Portrait and landscape and ten panels for iPad multitasking. To develop a storyboard or code for each device  would mean designing for twenty-four views . If you hear about device fragmentation this is what people are talking about: different devices need different layouts because their screens are different sizes. It’s not just mobile devices that have this problem. AppleTV might run your iOS app. Televisions vary widely in size. AppleTV needs to change the view to handle those differences.

Over that last few years, there have been many solutions to adaptive user interfaces, which are interfaces that adapt their size and shape automatically to the device or window they happen to be in. Apple’s solution to this problem is auto layout, which lets the system do the hard work. Using relations between views, we describe how to layout the user interface.

In iOS 8, Apple introduced size classes, a way to describe any device in any orientation. Size classes rely heavily on auto layout. Until iOS 8, you could escape auto layout. IN iOS8, Apple changed several UIKit classes to depend on size classes. Modal views, popovers, split views, and image assets directly use size classes to determine how to display an image. Identical code to present a popover on an iPad  causes a iPhone to present a modal view.

Different Size Classes

There are two sizes for size classes: compact, and regular. Sometime you’ll hear about any. Any is the generic size that works with anything. The default Xcode layout, is width:any height:any. This layout is for all cases. The Horizontal and vertical dimensions are called traits, and can be accessed in code from an instance of UITraitCollection. The compact size describes most iPhone sizes in landscape and portrait. The trait of most importance is the width trait. The width is compact on all phones but the iPhone Plus models. There is one exception: the width in landscape is regular for an iPhone 6 Plus, which can cause some confusion. iPhone 6 Plus acts like a iPhone in portrait but an iPad in landscape. For both width and height, the full iPad and the 2/3 iPad  for multitasking is the regular size. The 1/3 and 1/2 iPad multitasking modes are compact in width and regular in height.

There’s one more variation to compact and regular. You can specify both compact and regular with Any. When you add Any to Compact and Regular there are nine size classes the developer can use.

size classes 9_1_1

Prior to Xcode 8, developers would have to know what all these sizes are and which device each belonged. That changed in Xcode 8 with a new user interface for Interface Builder. Xcode displays as selection of devices, the developer selects the devices, then Xcode previews the layout on that device.

Viewing Size Classes

The  any class makes working with size classes a bit more generic, saving work.  Our designs start in the width:any height:any size class, covering all cases. If we need a special design, then we use that specific size class. Prior to Xcode 8,  any needed to be explicit, and in some places you’ll find explicit uses of any still. Often Xcode 8 hides Any from you, making it implicit instead of explicit.

Open a new single view  project in XCode.  Go to the storyboard.  At the bottom left storyboard you’ll find this:

2016-09-02_06-29-00

the iPhone 6s is the default device for Interface Builder. Click this text. A new toolbar appears below, with the iPhone6s in portrait selected.

2016-09-02_06-31-53

 

Select the iPad Pro 12.9″ on the left. The toolbar changes to include all size classes for an iPad Pro.

2016-09-02_06-36-40

You’ll notice the screen looks blank. The iPad is too big for the screen. In the center of the toolbar click the 100%. In the menu that appears, click the 50%.

2016-09-02_06-40-09

To make the device easier to see, I changed the background color attribute of the view to light gray.

2016-09-02_06-43-44

Select  the iPhone 4s and under Orientation set the orientation to Landscape. We get a preview of a iPhone4s in landscape. Zoom in to 100% to see it better.

2016-09-02_06-47-16

All of this is in the Any size.  To change the size for devices, you can  click the Vary for Traits button.

A Little Auto Layout

So far you’ve previewed a blank storyboard.  Click the icon for a portrait phone. Add two buttons labeled Button 1  on the top of the scene and Button 2 towards the bottom. In the attributes inspector, Set the Text Color to White and the background of the button to Black.

2016-09-02_06-56-34

You’ll need a little auto layout to take advantage of size classes. If you’ve never used auto layout, we’ll keep it simple. Select Button 1. Find the pin button pinMenuButtonin the Auto Layout menu on the lower right side of Interface builder. Click pinMenuButtonto get a popup.

2016-09-02_07-01-19

In the highlighted box pin to the view above by typing 10 and hitting tab on your keyboard. The I-Beam below turns black, and the cursor moved to the left pin. Type 10 and tab again to pin the button to the right margin 10 points. Type 10 and tab one more time to pin the right side to the right margin by 10 points. Be sure to type tab to make sure the I-beam shows.  Toward the bottom of the popup, You’ll find the Update Frames button. Change the value from None to Items of new constraints. Your popup should look like this:

2016-09-02_07-07-01

Click Add 3 constraints and the constraints will appear on the storyboard, stretching the button to the correct size and position.

2016-09-02_07-11-20

Select Button 2 on the bottom. You’ll pin this to the bottom. Click pinMenuButtonto get a popup.  Tab past the first box without entering anything. try 10 then tab for the left pin, 10 and tab for the right pin, and 10 and tab for the bottom pin. Again set Update frames to Items of new Constraints. The popup should look like this.

2016-09-02_07-16-10

Add the three constraints. Your storyboard should look like this:

2016-09-02_07-17-38

Click the portrait orientation icon portrait icon 4sand the view resizes the buttons:

2016-09-02_07-20-16

Click the iPad Pro 9.7″ icon. Close the navigation and attributes inspector panels to give yourself room.  Change the scale to 75%. You’ll see this:

2016-09-02_07-25-30

Varying a Trait

The horizontal and vertical dimensions we refer to as traits. You can change one or both traits of the displayed device. The current traits are found after the name of the device in the view as button.  The current view show us a width of regular(wR) and height of regular(hR). The  default  iPhone6s in portrait is (wC hR) for compact width, Regular height.

2016-09-02_07-30-41a

Select an iPhone 6s Plus(wR hC) in landscape in the toolbar.  All phones in landscape are compact width and compact height  except  iPhone Plus. The iPhone plus models  are  regular width not compact. On regular width devices,  usually users will hold with both hands on the sides, using their thumbs across the device for most button presses. On compact width devices, users typically will hold from the bottom for compact width devices, using the thumb up and down.  You’ll now change all devices with a regular width to place the buttons on the sides, which is the easier to use place for thumbs to contact them.

Click the Vary for Traits button on the right side of the size class toolbar.  A popup appears.

2016-09-02_07-43-42

Check the Width checkbox. The  toolbar changes color and displays all size classes affected.

2016-09-02_07-46-05

Click in the newly colored area to close the popup. You are now in the mode that changes only the devices with a regular class width.  Open the attributes inspector, and change to the ruler for the size inspector.

2016-09-02_07-48-38

Select the Button 2.  Scroll down the size inspector until you find this constraint.

2016-09-02_07-50-34 Select it and press delete on your keyboard. Select Button 1. Find this constraint  and delete it:

2016-09-02_07-53-15

The storyboard looks like this:

2016-09-02_07-54-54

Select Button 1.  Click on the pin constraint button pinMenuButton. Select the bottom constraint and type 10 , then hit tab. Select Items of new constraints. The popup should look like this:

2016-09-02_07-58-38

Add the constraint. Select the Button 2. Type 10 , then hit tab for the top constraint. Select Items of new constraints like this:

2016-09-02_07-59-37

 

Add the new constraint. The layout now looks like this:

2016-09-02_08-01-45

 

Press Done varying.  The highlight color disappears. Change to Portrait 2016-09-02_09-47-12and the buttons are on the top and bottom.

2016-09-02_08-48-58

Go to an iPad Pro 9.7″ in portrait.  At 75% scale it looks like this:

2016-09-02_09-48-53

Under Adaptations in the size class toolbar, click the middle button. This is a multitasking view, which is compact width. The buttons are on the top and bottom again.

2016-09-02_08-53-24

Class Size Variations on Attributes

You can also change attributes based on the size classes. Click the gray view.  Go to the attributes inspector and click the + next to the background attribute.

 

2016-09-02_08-55-17

A popup appears:

2016-09-02_09-05-07

The current device’s traits appear in the box. Change Width to Regular and  Height to Any to make this an attribute of all regular width devices.

2016-09-02_09-07-03

Click Add Variation. A new background attribute for regular width appears under the default one.

2016-09-02_09-08-03

Change the wR background color to another color. I used Orange(#FF8000). Nothing changes on the storyboard. You are still on a compact width. Click the 2016-09-02_09-12-10 icon in Adaptation to go back to regular size. The background is Orange.

2016-09-02_09-13-32

 

You can browse though the other devices to see which ones have orange backgrounds and which have gray ones. As you can see, Size Classes in Xcode 8 are quite powerful ways of laying out your project easily, while customizing the layout for different devices.

Make a WatchOS 3 Haptic Catalog with a Picker

How does the Apple Watch communicate to the user when they are not looking at the watch face? That is done with haptics. Haptics are sounds and taps letting the user know something is happening. In this lesson, I’ll explain to you how to use haptics in Watch OS applications by creating a catalog of all the haptics using a WatchOS picker.

The picker control is an object for selection of multiple items. You can use the picker to display either full-screen images or text for selection. In this lesson, I’ll keep it simple with a text-based picker.

Make a New Project

Create a new WatchOS project called HapticPickerDemo. Use Swift as the language, uncheck Notifications, and keep the device Universal.  Open the interface.storyboard file in the WatchKit App group.

Find the picker in the object library. Drag a picker to the interface.

2016-08-27_07-01-54

With the picker selected you’ll see four attributes of a
picker.  The Focus Style and Indicator give you visual elements to highlight the picker. The most important attributes are Style and of course Enabled. You have three style choices: List, Stack and Sequence. List is a text-based picker. Stack and Sequence is an animated and non-animated image picker.

2016-08-27_07-09-18

Set your attributes to the illustration above.  Set the picker’s Style  to List. Set the Focus Style to  Outline with Caption. This will show an outline around the picker with a caption at the top. This caption is context sensitive to the picker selection. If you’ve set a complication on a watch, you are familiar with this focus style, such as  the black on green lettering  for weather in this setting:

2016-08-27_07-02-50

From the Object Library, add  a button under the picker. Title the button Play Haptic.

2016-08-27_07-04-29

Close the attributes and navigation panels. Open the assistant editor.

Make an outlet for the picker by control dragging from the picker to the InterfaceController class

@IBOutlet var picker: WKInterfacePicker!

Make an action for the picker’s action,changing Outlet to Action in the popup after you control drag.

 func pickerDidChange(_ value: Int) {
    }

Make an action for the button’s action, changing Outlet to Action in the popup after you control-drag.

 func playHaptic(_ value: Int) {
    }

Playing a Haptic

There’s two parts to a haptic you need to know. There is a play function [in WatchOS 2 playHaptic()]  you call on the current device object called by
WKInterfacedevice.current()object [in WatchOS 2 WKInterfaceDevice.currentdevice() ]. The play method has one parameter of type WKHapticType, which is an enumeration of haptic types.

  • notification tells the user there is a notification,
  • directionUp indicates an upward value,
  • directionDown indicates a downward value,
  • success indicates the successful completion of a task,
  • failure a failed task,
  • retry tells the user to retry,
  • start the beginning of an action,
  • stop the end of an action,
  • click is a very slight click, which you probably won’t hear but probably feel on a watch.

Close the Assistant editor.  Go to InterfaceController.swift in the App extension group. To make the button play a .success haptic change the playHaptic action to this:

 
func playHaptic(_ value: Int) {
    WKInterfaceDevice.current().play(.success)
}

Build and run using the 38mm simulator. Press the Play Haptic Button and  you get a sound.

In the simulator, you don’t get the taps you’ll get if you were using the watch. If you have a watch, run this on your watch and you’ll feel the tap.

Implementing the Picker

There is no documentation on what the taps feel like. It’s hard to describe in words,you have to feel it. In the rest of this lesson we’ll use WatchKit’s WKInterfacePicker to make a selectable catalog of haptics you can run on an Apple Watch.

Pickers have one parameter. Since pickers hold the list in a sequence, the parameter value is the index of that sequence. Delete all life cycle methods but willActivate. Add the list of haptic types exactly in the order above for the picker as an array.

let titles = [
    "notification","directionUp",
    "directionDown","success",
    "failure","retry",
    "start","stop","click"
]

Pickers will not use these arrays directly. Pickers get their selections from a WKPickerItem object. Code will create an array of WKPickerItem, and set that as the picker’s items. WKPickerItem has several properties to make the picker flexible.

  • title – A String? to use in a list
  • caption – A String? used as a caption for item in a list
  • accessoryImage – A Small WKImage? to display next to title in a
    list or as an alternate to text as in small complication setting.
  • contentImage – In a Stacked or Image Sequence style, a
    WKImage?

The picker has a setItems method which takes the array of picker
items to make the list, stack, or sequence of images, depending on the display style. For every picker you create, you build a function that iterates through the arrays, adding the elementsts as pickerItems. Add a function refreshPickerItems:

 func refreshPickerItems(){
 }

Add an empty array of picker items to the function.

 
func refreshPickerItems(){
    var pickerItems:[WKPickerItem] = []
}

Add a loop iterating through the titles.

 
func refreshPickerItems(){
    var pickerItems:[WKPickerItem] = []
    for item in titles{
    }
}

In the loop creates an instance of WKPickerItem. Add to pickerItem the title  from the corresponding element of the arrays. Add the literal caption Haptic. (If you want to experiment, with captions, try coding it as "Haptic-" + item ) Add the picker item to the pickerItems array.

func refreshPickerItems(){ 
    var pickerItems:[WKPickerItem] = [] 
    for item in titles{ 
        let pickerItem = WKPickerItem()
        pickerItem.title = item
        pickerItem.caption = "Haptic"
        pickerItems += [pickerItem]
    }
}

Once the pickerItems array is complete, set the items in the picker.

func refreshPickerItems(){ 
    var pickerItems:[WKPickerItem] = [] 
    for item in titles{ 
        let pickerItem = WKPickerItem()
        pickerItem.title = item
        pickerItems += [pickerItem]
    }
    picker.setItems(pickerItems)
}

In willActivate, call this function to refresh the list.

override funcwillActivate() {
    super.willActivate()
    refreshPickerItems()
}

Selecting a Picker Entry

If you were to run the project now, you would see the picker displayed correctly. You could not select anything though. That happens in the action. The pickerDidChange action has a Int parameter value, which is the current index on the picker.

I told you earlier to be careful getting the order correct in the titles  array.  This is why. Since WKHapticType is a enum you can select the type by the rawValue of the enum. That raw value matches the value parameter. Before we try this, add a property hapticType to store the type.

varhapticType:WKHapticType = .notification

I’ve initially set this to .notification. In the pickerDidChange action set the hapticType property with the raw
value we can get the value parameter.

@IBAction func pickerDidChange(_ value: Int) { 
    hapticType = WKHapticType(rawValue:value)!
}

Playing the Haptic

The last step is to play the haptics. To the action  pickerDidChange  add a .click haptic

WKInterfaceDevice.current().play(.click)

This demonstrates one use for haptics, feedback to the user.  In the action playHaptic, change the .success haptic to the property hapticType

 @IBAction fund playHaptic() {
        WKInterfaceDevice.current().play(hapticType)
    }

Run in the 38mm simulator. Select a haptic and press Play Haptic. You’ll hear the chime for it.

If you have a watch, load it on your watch, and try the application. When you tap Play haptic on the watch you will get the watch tap as well.

Don’t over use Haptics. Unless your app is an interval timer, don’t  have one go off every second or minute. That just gets annoying. Use haptics at the right times to attract a user’s attention to their Apple watch or give instant feedback.

The Whole Code

//
//  InterfaceController.swift
//  HapticPickerDemo WatchKit Extension
//
//  Created by Steven Lipton on 8/26/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import WatchKit
import Foundation


class InterfaceController: WKInterfaceController {

    @IBOutlet var picker: WKInterfacePicker!
    
    //List of WKHapticType in rawValue order
    let titles:[String] = [
        "notification","directionUp",
        "directionDown","success",
        "failure","retry",
        "start","stop","click"]
    
    var hapticType:WKHapticType = .notification
    
    //Use the index from the picker as the rawValue for the WKHapticType
    @IBAction func pickerDidChange(_ value: Int) {
        hapticType = WKHapticType(rawValue: value)!
        WKInterfaceDevice.current().play(.click)
    }
    
    
    @IBAction func playHaptic(){
        WKInterfaceDevice.current().play(hapticType)
    }
    
    
    func refreshPickerItems(){
        var pickerItems:[WKPickerItem] = []
        for item in titles{
            let pickerItem = WKPickerItem()
            pickerItem.title = item
            pickerItem.caption = "Haptic"
            pickerItems += [pickerItem]
        }
        picker.setItems(pickerItems)
    }
    
    override func willActivate() {
        // This method is called when watch view controller is about to be visible to user
        super.willActivate()
        refreshPickerItems()
       
        
    }

}

Accessing the Digital Crown in WatchOS

On the Apple Watch, the digital crown seems to be a great way to control your watch. the Slider and picker controls do use it, but direct developer use was prohibited in Watch OS2. In a nice change, Watch OS3 does. In this lesson, we’ll show how to get data from the digital crown in your applications.

Start a new Xcode WatchOS project DigitalCrownDemo. Deselect the notifications and save the project.

2016-08-22_06-26-21

Add three labels to the watch interface. Title them as follows:

2016-08-22_05-51-19

In the assistant editor, Connect the labels to outlets like this:

@IBOutlet var statusLabel: WKInterfaceLabel!
@IBOutlet var valueLabel: WKInterfaceLabel!
@IBOutlet var rpsLabel: WKInterfaceLabel!

We’ll track two data values the rotation speed and a value based on the change in value from the watch. Add them to the controller

var rps = 0.0
var value = 0.0

Add a update method for the display:

func updateLabels(){
    valueLabel.setText(String(format:"Value:%1.3f",value))
    rpsLabel.setText(String(format:"RPS:%1.3f rps",rps))
}

The crown uses a delegate. In interface controller, Adopt the <code>WKCrownDelegate</code>
class InterfaceController: WKInterfaceController,WKCrownDelegate {

The crown must have focus to return information. In awake(withContext) set the focus for the crown

    
override func awake(withContext context: AnyObject?) {
    super.awake(withContext: context)
    crownSequencer.focus()
    crownSequencer.delegate = self
}

The interface controller has a property crownSequencer that monitors the activity on the crown. We set the focus to crownSequencer and set the delegate method locations to this class.

Add the crownDidRotate delegate method, which returns values when there is a change in the rotation of the crown.

//MARK: Delegates
func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) {
   value += rotationalDelta
   rps = (crownSequencer?.rotationsPerSecond)!
   statusLabel.setText("Moving")
   updateLabels()        
}

We have two arguments in this delegate method: the crownSequencer and the rotationalDelta. The Rotational debts is the change between the current and last position of the crown's rotation. One of the properties of the crownSequencer is rotations per second, giving a speed to the rotations. The code places the rotational delta and the rotations per send into a string for output to the watch. Most likely we'll want a position from the rotational delta, so we've added it to value.

Add the crownDidbecomeIdle delegate method

 
func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) {
    rps = (crownSequencer?.rotationsPerSecond)!
    statusLabel.setText("Stopped")
    updateLabels()
}

This delegate method fires when the crown stops. The code prints that the crown stopped and the last rotational speed before it did.

Run the code on the 42mm simulator. To use the crown hardware in the simulator, After a single click on the background of the watch face drag up and down two fingers. Stopping and starting the drag, you will see the display change.

The crownSequencer is smart enough to know your watches orientation from your settings. Up is always a positive value and down is always negative. there's a lot to do with this, from making new controls for your watch to an input for small games with sprite kit and scene kit.

The Whole Code

//
//  InterfaceController.swift
//  DigitalCrownDemo WatchKit Extension
//
//  Created by Steven Lipton on 8/22/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import WatchKit
import Foundation


class InterfaceController: WKInterfaceController, WKCrownDelegate {
    @IBOutlet var statusLabel: WKInterfaceLabel!
    @IBOutlet var valueLabel: WKInterfaceLabel!
    @IBOutlet var rpsLabel: WKInterfaceLabel!

    var value = 0.0
    var rps = 0.0
    
    func updateLabels(){
        valueLabel.setText(String(format:"Value:%1.3f",value))
        rpsLabel.setText(String(format:"RPS:%1.3f rps",rps))
    }
    
    override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        crownSequencer.focus()
        crownSequencer.delegate = self
    }
    //MARK: Delegates
    func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) {
        value += rotationalDelta
        rps = (crownSequencer?.rotationsPerSecond)!
        statusLabel.setText("Moving")
        updateLabels()
        
    }
    
    func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) {
        rps = (crownSequencer?.rotationsPerSecond)!
        statusLabel.setText("Stopped")
        updateLabels()

    }

    
}

How to Make Local Notifications in iOS 10

I’ve written before about local notifications in iOS. However, Apple has changed all that starting in iOS 10. In versions earlier than iOS 10, notifications were part of UIKit, but required the developer to make changes to the view controller and app delegate. Different types of notifications had different code. In iOS 10 notifications unifies the coding of notifications into the view controller.

There are two types of notifications: Local and remote notifications. Remote notification require connection to a server which pushes the notification to your device. These are beyond the scope of this lesson. Local notifications are notifications for events that happen on your device. We’ll set up these types of notifications in this lesson

Make A New Project

Make new single-view project named LocalNotificationDemo with Swift as the language. Go to the storyboard. Add a button centered on the view labeled 10 Second Notification. Using the align button alignment iconin auto layout, center the button like this.

2016-08-08_05-42-13

Be sure to check the update frame Items to New Constraints. You’ll have a storyboard like this:

2016-08-08_05-46-41

Open the assistant editor and make an action send10SecNotification to the button by control-dragging to the view controller code. Close the assistant editor.

Add the Framework

Notifications are a new framework. Go the to the viewcontroller.swift code and add the following under import UIKit :

import UserNotifications

This added the framework for the user notifications.

Check for Permission

Before any notification, we’ll need to check if the user allows us to use notifications. We only do this once, so add this to viewDidLoad:

override func viewDidLoad()
     super.viewDidLoad()
     UNUserNotificationCenter.current().requestAuthorization(
         options: [.alert,.sound,.badge],
         completionHandler: { (granted,error) in
             self.isGrantedNotificationAccess = granted
         }
    )
}

The object UNUserNotificationCenter.current() returns the current notification center. We ask for authorization with the requestAuthorization(options:completionHander) method. We have three options we can use to give an alert:

  • .alert – Display the alert window and text of the notification
  • .sound – Plays a sound or vibrates. On Apple Watch uses a haptic.
  • .badge – Places the red dot of the app to give a count of notifications from the app.

The argument options is an array. We add which of these three we wish to use for our notification.

The completion handler has two arguments grants and error. You can use these to handle the user preferences in notifications. Here I’ve set a Bool value I’ll use in the action. Add the property to the viewController class

var isGrantedNotificationAccess:Bool = false

Add this to the send10SecNotification action:

@IBAction func send10SecNotification(_ sender: UIButton) {
    if isGrantedNotificationAccess{
        //add notification code here
    }
}

This prevents the notification from scheduling if the user doesn’t want one.

Setting up Content

The notification is put together a lot like an onion – it has layers on top of layers. One layer creates the content of the notification . Add this under the //add notification code here comment:

let content = UNMutableNotificationContent()
content.title = "10 Second Notification Demo"
content.subtitle = "From MakeAppPie.com"
content.body = "Notification after 10 seconds - Your pizza is Ready!!"
content.categoryIdentifier = "message"

The UNMutableNotificationContent object contains all the content in a notification. We’ve included all the text properties title, subtitle and body, but you can add media content as well. An important property is the categoryIdentifier, which will help us in a few steps associate actions to the notification.

Setting Up Triggers

Triggers are the events which set off a notification. In earlier versions of iOS, there was only one easily accessible trigger: time expressed as a date. For local notifications, you have triggers for times, dates, and locations. There’s one more type of trigger for a remote notification. We’ll use the time interval trigger to set a trigger for ten seconds.

let trigger = UNTimeIntervalNotificationTrigger(
    timeInterval: 10.0,
    repeats: false)

For the time interval trigger, we have another new argument: repeats. If you need multiples of the same notification,set this to true. This is a feature added to iOS 10. In earlier versions of notifications, multiple notifications needed to be scheduled clogging up you limited number of notifications. If repeats is true, then there is only one. The notification triggers, deletes itself from the schedule then re-assigns itself.

Adding the Notification Request

We’ll take the trigger and content, add a string to name the request and whip them together in a UNNotificationRequest like this:


let request = UNNotificationRequest(
    identifier: "10.second.message",
    content: content,
    trigger: trigger
)

The identifier plays an important role. The system will prevent conflicts of the same notifications firing simultaneously. The watch and the phone may want to display the same local notification, but the system will see the identifier, and choose only one. The identifier also gives the developer control of the notifications. To stop a repeating UNTimeIntervalNotificationTrigger, there is a method to delete the notification you’d have to implement.

Adding the Notification

Once you have a request, add it to the current notification center

UNUserNotificationCenter.current().add(
    request, withCompletionHandler: nil)

Build and Run. On the first execution, we’ll be asked for permission to use notifications. After this first answer, the device will store the answer in the settings for the app.

2016-08-08_06-28-13

The user can change it at any time in the user settings, though the change will not take until the next start of the application.

Allow notifications. When the application appears, Press the button and then press Command-Shift-H to go to the home screen. Wait ten seconds. You will see this:

2016-08-08_06-29-00

Click on the notification, and you are back in the app. Click the button again, and press Command-L to simulate the user locking their phone. After ten seconds you get this:

2016-08-08_06-31-46

Click on the notification then open the phone with Command-Shift-H for the home button. You are back in the app.

That’s how to make a basic notification in iOS. You can use the same code in WatchOS to get local notifications as well. There’s a lot more to explore in notifications, but that will be in another lesson.

The Whole Code

//
//  ViewController.swift
//  LocalNotificationDemo
//  A Very Basic Local Notification
//  Created by Steven Lipton on 8/8/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit
import UserNotifications

class ViewController: UIViewController, UNUserNotificationCenterDelegate {
    var isGrantedNotificationAccess:Bool = false
    @IBAction func send10SecNotification(_ sender: UIButton) {
        if isGrantedNotificationAccess{
            //add notification code here
            
            //Set the content of the notification
            let content = UNMutableNotificationContent()
            content.title = "10 Second Notification Demo"
            content.subtitle = "From MakeAppPie.com"
            content.body = "Notification after 10 seconds - Your pizza is Ready!!"
            
            //Set the trigger of the notification -- here a timer. 
            let trigger = UNTimeIntervalNotificationTrigger(
                timeInterval: 10.0,
                repeats: false)
            
            //Set the request for the notification from the above
            let request = UNNotificationRequest(
                identifier: "10.second.message",
                content: content,
                trigger: trigger
            )
            
            //Add the notification to the currnet notification center
            UNUserNotificationCenter.current().add(
                request, withCompletionHandler: nil)
            
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert,.sound,.badge],
            completionHandler: { (granted,error) in
                self.isGrantedNotificationAccess = granted
            }
        )
    }



}

Adventures in Swift and iOS App Development