How to Make Keyboard Shortcuts in SwiftUI

As of WWDC 2020, SwiftUI has made it insanely easy to add keyboard shortcuts to any SwiftUI app. Let’s look at how we can add keyboard shortcuts to our project by looking at the code example below.

struct ContentView: View {
    var body: some View {
        
        Button(action: {
            print("Button Tapped!!")
        }) {
            Text("Button")
        }.keyboardShortcut("T")
        
    }
}

As you can see in the example above all we needed to do was add the .keyboardShortcut() modifier to our button and assign it a key. Now by holding the command button on our external keyboard and pressing “T”, our action for our button will be triggered.

We can customize our shortcut by modifying which buttons need to be pressed.

.keyboardShortcut("T", modifiers: .control)

In the example above if we want to trigger our new modified shortcut we need to press the control button and the “T” button.

We can also use different combinations of modifier keys in an array.

.keyboardShortcut("T", modifiers: [.command, .control])

Like in the example before we need to hold the control and “T” key but now we also need to hold the command key to get our shortcut to work.

We can also use other keyboard keys like directional keys, spacebar, and many more.

.keyboardShortcut("T", modifiers: [.command, .shift, .space])

Also be mindful that system keyboard shortcuts like cut, copy, and paste take priority over app shortcuts. Keep this in mind when assigning your keyboard shortcuts.

I hope this helps you to expand keyboard shortcuts to your SwiftUI app!

⌨️ 🩳 πŸ’‡β€β™‚οΈ Happy Coding ⌨️ 🩳 πŸ’‡β€β™‚οΈ

How to use LazyVGrid and LazyHGrid in SwiftUI

As of iOS 14, SwiftUI now gives developers two new UI components called LazyVGrid and LazyHGrid. LazyVGrid is used for creating vertical grids and LazyHGrid is used for horizontal grids. The keyword Lazy means that the grids view does not create items for the grid until they are needed to appear on screen. This allows our app to have much faster loading times when using grids!

In this post we are going to look at how to make a LazyVGrid. All the examples in this post can be applied to LazyHGrid.

There are three things we need to make a LazyVGrid.

  1. We need a list of data to display
  2. We need a GridItem. This defines how we want the layout to look
  3. We need to choose if we want our grid to be vertical or horizontal
    private var gridLayout = [
        GridItem(.flexible()),
        GridItem(.flexible())
    ]

Above we are creating the gridLayout variable which is an array of GridItem‘s. We will use this variable to set the layout we want for our grid. Later we will talk about the .flexible() but for now don’t worry.

In this example we want two columns in our grid. The more GridItem‘s we add to the array will mean more columns of our data to be displayed on the screen. We can add as many columns as our display can handle but for this example we are going to use two columns.

Next we need to add a ScrollView and our LazyVGrid.

    private var gridLayout = [
        GridItem(.flexible()),
        GridItem(.flexible())
    ]
    
    var body: some View {
        
        ScrollView {
            LazyVGrid(columns: gridLayout, spacing: 20) {
                ForEach((1...100), id: \.self) {
                    Text("\($0)")
                        .font(.title)
                        .foregroundColor(Color.white)
                        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 50)
                        .background(Color.red)
                }
            }
            .padding(.all, 10)
        }
    }

In the above code we are applying our gridLayout variable to our LazyVGrid with a spacing of 20. Then we are adding a ForEach statement that will display numbers in a text view from 1 to 100 and we added a little styling to the text view.

Now if we run the code above we should get two columns of numbers in a vertical grid like in the image below.

In the above example we used the flexible lazy grid style. This flexible style allows us to specify how big we want each item to be and also lets us control of how many columns we want to display. There are two other types of grid styles called fixed and adaptive.

The fixed grid type will make any of our column’s fixed to a particular width size. To show this in an example we are going to run the same code as above but switch our first GridItem style.

    private var gridLayout = [
        GridItem(.fixed(100)),
        GridItem(.flexible())
    ]

You can now see that our first GridItem in our grid will always have a fixed width of 100, while the flexible style on the second GridItem will take up the rest of the row space.

Next we have the adaptive grid layout which will take a minimum width and display as many rows as it can on the screens layout.

    private var gridLayout = [
        GridItem(.adaptive(minimum: 100)),
    ]

All of these same methods for customizing LazyVGrid can be used when creating LazyHGrid. A reminder you can only use this style of lazy grids in apps supporting iOS 14 and above.

Hope this helps you to be more lazy with your grids!

πŸ˜΄πŸ›Œ Happy Coding πŸ˜΄πŸ›Œ

How to Detect Dark Mode in SwiftUI

SwiftUI makes it really simply to detect when dark mode is enabled. We simply have to add a @Enviroment variable and use .colorScheme property to scan the settings on our device and see if dark mode is enabled.

Let’s take a look at the example below.

struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme

    var body: some View {
        ZStack {
            Color(colorScheme == .light ? .blue : .red)
            Text("Hello, World!")
        }
    }
}

In the code above we are creating the @Environment variable to see if our device is in dark mode. Then inside of our body view we are setting the background color to red if its in dark mode or blue if its not in dark mode by using our colorScheme variable inside of a ternary operator.

A great use case for this is if you want to support different custom UI’s for when the users device is in dark mode.

πŸŒƒ Happy Coding! πŸŒƒ

How to Set an Image as a WatchOS Complication

The reason I’m writing this post is because I had submitted a WatchOS side project I was working on (Status of Github) to the app store. The problem was that I submitted the app with all complications activated but had no image or placeholders for the complications (How embarrassing πŸ€¦πŸ»β€β™‚οΈ). So let’s jump into the project and fix this issue by setting a image to our complications.

Let’s get started by going into the WatchOS project folder at the top of our file navigation.

We need to select the General tab at the top and then scroll down to the Complication Configuration section.

Next we will select which complications we want to support for our project.

Now we will need to add the image we want to use to the Assets folder in the WatchKit Extension part of our WatchOS app.

In the Assets folder you will see another folder named Complication.

In this folder we will add the image that we want to show for the complication types we chose to support in the Complication Configuration.

I would recommend checking out Apple’s Human Interface Guidelines to get the right dimensions for each complication so we have the right size image to display.

Now we will move on to the Complication Controller file where we will set the image from our assets folder to our complications.

    func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
        // Call the handler with the current timeline entry

        if complication.family == .circularSmall {
            let template = CLKComplicationTemplateCircularSmallRingImage()
            guard let image = UIImage(named: "Complication/Circular") else { handler(nil); return}
            template.imageProvider = CLKImageProvider(onePieceImage: image)
            let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
            handler(timelineEntry)
            
        } else if complication.family == .utilitarianSmall {
            let template = CLKComplicationTemplateUtilitarianSmallRingImage()
            guard let image = UIImage(named: "Complication/Utilitarian") else { handler(nil); return}
            template.imageProvider = CLKImageProvider(onePieceImage: image)
            let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
            handler(timelineEntry)
            
        } else if complication.family == .modularSmall {
            let template = CLKComplicationTemplateModularSmallRingImage()
            guard let image = UIImage(named: "Complication/Modular") else { handler(nil); return}
            template.imageProvider = CLKImageProvider(onePieceImage: image)
            let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
            handler(timelineEntry)
            
        } else if complication.family == .graphicCircular {
            let template = CLKComplicationTemplateGraphicCircularImage()
            guard let image = UIImage(named: "Complication/GraphicCircular") else { handler(nil); return}
            template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
            let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
            handler(timelineEntry)
            
        } else if complication.family == .graphicCorner {
            let template = CLKComplicationTemplateGraphicCornerCircularImage()
            guard let image = UIImage(named: "GraphicCorner") else { handler(nil); return}
            template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
            let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
            handler(timelineEntry)

        } else {
            handler(nil)
        }

    }

In the code above we created a image template for each of the complications. Then we set our image to the template. Lastly we set our temple to a complication timeline entry so that it can be displayed to the watch face.

Now let’s run our code in the simulator and go to the watch face home screen.

Here we can press and hold on the screen to customize the watch face and set our new complication.

Now that our complication is on our watch face, we can simply tap on our complication and we will be take directly into our app!

I hope this helps you from making the same mistake I made!

If you want to learn more about complications in WatchOS I highly recommend checking out this WWDC15 video called Creating Complications with ClockKit.

⌚️ Happy Coding! ⌚️

Disclosure Groups in SwiftUI

At WWDC 2020 disclosure groups were announced for SwiftUI. Disclosure groups are very similar to how drop down menus work on the Mac. Apple’s documentation defines it as “A view that shows or hides another content view, based on the state of a disclosure control”.

Before we get started on building a disclosure group you will need to have Xcode 12 installed and be running your project for iOS 14 or newer.

In this post we will be using disclosure groups to help us build parts of a settings menu in a app.

Let’s open a new SwiftUI project in Xcode and get started.

The first thing we will need for our disclosure group is a @State variable so we can keep track of if our disclosure view is expanded or not.

@State private var isSettingsExpanded: Bool = false

Next we need to add our disclosure group to our body view.

@State private var isSettingsExpanded: Bool = false
    
var body: some View {
        DisclosureGroup("Settings", isExpanded: $isSettingsExpanded) {
            Text("Terms of service")
        }
        .padding()
    }

If we go and run this code you should see a disclosure group labeled “Settings” and if we tap on the arrow we should see a drop down that has our “Terms of Service” text view.

What’s really cool is that we can nest Disclosure groups as well.

@State private var isSettingsExpanded: Bool = false
@State private var isTermsOfServiceExpanded: Bool = false

var body: some View {
    DisclosureGroup("Settings", isExpanded: $isSettingsExpanded) {
        DisclosureGroup("Terms of Service", isExpanded: $isTermsOfServiceExpanded) {
            Text("NO ONE READS THESE THINGS!")
        }
    }
    .padding()
}

Above we added another state property and a disclosure group like we did before. Now we have our “Terms of Service” disclosure view nested to our parent “Settings” disclosure view.

We also are not limited to just using text, we can also use any kind of view in our disclosure group. For example maybe we want to turn on and off dark mode in our app. We can simply add a toggle to our disclosure group.

@State private var isDarkModeOn = true
@State private var isSettingsExpanded: Bool = true

var body: some View {
    DisclosureGroup("Settings", isExpanded: $isSettingsExpanded) {
        Toggle("Dark Mode", isOn: $isDarkModeOn)
    }
    .padding()
}

With just a few lines of code we can have all kinds of information being displayed or hidden from the user.

I hope this brief look into disclosure groups helps you to build amazing SwiftUI apps!

Happy Coding!

How to Style an Image in SwiftUI

In this post we are going to take a look at how to customize images in SwiftUI.

First thing we need to do is open a new SwiftUI Xcode project and add an image to our assets folder. I downloaded a random image of a husky as my image asset but feel free to use any image you would like.

Next we will set our image in our ContentView.swift file like the example below.

struct ContentView: View {
    var body: some View {
        Image("husky")
    }
}

Now that we have our image set let us customize it so that it fits our screen better. We can start by resizing our image using the .resizeable() modifier.

struct ContentView: View {
    var body: some View {
        Image("husky")
            .resizable()
    }
}

Now the image fits the screen but we definitely want the image to look a little less crammed on screen. We can fix this by adding the .scaledToFit() modifier. This allows the image to fit the screen without making the image look crammed.

struct ContentView: View {
    var body: some View {
        Image("husky")
            .resizable()
            .scaledToFit()
    }
}

We can also resize the frame of our image by add the .frame() modifier.

struct ContentView: View {
    var body: some View {
        Image("husky")
            .resizable()
            .scaledToFit()
            .frame(width: 300.0, height: 300.0)
    }
}

We can also clip our image into a shape. In this example we are going to clip out image into a circular shape like how many apps show profile pictures. SwiftUI makes this real easy by adding the .clipShape(Circle()) modifier.

struct ContentView: View {
    var body: some View {
        Image("husky")
            .resizable()
            .scaledToFit()
            .clipShape(Circle())
    }
}

These are some of the ways we can manipulate images in SwiftUI.

As a recap we learned how to use .resizeable() modifier which allows us to resize the aspect ratio of the image. We also used .aspectRatio() or the .scaledToFit() modifier to fill the content of our image so it will look proper on screen. Lastly we learned how to resize the frame of an image and saw how we can clip images into shapes like we did with the circle image.

I hope this helps to show how powerful and easy it is to customize images in SwiftUI.

πŸ“·πŸŒ‰ Happy Coding!! πŸŒ‰πŸ“·

How to Customize Text in SwiftUI

In this post we are going to take a quick look at how to style text in SwiftUI.

If we want to change the font of our text we can simply add the .font modifier.

Above you can see Swift has preset options of font types like title, body, large title and many more. We also have the option of using our own custom fonts like the example below.

.font(.custom("Helvetica", size: 15))

Let’s say we want to change the thickness or thinness of our text. We can do this by using the .fontWeight() modifier.

We can also change the color of the font by simply using .foregroundColor() modifier. This allows us to select the color of our text lettering.

You can also change the background color of the text view by using .backgroundColor() modifier.

Text("Warning")
    .background(Color.red)
    .foregroundColor(.white)

Lastly we can also change how far the spacing is between multiple lines by using .lineSpacing and .lineLimit.

The .lineSpacing modifier allows us to make the lines shorter or longer between line breaks and the .lineLimit modifier allows us to limit the lines in a text view.

.lineLimit(3)
.lineSpacing(10)

These are the basics on how to manipulate text so we can customize them to our liking.

I hope this helps you to better understand how to customize text in SwiftUI.

πŸ‘¨πŸ»β€πŸ’»πŸ’» Happy Coding!! πŸ‘¨πŸ»β€πŸ’»πŸ’»

What is @escaping and @nonescaping in a Swift closure?

A Closure is a block of code that can pass around data. Closure’s are similar to functions but have special attributes. One feature of what makes closure’s special is that the block of code inside the closure can live passed the function they are called in. It does this by retaining a copy of the closures code in memory until it finishes executing. More about that in a bit, let’s look at an example of a simple closure below.

let greetingClosure = { (name:String) -> String in  
    return "Hello \(name)!"  

}  

let message = greetingClosure("Swift Tom")  

print(message)

// Output: Hello Swift Tom!

In the example above we have a closure variable that takes in a parameter and returns a string. When the example above is called its value is assigned to our closure variable and then printed out.

Now that we have seen a basic closure let’s look at when to use @escaping and @nonescaping in a closure.

A common example of a @escaping closure would be a completion handler. Completion handlers run asynchronous tasks like a network call. Let’s look a example of a network call below.

func performRequest(parameters: [String: String], completionHandler: @escaping (Result<Data, Error>) -> Void) {
   
 var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try! JSONEncoder().encode(parameters)

    let task = URLSession.shared.dataTask(with: request) { data, _, error in
        guard let data = data else {
            completionHandler(.failure(error!))
            return
        }
        completionHandler(.success(data))
    }
    task.resume()
}

The @escaping allows for the closure to be retained in memory until the completion handler either returns success on the network call or a failure. The @escaping outlives the function it was created in to retrive the data we are requesting.

A @nonescaping closure is the opposite of @escaping and doesn’t live past the function it was called in. Below is an example of a @nonescaping closure.

func calculateSumOf(array:[Int],  handler: ((Int)->Void)) {  
        var sum: Int = 0  
        for value in array {  
            sum += value  
        }  
            handler(sum)  
    }  

func getSum() {  
        self.calculateSumOf(array: [22, 18, 9, 50]) { [weak self] sum in  
        
        print(sum)  
        // finishing the execution  
        }  
    } 

getSum()
//Output: 99

In the above example we can see that the function getSum( ) will calculate the sum of the values passed into the array. Once the function finishes running the closure will not be retained memory.

All closures by default are @nonescaping closure’s. The first example I used in this post is also a @nonescaping closure.

The reason for all closures being @nonescaping by default is performance and code optimization for the compiler. If the compiler knows that the closure is @nonescaping it can manage the memory allocation in the closure much faster because it doesn’t have to retain memory until it is returned like in a @escaping closure.

If you want to learn more about closures and how to use them checkout the excellent Swift documentation on closures.

Hope this helps to explain escaping and non-escaping closure.

␛ Happy Coding πŸš«β›

What are Optionals in Swift

A optional in Swift is the handling of nil in a variable. We check to see if our value is nil by a process we call unwrapping. Similar to a box that came in the mail, we have no idea if the box has anything inside or if it has nil inside. The only way to find out is by unwrapping it. Let’s look at a example to see how we can handle unwrapping optionals.

import UIKit

var studentScores: [Int] = [81, 77, 65, 93, 55]
studentScores.sort()

// if let
if let topScore = studentScores.last {
    // If topScore has a value
    print("The top score in the class is \(topScore)")
} else {
    // If nil
    print("There are no student scores")
}



// guard let
func getTopScore() {
    // If topScore is nil break out of the function
    guard let topScore = studentScores.last else {
        return
    }
    print(topScore)
}



// nil coalescing
    // allows us to set a default value of the same type if nil
let topScore = studentScores.last ?? 100



// force unwrap
let highestScore = studentScores.last!

In the above example we are trying to see what the students top score was in the class. Let’s pretend that we don’t know if the studentScores array is nil. In the example we use the 4 main ways to handle unwrapping optionals in Swift.

The first way we handle an optional value is by using a if let statement. A if let statement allows us to check the variable and then handle the value in a if else statement.

The second way is using a guard let statement. A guard let statement can only be used inside of a function. This way of checking an optional is like a line in the sand because if the guard let statement finds that the variable is nil it will no longer run the code inside of the function.

The third method of unwrapping is called nil coalescing. This allows us to set a default value of the same type if the value is nil.

Our last way of unwrapping an optional is by force unwrapping. This is dangerous to do because if the value turns out to be nil your app will crash. You should only force unwrap if you know the value can never be nil.

Hope this helps you to better understand optionals in Swift.

πŸ‘¨πŸ»β€πŸ’» Happy Programming! πŸ‘¨πŸ»β€πŸ’»

What are Life Cycle Functions in SwiftUI?

If you have done any development with UIKit you have used a life cycle function. These UIKit life cycle functions would be viewDidLoad, viewWillAppear, viewWillDisappear, and viewDidDisappear. In SwiftUI we lose these life cycle functions because there are no more ViewControllers in SwiftUI, only views. SwiftUI replaces these functions with two methods called onAppear( ) and onDisappear( ).

onAppear( ): is a function that runs every time a view appears on screen. The onAppear( ) function is similar to the viewWillAppear function used in UIKit. A good example of how we can use the onAppear( ) function is for any network calls we would need for that view.

onDisappear( ): is a function that runs when a view disappears from our apps screen. This function is similar to the viewWillDisappear function in UIKit. A example for when to use the onDisappear( ) function is for clearing any data before dismissing that view.

By using the onAppear( ) and onDisappear( ) functions in SwiftUI we can achieve similar behavior and functionality to the UIKit lifecycle functions.

Thanks for reading and I hope this helps in your learning of SwiftUI!

πŸ‘¨πŸ»β€πŸ’» Happy Programming πŸ‘¨πŸ»β€πŸ’»