A Pro walkthrough of Swift Playgrounds 4

I’ll show you many of the new and nifty features of Swift Playgrounds 4 in this video.

Why You Don’t Draw a Straight Line to Success

When I start talking about drawing, most people usually say they can’t do art with the phrase “I can’t even draw a straight line.” Video 10 in the Explore art with Procreate series addresses this directly: I teach you to make the straight lines of the artist. However, in the video, I didn’t unpack what that phrase really means and why it is holding you back in a lot more than drawing.  

What do we mean by a straight line? Generally, we mean a line made with the aid of a ruler or computer. We are demanding a line that is accurate to a level only machines can do. It looks like this: 

It may be accurate, but an important feature of this line is how boring it is. It has no life or energy. the line is always the same, no matter who draws it.  What many people draw instead is this: 

It is the careful line of trying to imitate a machine. Even the slightest error is magnified in our brains, often leading to more errors. Perfectionism kills this line. The controlled line of handwriting often does not work as accurately as we wish. Controlling our fingers and wrist to make a line requires too many inputs for the long distance. Our bodies, therefore, cause us to send different inputs than someone else’s body, and this line is always unique in its mistakes. If it were accurate like the machine line, everyone’s handwriting would be the same. Our errors in lines are what make us individuals. It is part of identity, yet we curse ourselves for being unique by saying we can’t draw a straight line. That line should be celebrated, not cursed. 

I draw, like many artists, not with the fingers and wrist like we are handwriting, but with the arm. I make not one but several strokes, and somewhere in all of them is a straight line. 

This line might look like a messy scribble close up. If you make your marks small enough or use big enough paper,  it looks straight with lot more energy. 

This line is a series of attempts to make a successful straight line. Each attempt would not be considered a straight line as we defined the mechanical one. Put together. They make a line that works for most uses of straight. 

This technique is not only true of lines but most creative work. We expect our creative work to look machine-made when it is our humanity and unique perspectives and abilities that give it value. Even more, when we try not one but multiple failed lines until we get a straight line without a machine and with more energy and personality than that drab mechanical one. Only on close inspection do we see all the failures that went into making that line. 

The best, most artistic way to make a straight line is to try and fail as many times as possible. Somewhere in all that mess, what we are looking for appears. So too with life, success and all creative work. 

Custom Quick Help in Xcode

There are some powerful documentation features of Xcode you may not be using and will make your life a lot easier. Take a look at the downloaded exercise file. Go ahead and run it like I have here. I wrote a simple app to turn on and off a matrix of lights. Stop the app. 

Looking at the ContentView code, It is hard to figure out what is going on. Click On LightbankView.  I’ve got a light bank up top and a Toggle buttonView below it that I made. One’s a model and one is a view. I might use elsewhere they are hard to add to the code. 

Option-Click the VStack. You get a popup that describes how a VSstack works. If you open the right sidebar and click the Help icon you see the same info again. If you option-click LightBank, you get very little, but you can change this. You see under the quick help Declared in. You can click that and go to the definition. 

Above the definition, you’ll add a special comment. Instead of two slashes, add three. Add the comment. 

/// A data structure for a row of lights 

 Hit Command-B to build the project. Click on the LightBank struct and the quick help shows your summary. 

Any time you add three slashes and text above a declaration, it shows up in the Quick Help summary. In Xcode 11, You can do this faster with a command-click.  Command-click BankState, and select Add documentation. You get a place marker for your summary. Add the following: 

/// A pattern of lights.

 As this is an enum, I might want the possible values, so I’ll add that.

Can be .allOn, .allOff, .evenOn or .oddOn.

Really that should be in the discussion, not the summary. Press return before Can. The comment appears on the next line. Add a dash between the triple slash and Can. Build again and check the Quick Help. Both the sidebar and the popup show the values in the discussion section of the quick help. 

 /// - Can be .allOn, .allOff, .evenOn or .oddOn.

You can format this too. For using code case on my enum values, I’ll use the reverse single quote found above the tab key. I can quote my values like this: 

 /// - Can be `.allOn`, .`allOff`, `.evenOn` or `.oddOn`.

Build and check it out. 

There are other callouts besides discussion. Head down to the init. Command-click and Add documentation to init. For methods with parameters you also get the parameters already listed for you. You can add these manually or use the Add parameter in Command-Click.  Add these comments: 

/// A data structure for a row of lights
/// - Parameter count: The number of lights in a row
/// - Parameter on: The pattern of lights in the row from `BankState`

I like having enum values handy, so I’ll finish this off with a copy and paste of the line above. 

 /// - Can be `.allOn`, .`allOff`, `.evenOn` or `.oddOn`.

Build again and head back to ContentView

Option-Click LightBank. You get the summary for the Struct.

Option-Click on count and you get the quick help for the initializer.

Your summary even shows in the code completion. add this: 

var moreLights = Lightban

Code completion has a summary. 

You can do a lot more formatting and even links with markup in quick help. Markup is also used in Playgrounds. Check out chapter 4 of my Swift Playgrounds Application Development course for a lot more you can do with markup.  

The Whole Code

You can look at the completed code for this project on GitHub here:

Checklists in Swift UI

In our last tip, I made a single checkbox in SwiftUI. Let’s learn more about collections in SwiftUI and build a checklist. 

If you download the exercise file, you’ll find an expanded version of last week’s project. Under the model folder, I added some data for our list of pizzas to try.

let checkListData = [
     CheckListItem(title: "Neopolitan"),
     CheckListItem(title: "New York"),
     CheckListItem(title:"Hawaiian"),
     CheckListItem(title:"Chicago Deep Dish"),
     CheckListItem(title:"Californian")
 ]

You’ll see that has a struct backing it that stores the title and  an default value of false for each item. 

struct CheckListItem{
     var isChecked: Bool = false
     var title: String
 }

Suppose I wanted to make a list of all my items. Head to CheckListView.  I’d use a list like this: 

List(checkListData){ item in
     CheckView(isChecked: item.isChecked, title: item.title)
}
.font(.title)

However we get a message that CheckListItem does not conform to the Identifiable protocol.  Any object you iterate in Swift UI must have essentially a primary key, a unique identifier. I have two options here: The first is to use existing data, such as the title as my key. I can specify that by adding a parameter of id, set to my title.

 List(checkListData, id:\.title){ item in

The error disappears. However, that means I cannot have two titles the same. If you have unique data this works fine. You have to specify the id in this case, so this makes the checklist less adaptable. 

Our second and better possibility is to adopt the protocol Identifiable.  Head over to the ChecklistItem.  Add the Identifiable protocol to the struct. 

struct CheckListItem:Identifiable{ 

This protocol has one required property named id, which contains a unique identifier. While id can be any hashable type, I’ll make id an integer. 

var id: Int

I’ll change my data to include the id. I’ll add it to the first item. Then cut and paste into the rest. 

let checkListData = [
     CheckListItem(id:0,title: "Neopolitan"),
     CheckListItem(id:1,title: "New York"),
     CheckListItem(id:2,title:"Hawaiian"),
     CheckListItem(id:3,title:"Chicago Deep Dish"),
     CheckListItem(id:4,title:"Californian")
 ]

Our errors have disappeared. 

Change your content view to use the CheckList view. 

Again, I’ll Run on a device instead of the canvas.  You get a nice looking checklist that you can check. This is not yet storing the checkmarks. We’ll look into that in another tip. You’ll find it is best to adopt the Identifiable protocol for iterating in lists. 

The Whole Code

The code for this project is downloadable on GitHub at https://github.com/MakeAppPiePublishing/Tips_08_04_Checklist_End. There’s an accompanying video you can find at LinkedIn Learning.

Checkboxes in SwiftUI

There’s lots of good stuff in SwiftUI. One missing control is old fashioned checkbox like I have on the web or on my Mac. There is a way to make a checkbox with a Toggle control. However, as a bit of a intro to the philosophy of SwiftUI, let look at creating a checkbox.

Open up the project I have in the exercise files.  I made a second struct CheckView for the checkbox.. My checkbox needs a box. I can use the Image object in SwiftUI with the systemName parameter to get a SFSymbol from SwiftUI. 

Image(systemName: "square")

The checked square is simply the word checkmark before the square. 

Image(systemName: "checkmark.square")

I just have to switch between them. That will need some kind of variable. I’ll add this

 var isChecked:Bool = false

In SwiftUI we avoid if...then...else control structures and defer to conditional operators. I can re-write the Image like this

 Image(systemName: isChecked ? "checkmark.square" : "square")

Now if false I get a square, and if true I get a checked square. I would have to change the parameter to do that, and I could only do that once for a given checkbox. I could write a function toggle like this: 

func toggle(){isChecked = !isChecked}

But that gives me an error. Within aView, values are immutable. But you can change them by adding @State infront of the var. 

That makes this variable a state variable.

@State var isChecked:Bool = false 

Not only can you change it, but any change to it updates the view.  So anytime toggle runs, the image will change.

So how does the toggle run?  In a button, making toggle the action and make the image the label for the button.

Button(action: toggle){
   Image(systemName: isChecked ? "checkmark.square" : "square")
}

Let’s try this out. I’ll run this on an iPhone XR simulator. And I can tap the checkmark. 

Of course there’s two more things I can can do. One is make the check mark a lot bigger that that. The other is add text for a title. Text will be through another variable

var title:String 

This one won’t need a state since I’m only going to display it, not change it.  I’ll add a HStack in the button to keep the checkmark and text next to each other , then add the text. 

Button(action: toggle){ 
   HStack{ 
      Image(systemName: isChecked ? "checkmark.square" : "square")
      Text(title)
   }
}

And fix the preview to match. 

CheckView(title:"Title") 

Head over to ContentView.  Change the title  to something bigger. 

CheckView(title:"Likes Pizza")
    .font(.title)

Run again and you get a nice checkmark control, ready to use. Buttons are tap only actions in SwiftUI, but there is a lot you can do with buttons paired up with a state variable. 

The Whole Code

You’ll find code for this lesson In GitHub here. There is aslo a video of this at LinkedIn learning. But the basic check box looks like this:

 struct CheckView: View {
    @State var isChecked:Bool = false
    var title:String
    func toggle(){isChecked = !isChecked}
    var body: some View {
        Button(action: toggle){
            HStack{
                Image(systemName: isChecked ? "checkmark.square": "square")
                Text(title)
            }

        }

    }

}

By the way, that action can be reduced to a closure, so you can skip the function altogether if you wish to streamline.

Segue Actions

If you’ve been working with View controllers for a while, you’ve probably dealt with prepareForSegue. In Xcode 11, there’s a new way to handle this that makes a little more sense: Segue actions. Let’s take a look at this feature.  

If you download the storyboard, you’ll find I set one up for you. It is just a button that increments a counter on a detail view. Delete the segue between the two. I’ll use the button as navigation only. I’ll control-drag to the detail controller, and use a show segue. 

Open up the assistant editor. Control drag from the segue to the code for ViewController somewhere below viewDidLoad. Release the button. You’ll be asked for a connection. Name it addOneSegue. This method is a segue action, which triggers on a segue. It does much of what prepareForSegue does without some of the overhead. Add the action, and close up the assistant editor. Head over to the View Controller code. 

I’m going to make a counter. I’ll need a counter property here

var count = 1 

You’ll be more specific here than in a prepareForSegue. You make an instance of the controller, set its properties, and return the controller. No messing with id’s or anything else. Let’s try this.

   @IBSegueAction func addOneSegue(_ coder: NSCoder) -> DetailViewController? {

First, I’ll make an instance. Because this is instantiating to coder, this is optional. So use an if let here. 

    if let detailVC = DetailViewController( coder: coder){

If the controller instantiates, I’ll add one and set that in the detail. 

count += 1
detailVC.count = self.count

Then return the view controller.

return detailVC    
}

 And finally, if it doesn’t work, return nil.     

return nil
}

Over at the detail, we’re all set up with viewDidLoad updating the count. Run this.

You’ll get the button. Tap it. You get a count. Hit Back, then the button. Again, it updates. 

Segue actions save time in finding and naming segues and destination controllers. This new feature is a small gift of SwfitUI to UIKit. You’ll use this to get SwiftUI into storyboards, but I’ll discuss that at another time and in my Course SwiftUI Essential Training. Do remember outlet actions instantiate the instance, but do not load it. You still can’t assign a value to the label directly. 

I find this a better alternative than prepare for segue in delegation between my view controllers. 

The Whole Code

You can find the code for this lesson in GitHub here:

Use Sets in Swift

Of the most underrated collection types is sets. Sets are an unordered collection with unique values. Let’s take a look at what you can do with sets. 

Download the exercise files and you’ll find I created  playground with a Struct called Pizza. I’ve made two pizzas for you. Currently, this is all arrays, but does it have to be arrays? Ask yourself three questions:

  • Do I need ordered values?
  • Do I need unique values
  • Do I need access directly to any value? 

If you need ordered values and direct access, you’ll want an array. If you want unique values and don’t care about order or access, you’ll want a set. A Pizza is a good example of where a set works well. An ingredient is an ingredient. There is no good reason to list it twice, and it also has no particular order for toppings. I don’t need to access only one ingredient.  That’s a set. Sets are stored and accessed more eifficiently than array or dictionaries, so if you can use a set, do so. 

Lets convert this struct to sets. For the two constants on top, I add Set and the compiler will implicitly make this a set of strings. For cheese and toppings, I have to get more explicit. 

var cheeses: Set<String>
var toppings: Set<String>

I added the type of the set in angle brackets. Run what you got and it still works.  The pizzas below still compile. 

You can enumerate a set using for. For example: 

for cheese in quattroFormaggi.cheeses{
    print(cheese)
}

You are not guaranteed the order however.  If I want them sorted, I can use the sorted method

for cheese in quattroFormaggi.cheeses.sorted(){
    print(cheese)
}

The core of sets is membership testing. I can check for the existence of something easily. 

chicago.toppings.contains("Sausage")

returns true

quattroFormaggi.toppings.contains("Sausage")

Return false.

I can look for a bigger set too. I have a set of toppings here. I can check it against one of my pizzas. 

 margheritaDoc.toppings.isSubset(of: toppings)

That comes out false. I can find the missing ingredients quickly. 

margheritaDoc.toppings.subtracting(toppings)

And I find I have whole tomatoes instead of crushed here. 

Are any authentic  cheeses also toppings?  I can use an intersection to check that.

margheritaDoc.toppings.intersection(margheritaDoc.authenticCheese)

That gives me one case. If I want true or false I can use isEmpty. To look for the empty set. 

margheritaDoc.toppings.intersection(margheritaDoc.authenticCheese).isEmpty

What if I want to check against two pizzas. I can union the pizzas and check that against the list. 

let cheeses =  quattroFormaggi.cheeses.union(margheritaDoc.cheeses)
cheeses.intersection(quattroFormaggi.authenticCheese)

So What of the two lists are excluded?  

cheeses.symmetricDifference(quattroFormaggi.authenticCheese)

 I for one would consider all that Authentic. 

In this simple example you can see that if you don’t care about order and have unique values, Sets may be a an efficient way of storing data.. 

The Whole Code

You can download this Swift playground from GitHub here: Tips_08_01_Sets_End

struct Pizza{
    let authenticCheese: Set = ["Bufalo","Fior de late","Gorgonzola","Mozzarella","Parmigiano"]
    let authenticIngredients: Set = ["Basil","Peppers","Tomatoes", "Basil", "Oregano"]
    enum Crust{
        case thin,thick,pan,lavosh,potPie
    }

    var crust : Crust
    var cheeses: Set<String>
    var toppings: Set<String>
}

let margherita = Pizza(crust: .thin, cheeses: ["Mozzarella"], toppings: ["Basil","Tomatoes","Parmigiano","Oil"])

let margheritaDoc = Pizza(crust: .thin, cheeses: ["Bufala"], toppings: ["Basil","Tomatoes","Parmigiano","Oil"])

let chicago = Pizza(crust: .pan, cheeses: ["Mozzarella"], toppings: ["Pizza Sauce","Sausage"])

let quattroFormaggi = Pizza(crust: .thin, cheeses: ["Fontina","Gorgonzola","Mozzarella","Parmigiano"], toppings: ["Crushed Tomatoes","Basil","Oil"])

for cheese in quattroFormaggi.cheeses.sorted(){
    print(cheese)
}

let toppings: Set = ["Crushed Tomatoes","Basil","Oil","Chicken","BBQ Sauce","Red Onions","Parmigiano", "Peppernoi","Prociutto","Pineapple","Canadian Bacon"]

chicago.toppings.contains("Sausage")

quattroFormaggi.toppings.contains("Sausage")

margheritaDoc.toppings.isSubset(of: toppings)

margheritaDoc.toppings.subtracting(toppings)

margheritaDoc.toppings.intersection(margheritaDoc.authenticCheese).isEmpty

let cheeses =  quattroFormaggi.cheeses.union(margheritaDoc.cheeses)

cheeses.intersection(quattroFormaggi.authenticCheese)

cheeses.symmetricDifference(quattroFormaggi.authenticCheese)

			
			

Strings in Swift: Going back to BASICs

A few tips ago, we went under the hood with unicode Characters and their relationship to the Swift String type. For most, that’s great theory, but how does it apply to strings, not characters? When I started programming back in the 1980’s, I had three string functions in BASIC: RIGHT$, LEFT$, and MID$. Let’s create a simple extension to String that will let you use MID$ using string ranges, then discuss right$ and left$ equivalents, and how these get returned. 

Download the exercise files. head to the embedded playground and hide everything but the playground. set run to manual if necessary.

You’ll find I started an extension. 

extension String{
}

I also added an example string you run the playground, you’ll find the string has a lot of grapheme clusters of arrows and emoji.

Strings in Swift do not use integer string indexes to access characters due to grapheme clusters. Instead of arrays, strings are an ordered collection that uses relative distances from two constants, startIndex and endIndex

For my function I’ll need those string indexes. I made you a function to get those, returning an optional value. If we are out of range, it returns nil.  

func index(_ position:Int)->String.Index!{
        if position < 0 || position > self.count {return nil}
        return self.index(self.startIndex, offsetBy: position)
    }

For my midString method, I want a string starting at one character position and going for a length,

func midString(from index:Int, length:Int)->String!{

}

First I’ll check if my starting positions is a valid one, returning nil if not a valid position: 

if let startPosition = self.index(index){

}
return nil

String Indexes take ranges, so I use a closed range from the startPostion to an endPosition. I calcualte the endPosition by adding the length, but still have to take 1 off the length. That’s an optional so I’ll optionally chain it this way. 

if let endPosition = self.index(index + length - 1){

}

Then I should be able to return the range.  

 return self[startPosition...endPosition]

Except that gets me an error. When you take something from a String range, you don’t get a string, but a substring. Substrings are excellent for memory allocation but extremely unstable, since they are really a set of pointers to the sub string within the parent string. As soon as possible with a substring, instantiate it as a string. 

return String(self[startPosition...endPosition])

Now to test all this with a string with extended grapheme clusters

print(yummy.midString(from: 3, length: 3))

And I get

I’ll change this to an invalid length

print(yummy.midString(from: 3, length: 35))

Which returns nil. That works as we wanted. 

Prefix and Suffix

Ranges work in strings to get you substrings. If you want the beginning or end of a string, you use prefix and suffix, which also return substrings. 

I can use

 print(yummy.prefix(3))

to get the substring

Or I can use 

print(yummy.suffix(3)

And get the substring.

If I overflow these, 

yummy.suffix(35)
yummy.prefix(35)

I get the full string, but as a substring. All Of what I did here is on grapheme clusters. Try one with both

yummy.suffix(5).prefix(1)

returns

There are other ways of breaking apart a string if you want the diacritical marks to be separate from the associated glyph, but for most applications, this is all you need. 

The Whole code

Here’s the code for the playground If you don’t want to download it from GitHub.

 var yummy = "D\u{1f369}ugh\u{20d7}n\u{20ed}uts"
 extension String{
      func index(_ position:Int)->String.Index!{
         if position < 0 || position > self.count {return nil}
         return self.index(self.startIndex, offsetBy: position)
     }
     func midString(from index:Int, length:Int)->String!{
         if let startPosition = self.index(index){
             if let endPosition = self.index(index + length - 1){
                 return String(self[startPosition...endPosition])
             }
         }
         return nil
     }
     
 }
 

 //Now to test all this with a string with extended grapheme cluster
 yummy
 yummy.midString(from: 3, length: 3)
  yummy.midString(from: 3, length: 35)
 

 yummy.prefix(3)
  yummy.suffix(3)
  yummy.prefix(35)
  yummy.prefix(35)
  yummy.suffix(5).prefix(1)
  
  
  
  
  
 

Launch an Alert from a Closure Safely

Your app can get into problems when you launch UI from others threads, such as closures. 

For example, you  might have an app that going to ask for permissions for things like photos, notifications, or location data. The system usually handles those, but you might want to be even more exclusive on what to use, and use your own alert. In this tip, I’ll show you how to launch an alert from a closure. 

I’ll use an slightly modified exercise file from the iOS and watchOS App Development:Notifications course I have in the LinkedIn Learning library. If you haven’t seen the course yet, notifications need permissions, and in that exercise file I show how to set up permissions. Since the demo app requires notifications, if the user prohibits notifications, I want the app to tell them that’s a problem. 

I’ll use this method I wrote in an earlier tip to launch the settings

let settingsAction = UIAlertAction(title: "Settings", style: .default) { (action) in
            if let appSettings = URL(string: UIApplication.openSettingsURLString) {
                UIApplication.shared.open(appSettings, options: [:], completionHandler: nil)
            }
        }

I’ll use this alert in my user permissions. I have two actions in my app that send notifications. Before they do, they check if they are allowed to using the getNotificationSettings method. That method  runs  a closure. In that closure, add the  code to run the alert if denied or not determined on schedulePizza and makePizza UIActions.

if status == .denied || status == .notDetermined{
    self.accessDeniedAlert()
    return
}

I’ll erase content and settings on my iPhone XR simulator to have a clean slate for permissions. Once it is ready, I’ll run the app. 

I’ll get the permissions alert from the system. I’ll hit Don’t allow, and try to schedule a pizza. We get the alert.

However, look at the console in Xcode.

2019-06-19 06:42:58.245095-0500 Huli Pizza Notification[14948:369926] [Assert] Cannot be called with asCopy = NO on non-main thread.

You have an error message, one I would not ignore. 

The app is running a UI thread of an alert on a non UI thread. The closure is creates a new thread, but alerts really need to be on the main thread where all the User interface is. There’s a huge problem with this that might not be easy to track if you are not aware of it. Because this alert is not running on the main thread, the main thread barrels on, running code without the correct settings. You can get very confused tracking down what happened with that. 

You must run UI on the main thread so the rest of the system responds correctly. Fortunately that is easy. Just state what code you want running on the main thread.   You can assign something to the main thread with he main singleton of the DispatchQueue class. I’ll change the code for a denied permission in schedulePizza and makePizza to this:

if status == .denied || status == .notDetermined{
    DispatchQueue.main.async {
        self.accessDeniedAlert()
    }
    return
}

Clean and Run again, and now we don’t get the error message.  I’m using an example from a notification, but anywhere you plan to call an object on the main thread, such as presenting an alert or changing a label, but doing so from a closure, make sure you assign the object to the main thread before doing so.  

The Not So Whole Code

This week’s project has a big one backing it, so I’m only going to show you the view controller here. Download the project from Github for the full project.

//
//  ViewController.swift
//  Huli Pizza Notification
//
//  Created by Steven Lipton on 11/23/18.
//  Copyright © 2018 Steven Lipton. All rights reserved.
//

import UIKit
import UserNotifications
// a global constant
let pizzaSteps = ["Make pizza", "Roll Dough", "Add Sauce", "Add Cheese", "Add Ingredients", "Bake", "Done"]


class ViewController: UIViewController {
    var counter = 0
   
    @IBAction func schedulePizza(_ sender: UIButton) {
        UNUserNotificationCenter.current().getNotificationSettings { (settings) in
            let status = settings.authorizationStatus
            if status == .denied || status == .notDetermined{
                DispatchQueue.main.async {
                    self.accessDeniedAlert()
                }
                return
            }
            self.introNotification()
        }
    }
    
    
    @IBAction func makePizza(_ sender: UIButton) {
        UNUserNotificationCenter.current().getNotificationSettings { (settings) in
            let status = settings.authorizationStatus
            if status == .denied || status == .notDetermined{
                DispatchQueue.main.async {
                   self.accessDeniedAlert()
                }
                return
            }
            self.introNotification()
        }
        
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        navigationController?.isNavigationBarHidden = true
    }

    //MARK: - Support Methods
    
    // A function to print errors to the console
    func printError(_ error:Error?,location:String){
        if let error = error{
            print("Error: \(error.localizedDescription) in \(location)")
        }
    }
    
    //A sample local notification for testing
    func introNotification(){
        // a Quick local notification.
        let time = 15.0
        counter += 1
        //Content
        let notifcationContent = UNMutableNotificationContent()
        notifcationContent.title = "Hello, Pizza!!"
        notifcationContent.body = "Just a message to test permissions \(counter)"
        notifcationContent.badge = counter as NSNumber
        //Trigger
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: time, repeats: false)
        
        //Request
        let request = UNNotificationRequest(identifier: "intro", content: notifcationContent, trigger: trigger)
        //Schedule
        UNUserNotificationCenter.current().add(request) { (error) in
            self.printError(error, location: "Add introNotification")
        }
    }
    //An alert to indicate that the user has not granted permission for notification delivery.
    func accessDeniedAlert(){
        // presents an alert when access is denied for notifications on startup. give the user two choices to dismiss the alert and to go to settings to change thier permissions.
        let alert = UIAlertController(title: "Huli Pizza", message: "Huli Pizza needs notifications to work properly, but they are currently turned off. Turn them on in settings.", preferredStyle: .alert)
        let okayAction = UIAlertAction(title: "Dismiss", style: .default, handler: nil)
        let settingsAction = UIAlertAction(title: "Settings", style: .default) { (action) in
            if let appSettings = URL(string: UIApplication.openSettingsURLString) {
                UIApplication.shared.open(appSettings, options: [:], completionHandler: nil)
            }
        }
        alert.addAction(okayAction)
        alert.addAction(settingsAction)
        present(alert, animated: true) {
        }
    }
}

Change UIColors to RGB and HSB colors

One dilemma you’ll find when working with colors is switching between color systems. There’s two you’ll most often be using: the Red-Green-Blue or RGB and Hue-Saturation-Brightness or HSB. Download the Exercise file and run. It will give you the HSB value, but what if you want a RGB Value for that color? What if you want the values for any UIColor? Let’s look at some special methods for that. 

In the exercise files, go to  ColorModel.swift and the ColorEntry class. I’ll add some methods here to do all this.  

I stubbed a method  rgbString here for  you with a few variables: 

func rgbString()->String{
        var rgb = ""
        var red:CGFloat = 0
        var green:CGFloat = 0
        var blue:CGFloat = 0
        var alpha:CGFloat = 0
        return rgb

There’s a UIColor method getRed that gets you the red green, blue and alpha components of a color.  It works a bit differently than most methods though. Its parameters are unsafe mutable pointers, and it returns a Bool if it was successful. I’ll put it in an if statement to catch any errors  like this: 

if !color.getRed(&red, green: &green, blue: &blue, alpha: &alpha){
           
        }

Unsafe pointers work a little backward. You put a strong  pass-through variable for the parameter prefixed by the ampersand (&). I invert the bool here so I’ll deal with error conditions in the if clause. I’ll just return the blank rgb value in this case, which

if !color.getRed(&red, green: &green, blue: &blue, alpha: &alpha){
return rgb
}

If successful, I’ll make a string to return the RGB color as a String

rgb = String(format:"R:%04.3f G:%04.3f B:%04.3f",red,green,blue)

I can do the same  with HSB Colors wit the getHue method. I already have properties for this in my model, I just need to add the alpha. The code for this is very similar. 

func hsbString()->String{
        var hsb = ""
        var alpha:CGFloat = 0
        if !color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha){
            return hsb
        }
        
        hsb = String(format:"H:%04.3f S:%04.3f B:%04.3f",hue,saturation,brightness)
        return hsb
    }

I can now set my color name by these two strings. I’ll make an initializer that creates the name from the strings: 

init(color:UIColor){
        self.color = color
        self.name = hsbString() + "\n" + rgbString()
    }

I’ll also change the name on my first initializer to include both: 

init(name:String,color:UIColor){
        self.color = color
        self.name = name + "\n" + hsbString() + "\n" + rgbString()
    }

I don’t need to use the first initializer in the hues method of color model, I can use the second, so I’ll change that: 

//let name = String(format:"H:%04.3f S:1.0 B:1.0 ",hueValue)
let colorEntry = ColorEntry(color: color)

Run the app. You’ll see the table has changed. When you select a color, both values show up. 

You can use these methods for testing colors in many places, including pixels in images. While a little clunky with he unsafe mutable pointers, this is a relatively easy way to get get colors from one system to another. 

The (not so) Whole Code

I’ve been using the same code for the last few weeks. You can go to the last lesson to get everything but the changes made to ColorModel.swift, which is below. You can also download the completed project from Github.

//
//  ColorModel.swift
//  ColorPicker
//
//
//  An exercise file for iOS Development Tips Weekly
//  by Steven Lipton (C)2018, All rights reserved
//  For videos go to http://bit.ly/TipsLinkedInLearning
//  For code go to http://bit.ly/AppPieGithub
//

import UIKit

class ColorEntry{
    var name:String = ""
    var color:UIColor
    var hue:CGFloat = 0.0
    var brightness:CGFloat = 0.5
    var saturation:CGFloat = 1.0
    
    init(name:String,color:UIColor){
        self.color = color
        self.name = name + "\n" + hsbString() + "\n" + rgbString()
    }
    
    init(color:UIColor){
        self.color = color
        self.name = hsbString() + "\n" + rgbString()
    }
    
    
    func rgbString()->String{
        var rgb = ""
        var red:CGFloat = 0
        var green:CGFloat = 0
        var blue:CGFloat = 0
        var alpha:CGFloat = 0
        
        if !color.getRed(&red, green: &green, blue: &blue, alpha: &alpha){
            return rgb
        }
        
        rgb = String(format:"R:%04.3f G:%04.3f B:%04.3f",red,green,blue)
        return rgb
    }
    
    func hsbString()->String{
        var hsb = ""
        var alpha:CGFloat = 0
        if !color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha){
            return hsb
        }
        
        hsb = String(format:"H:%04.3f S:%04.3f B:%04.3f",hue,saturation,brightness)
        return hsb
    }
    
}


class ColorModel{
    var colors = [ColorEntry]()
    init(){
        colors = []
    }
    func hues(count:Int)->[ColorEntry]{
        colors = []
        if count <= 0 {return colors}
        for hue in 0...count{
            let hueValue = CGFloat(hue)/CGFloat(count)
            let color = UIColor(hue: hueValue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
            //let name = String(format:"H:%04.3f S:1.0 B:1.0 ",hueValue)
            let colorEntry = ColorEntry(color: color)
            colors += [colorEntry]
        }
        return colors
    }
    
    func lightnessScale(hue:UIColor,count:Int){
        
    }
    
}