How to Use SF Symbols in a SwiftUI project

SF Symbols are set of over 2,000 symbols created by Apple for developers to use in there apps. SF Symbols are dynamic. This means that they will automatically align with the size and weight of text in our app. We can use these symbols for almost anything but they are most commonly used in toolbars, menus, and navigation bars.

We can use SF Symbols in our project by using the Image(systemName:) image view. In the system name parameter we will add the string of the SF Symbol we want to use.

Image(systemName: “cloud.sun.fill”)

As you can see in the example above we added the “cloud.sun.fill” SF Symbol to our image view.

We can also customize SF Symbols. If we want to change something like the color of our SF Symbol all we need to do is change the .foregroundColor(). We can also change the size of our symbol by changing the .font size.

Image(systemName: “cloud.heavyrain.fill”)
.foregroundColor(.green)
.font(.title)

That was a quick look at how we can add SF Symbols to our SwiftUI projects. For more info checkout Apples Human Interface Guidelines on how you should use SF Symbols in our projects.

Thanks for reading and Happy Coding!

Access Control in Swift

What is Access Controls in Swift? According to the documentation “Access control restricts access to parts of your code from code in other source files and modules. This feature enables you to hide the implementation details of your code, and to specify a preferred interface through which that code can be accessed and used.” We can implement access controlS by adding five different types of modifiers to our code. These modifiers are:

  • Public: Which means anyone can read and write to this property.
  • Internal: This is the default access control for any property we create. This allows us access within the same module.
  • File Private: Like the name says, this modifier allows us to only access this property within the same file.
  • Private: This is the most restrictive. This only allows access within the current enclosing it is being used in.
  • Open: This is the most unrestricted. This property can be accessed from anywhere and other developers can subclass it and/or override it.

If you would like a deeper dive into Access Control check out the Swift Documentation on it.

Thanks for reading and I hope this gives you a better understand of when to use Access Controls in Swift.

Happy Coding!!!

How to Make Static and Dynamic Home Screen Quick Actions in SwiftUI

Home screen quick actions are shortcuts for users to navigate into different parts of our app. In this post we are going to see how we can add these quick actions into our SwiftUI project. The two different types of quick actions we can use is static quick actions and dynamic quick actions. Let’s start by taking a look at how we can add static quick actions to our project.

As the name implies static quick actions do not change. If we want to add static quick actions to our project we will need to implement it in our info.plist. If we right click on the info.plist and go to >  Open As > Source Code and at the bottom before the last </dict> add the following code below.

	<key>UIApplicationShortcutItems</key>
	<array>
		<dict>
			<key>UIApplicationShortcutItemType</key>
			<string>AddAction</string>
			<key>UIApplicationShortcutItemIconType</key>
			<string>UIApplicationShortcutIconTypeAdd</string>
			<key>UIApplicationShortcutItemTitle</key>
			<string>Add</string>
			<key>UIApplicationShortcutItemSubtitle</key>
			<string>Add Item</string>
		</dict>
	</array>

In the code above we are using UIApplicationShortcutItems to create an add button for our quick action shortcuts. Our shortcut items can be found inside an array of a dictionary. In each dictionary a UIApplicationShortcutItemType and UIApplicationShortcutItemTitle are needed to make our static shortcut. UIApplicationShortcutItemType is a string that represents what kind of quick action we want in our app. UIApplicationShortcutItemTitle is the title or name of our quick action button.

If you want to learn more about UIApplicationShortcutItem‘s check out the Apple documentation.

Now if you run the app and then go to the home screen we can test our quick action. On the home screen if we tap and hold on our app icon we should see the “Add” quick action button pop up on our home screen.

Now that we made a static quick action let’s move on to adding a dynamic quick action button.

To make a dynamic quick action we will need to add them in the App struct also known as the entry point of our app. This file can be located in the navigator and is usually named after your project name followed by the word App. In this example it will be in the QuickActionSwuiftUIBlogApp.swift file.

The reason we need to create our quick action buttons in this file is because these buttons need to be made at specific point in the apps life cycle. Apples documentation recommends that we create dynamic quick actions when the app is moving to the background.

import SwiftUI

@main
struct QuickActionSwuiftUIBlogApp: App {
    @Environment(\.scenePhase) var lifeCycle
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: lifeCycle) { (newLifeCyclePhase) in
            switch newLifeCyclePhase {
            case .active :
                print("App is active")
            case .inactive:
                print("App is inactive")
            case .background:
                print("App is going to the Background")
                // This will add our dynamic quick actions when our app is sent to the background on our device
                addQuickActions()
            @unknown default:
                print("default")
            }
        }
    }
    
    func addQuickActions() {
        UIApplication.shared.shortcutItems = [
            UIApplicationShortcutItem(type: "Tweet", localizedTitle: "Tweet"),
            UIApplicationShortcutItem(type: "Call", localizedTitle: "Call"),
            UIApplicationShortcutItem(type: "Message", localizedTitle: "Message"),
            UIApplicationShortcutItem(type: "Saved", localizedTitle: "Saved"),
        ]
    }
}

As you can see in the code above we created some dynamic shortcuts buttons in our addQuickActions() function. We call the addQuickActions() function to make our dynamic buttons when our app goes into the background state. We know when our app goes to the background by using the .onChange modifier with our @Environment(\.scenePhase) var lifeCycle variable. For a deeper dive please check out the Apple documentation.

Now if we rerun our app and long press on our app icon we should see our static quick action button with our dynamic quick action buttons underneath. You may also notice that one of our quick buttons doesn’t show up in the quick action sheet. This is because Apple will only show a maximum of 4 quick actions at a time.

Next we will go back into our addQuickActions() function and add some more code.

    func addQuickActions() {
        var tweetuserInfo: [String: NSSecureCoding] {
                    return ["name" : "tweet" as NSSecureCoding]
                }
                var calluserInfo: [String: NSSecureCoding] {
                    return ["name" : "call" as NSSecureCoding]
                }
                var messageuserInfo: [String: NSSecureCoding] {
                    return ["name" : "message" as NSSecureCoding]
                }
                var saveduserInfo: [String: NSSecureCoding] {
                    return ["name" : "saved" as NSSecureCoding]
                }
        
        UIApplication.shared.shortcutItems = [
            UIApplicationShortcutItem(type: "Tweet", localizedTitle: "Tweet", localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .compose), userInfo: tweetuserInfo),
            UIApplicationShortcutItem(type: "Call", localizedTitle: "Call", localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .audio), userInfo: calluserInfo),
            UIApplicationShortcutItem(type: "Message", localizedTitle: "Message", localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .message), userInfo: messageuserInfo),
            UIApplicationShortcutItem(type: "Saved", localizedTitle: "Saved", localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .love), userInfo: saveduserInfo),
        ]
    }

The example above will help us to identify each quick action button with a name key. This will allow us to know which of our quick actions was pressed this way we can have our app react according to what quick action the user has tapped. We will attach these new variables we created to the userInfo parameter of our UIApplicationShortcutItem. Above we also added icons to the shortcut items so we don’t just have dots as icon placeholders.

Now let’s add two global variables above our @main in our App file. These variables will help us to pass our users input along to different areas of our app depending on which quick action we press.

var shortcutItemToHandle: UIApplicationShortcutItem?
let quickActionSettings = QuickActionSettings()

Next we will need to add two more functions below our addQuickActions() function.

    class AppDelegate: NSObject, UIApplicationDelegate {
        func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
            if let shortcutItem = options.shortcutItem {
                shortcutItemToHandle = shortcutItem
            }
            
            let sceneConfiguration = UISceneConfiguration(name: "Custom Configuration", sessionRole: connectingSceneSession.role)
            sceneConfiguration.delegateClass = CustomSceneDelegate.self
            
            return sceneConfiguration
        }
    }

    class CustomSceneDelegate: UIResponder, UIWindowSceneDelegate {
        func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
            shortcutItemToHandle = shortcutItem
        }
    }

In the code above we added an Appdelegate method and a Scenedelegate method to handle what happens to our quick action buttons at different parts of the life cycle of our app. What these two class methods are doing is handling if the app is closed we want it to open our application and if our app is in the background to handle a quick action if the user taps on it.

Now that we have set up our quick action buttons we will want them to do something when a user taps on them. We want the user to be able to navigate to the specific section they tapped on in the home screen shortcut. This means that in our newLifeCyclePhase switch statement in our .active case we will need to perform an action. Let’s take a look at how we implement this in the code below.

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(quickActionSettings)
        }
        .onChange(of: lifeCycle) { (newLifeCyclePhase) in
            switch newLifeCyclePhase {
            case .active :
                print("App is active")
                guard let name = shortcutItemToHandle?.userInfo?["name"] as? String else { return }
            case .inactive:
                print("App is inactive")
            case .background:
                print("App is going to the Background")
                // This will add our dynamic quick actions when our app is sent to the background on our device
                addQuickActions()
            @unknown default:
                print("default")
            }
        }
    }

Above we added a guard let variable to our .active case. This will check to see if we have a name attached to the button the user pressed on the quick action shortcuts.

Now that we set that up we will go into the ContentView.swift and we are going to make a simple list view that will correspond to our quick action dynamic buttons. This will also be where we setup a observable variable to see which button has been pressed from our App file and react by bringing our user to that view in the app.

Our ContentView.swift file should have the following code in it.

import SwiftUI

struct ContentView: View {
    @EnvironmentObject var quickActionSettings: QuickActionSettings
    @State var selectedAction: Int?

    var body: some View {
            NavigationView {
                List {
                    ForEach(0..<allQuickActions.count) { index in
                        NavigationLink(destination: DetailView(name: allQuickActions[index].name), tag: allQuickActions[index].tag, selection: $quickActionSettings.quickAction) {
                            Text(allQuickActions[index].name)
                        }
                    }
                }
                .listStyle(SidebarListStyle())
                .navigationBarTitle("Quick Actions")
            }
        }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

    struct QuickActionModel : Identifiable {
        let id = UUID()
        let name: String
        let tag: QuickActionSettings.QuickAction
    }

    let allQuickActions = [
        QuickActionModel(name: "Tweet", tag: .details(name: "tweet")),
        QuickActionModel(name: "Call",tag: .details(name: "call")),
        QuickActionModel(name: "Message", tag: .details(name: "message")),
        QuickActionModel(name: "Saved", tag: .details(name: "save")),
    ]

As you can see in the example we added a @EnvironmentObject to track which quick action our user has tapped from our quick actions on the home screen. We also setup a simple list view with all of our dynamic quick action buttons. We created a QuickActionModel struct with a name and a tag and created an array of quick actions so that we can segue to the detail view depending on which quick action was tapped.

Next we will need to make a new SwiftUI file called DetailView. In this file add the following code to make a simple view for our list view buttons to segue too. This detail view will just show the name of the button we selected.

import SwiftUI

struct DetailView: View {
    var name: String
    
    var body: some View {
        Text("\(name)!")
            .navigationBarTitle(name)
    }
}

Now we will make another swift file and name this one QuickActionSettings. This will be our observable object so we can keep track of which quick action was pressed.

import Foundation

class QuickActionSettings: ObservableObject {
    
    enum QuickAction: Hashable {
        case home
        case details(name: String)
    }
    
    @Published var quickAction: QuickAction? = nil
}

Here we are using a QuickAction enum to see if the name value changes. If it does change it will trigger our navigation link in our content view to segue to the quick action the user has tapped.

Lastly let’s go back to our App file and add our .enviormentObject to our ContentView() in our WindowGroup like below.

WindowGroup {
            ContentView()
                // We will use this modifier below to pass along which quick action that was pressed
                .environmentObject(quickActionSettings)
        }

We also need to update our code inside of our .active case. Here we will add another switch statement that will check the names associated with our quick actions buttons. We will then take this name variable and pass it along our quickActionSettings.quickAction so the rest of our app can update. Our final QuickActionSwuiftUIBlogApp file should look like the code below.

import SwiftUI

let quickActionSettings = QuickActionSettings()
var shortcutItemToHandle: UIApplicationShortcutItem?

@main
struct QuickActionSwuiftUIBlogApp: App {
    @Environment(\.scenePhase) var lifeCycle
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                // We will use this modifier below to pass along which quick action was pressed
                .environmentObject(quickActionSettings)
        }
        .onChange(of: lifeCycle) { (newLifeCyclePhase) in
            switch newLifeCyclePhase {
            case .active :
                print("App is active")
                guard let name = shortcutItemToHandle?.userInfo?["name"] as? String else { return }
                switch name {
                               case "tweet":
                                   print("tweet is selected")
                                   quickActionSettings.quickAction = .details(name: name)
                               case "call":
                                   print("call is selected")
                                   quickActionSettings.quickAction = .details(name: name)
                               case "message":
                                   print("message is selected")
                                   quickActionSettings.quickAction = .details(name: name)
                               case "saved":
                                   print("saved is selected")
                                   quickActionSettings.quickAction = .details(name: name)
                               default:
                                   print("default ")
                               }
            case .inactive:
                print("App is inactive")
            case .background:
                print("App is going to the Background")
                // This will add our dynamic quick actions when our app is sent to the background on our device
                addQuickActions()
            @unknown default:
                print("default")
            }
        }
    }
    
    func addQuickActions() {
        var tweetuserInfo: [String: NSSecureCoding] {
                    return ["name" : "tweet" as NSSecureCoding]
                }
                var calluserInfo: [String: NSSecureCoding] {
                    return ["name" : "call" as NSSecureCoding]
                }
                var messageuserInfo: [String: NSSecureCoding] {
                    return ["name" : "message" as NSSecureCoding]
                }
                var saveduserInfo: [String: NSSecureCoding] {
                    return ["name" : "saved" as NSSecureCoding]
                }
        
        UIApplication.shared.shortcutItems = [
            UIApplicationShortcutItem(type: "Tweet", localizedTitle: "Tweet", localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .compose), userInfo: tweetuserInfo),
            UIApplicationShortcutItem(type: "Call", localizedTitle: "Call", localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .audio), userInfo: calluserInfo),
            UIApplicationShortcutItem(type: "Message", localizedTitle: "Message", localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .message), userInfo: messageuserInfo),
            UIApplicationShortcutItem(type: "Saved", localizedTitle: "Saved", localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .love), userInfo: saveduserInfo),
        ]
    }
    
    class AppDelegate: NSObject, UIApplicationDelegate {
        func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
            if let shortcutItem = options.shortcutItem {
                shortcutItemToHandle = shortcutItem
            }
            
            let sceneConfiguration = UISceneConfiguration(name: "Custom Configuration", sessionRole: connectingSceneSession.role)
            sceneConfiguration.delegateClass = CustomSceneDelegate.self
            
            return sceneConfiguration
        }
    }

    class CustomSceneDelegate: UIResponder, UIWindowSceneDelegate {
        func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
            shortcutItemToHandle = shortcutItem
        }
    }
}

If we test our code on our home screen we should be able to long press our icon and if we tap one of the dynamic quick action buttons it will open our app and navigate to that specific section of our app.

That is all you need to make home screen quick action shortcuts in SwiftUI.

All the code can be found here on my Github.

I hope this helped you add quick actions to your next SwiftUI project.

Happy Coding!

How to Make a Expandable Button in SwiftUI

In this post we are going to learn how to make a SwiftUI button that expands out to show more button options. Below is an example of what our button will look like when we are finished.

Let’s get started by creating a new SwiftUI project.

In our project let’s add a new SwiftUI file and name it ExpandableButton. Inside of our ExpandableButton.swift file we will add the following code.

import SwiftUI

struct ExpandableButton: View {
    var body: some View {
        Button(action: {
                    print("Expandable button tapped!!!")
        }) {
            Image(systemName: "plus.circle.fill")
                .resizable()
                .frame(width: 75, height: 75)
                .foregroundColor(.green)

        }
        
    }
}

In the code above all we did was create a button and stylize it. Next we will need to wrap our button in a VStack so that when we add our menu buttons they will appear above our expandable button.

import SwiftUI

struct ExpandableButton: View {
    var body: some View {
        VStack {
            Button(action: {
                        print("Expandable button tapped!!!")
            }) {
                Image(systemName: "plus.circle.fill")
                    .resizable()
                    .frame(width: 75, height: 75)
                    .foregroundColor(.green)

            }
        }
        
    }
}

Now let’s create our menu buttons in a new SwiftUI file. We will name this file MenuButtons and we will add the following code to it.

import SwiftUI

struct MenuButtons: View {
    var buttonImage: String
    
    var body: some View {
        Button(action: {
            print("Menu Button tapped")
            
        }) {
            ZStack {
                Circle()
                    .foregroundColor(.green)
                    .frame(width: 50, height: 50)
                
                Image(systemName: buttonImage)
                    .imageScale(.large)
                    .foregroundColor(.white)
                
            }
        }
    }
}

In the code above we made our menu buttons similar to how we created our expandable button. The only difference is that our menu buttons are slightly smaller and have a green circular background. We also added a buttonImage variable so that we can change the image that appears on our menu button.

Now that we have our menu button created let’s go back and add it to our ExpandableButton.swift file. First we will need to create a @State variable so that we can control when we show our menu buttons. In this example we will name are state property isExpanded and set it to false.

@State var isExpanded = false

Next we will add a conditional statement above our expandable button that will check if our button is expanded. If isExpanded returns true we will show our the menu buttons.

            if isExpanded {
                MenuButtons(buttonImage: "camera.fill")
                MenuButtons(buttonImage: "record.circle.fill")
                MenuButtons(buttonImage: "photo")
            }

In the action of our expandable button we will toggle our isExpanded property so that we can control when to show and hide our menu buttons. We’ll also add an animation so that when the expandable button is pressed our button will have a smooth transition. Our code should now look like the example below.

import SwiftUI

struct ExpandableButton: View {
    
    @State var isExpanded = false
    
    var body: some View {
        VStack {
            if isExpanded {
                MenuButtons(buttonImage: "camera.fill")
                MenuButtons(buttonImage: "record.circle.fill")
                MenuButtons(buttonImage: "photo")
            }
            
            Button(action: {
                print("Expandable button tapped!!!")
                isExpanded.toggle()
                
            }) {
                Image(systemName: "plus.circle.fill")
                    .resizable()
                    .frame(width: 75, height: 75)
                    .foregroundColor(.green)
            }
            
            
        }
        .animation(.spring())
    }
}

Now if we go and run our preview canvas we should see our button. If we go and tap our button then we should see our menu buttons pop up with an animation.

Thanks for reading! I hope this helps you in your next SwiftUI project.

All the code from this post can be found on my Github.

📱 Happy Coding! 💻

How to Add Custom Modifiers and Views to Xcode’s Library

SwiftUI allows us to add our own custom views and modifiers to Xcode’s library. By doing this we are able to easily access our custom views and add them into our code in just a click. Let’s see how we can take advantage of this in our own SwiftUI project.

First thing we will need is to add a new SwiftUI file to our project to store our custom views and modifiers. We’ll name our file LibraryContent and replace the boilerplate code with the code in the example below.

import SwiftUI

struct LibraryContent: LibraryContentProvider {
    @LibraryContentBuilder
    var views: [LibraryItem] {
        LibraryItem(CloseButton(), category: .control)
    }
}

In the example code we are creating a LibraryItem so we can add our custom view to the Xcode library. We chose to add our custom close button because we will most likely use this view throughout the app and we will want to have quick access to it from our Xcode library.

If we dive into the Documentation for LibraryItem we can see that we can add things like a title and category to our LibraryItem. This is to help us better organization our views and modifiers in our Xcode Library. In our example we are adding the close button to the .control category. Also you will notice we didn’t add a title to our LibraryItem but Xcode is smart enough to use the filename “CloseButton” as the title.

Now Xcode probably is showing some errors because it can’t find our CloseButton view. Let’s go ahead and create a new SwiftUI file and name it CloseButton.

In our CloseButton.swift file add the following code.

import SwiftUI

struct CloseButton: View {
    var body: some View {
        Image(systemName: "xmark")
                    .font(.system(size: 17, weight: .bold))
                    .foregroundColor(.white)
                    .padding(.all, 10)
                    .background(Color.black.opacity(0.6))
                    .clipShape(Circle())
    }
}

struct CloseButton_Previews: PreviewProvider {
    static var previews: some View {
        CloseButton()
    }
}

If we go and run the preview canvas in CloseButton.swift we should see a circular button with an X in the center.

Now if we go to our Xcode library we can see that our close button has been added. We can open up our Xcode library by going to the + button in the top right of Xcode or by using the keyboard shortcut Command + Shift + L.

Now that the Xcode library is open we can use its search bar at the top to search for our custom “Close button”. Like in the image below we should see our new custom view show up and allow us to drag and drop it or double click on it to add it to our current SwiftUI file.

We can also add custom modifiers to our library like we did our custom view.

To add a reusable modifier we will need to add a View extension to our LibraryContent.swift file. Inside of the extension we will need to create a function that returns a view with the modifiers we want added.

Let’s take a look at the example code below.

extension View {
    func customViewStyle() -> some View {
        return self
            .background(Color.red)
            .cornerRadius(22)
            .shadow(color: Color.red.opacity(0.3), radius: 20, x: 0, y: 10)

    }
}

Above we created our customViewStyle() function which is returning a view with 3 custom modifiers.

With just this code we can now go to any file in our project and apply our new custom modifier. Let’s see this in action by going to our ContentView.swift file. At the end of the Hello World text view we can add our new modifier.

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
            .customViewStyle()
    }
}

This is great but the only problem is that our new modifier for our view doesn’t show up in our Xcode library.

Let’s go back to the LibraryContent.swift file and under our CloseButton() let’s add another @LibraryContentBuilder with the following code.

    @LibraryContentBuilder
    func modifiers(base: Image) -> [LibraryItem] {
        LibraryItem(
            base.customViewStyle(),
            title: "Custom View Style 1",
            category: .effect
        )
    }

As you can see in the example above this code is very similar to how we added our custom close button to our library. Now if we go to our library with the second tab selected and search for our modifier we will see our “Custom View Style 1” modifier has been added.

Lastly let’s make our custom modifier customizable. We are going to change the hard coded values into variables so that anyone that is using our modifier can style it to their liking. Let’s go back to our LibraryContent.swift file and add the following changes to our code.

    @LibraryContentBuilder
    func modifiers(base: Image) -> [LibraryItem] {
        LibraryItem(
            // Added default values for our customViewStyle function
            
            base.customViewStyle(color: Color.red, cornerRadius: 22),
            title: "Custom View Style 1",
            category: .effect
        )
    }
}

extension View {
    // Add variables to our function so we can customize it to our like for the specific view we are working on
    
    func customViewStyle(color: Color, cornerRadius: CGFloat) -> some View {
        return self
            .background(color)
            .cornerRadius(cornerRadius)
            .shadow(color: color.opacity(0.3), radius: 20, x: 0, y: 10)

    }
}

As you can see in the code above we added variables to our customViewStyle() function so we can change the color and the corner radius to whatever value we want. We also added our previously hard coded values as the default values for our modifier. Let’s go back to our ContentView.swift file and apply our updated modifier to the Hello World text view.

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
            .customViewStyle(color: .green, cornerRadius: 10)
    }
}

Hope this helps you on your next SwiftUI project!

Thanks for reading!

All the code in this post can be found on my Github.

👨🏻‍💻 Happy coding! 👨🏻‍💻

How to Make a Placeholder View in SwiftUI using the Redacted Modifier

As of iOS 14 we can now use the new .redacted(reason:) modifier in SwiftUI to make placeholders for our views. Let’s take a look at the example below to get a better understanding of how we can add placeholders to our SwiftUI project.

        Text("Hello world!")
            .padding()
            .redacted(reason: .placeholder)

By adding the redacted modifier our users will get a outline preview of our content. This is great to use when content is being loaded to our views.

We can also redact groups of SwiftUI views. Let’s write some code to see how we can implement this in a example. First let’s create a new SwiftUI file and name it ComplexView. Next let’s add the following code below.

import SwiftUI

struct ComplexView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Spacer()
            Image("husky")
                .resizable()
                .frame(width: 200, height: 200)
                .clipShape(Circle())
            Text("Husky World!")
                .font(.title)
            .fontWeight(.bold)
                Spacer()
            Text("A husky is a sled dog used in the polar regions. One can differentiate huskies from other dog types by their fast pulling-style. They represent an ever-changing crossbreed of the fastest dogs (the Alaskan Malamute, by contrast, pulled heavier loads at a slower speed). Humans use huskies in sled-dog racing. Various companies have marketed tourist treks with dog sledges for adventure travelers in snow regions. Huskies are also kept as pets, and groups work to find new pet homes for retired racing and adventure-trekking dogs.")
            HStack() {
                Spacer()
                Button("More Huskies!") {
                    print("Huskies!!!!")
                }
                .padding()
                .font(.system(size: 32))
                Spacer()
            }
            Spacer()
        }
        .padding()
    }
}

We should now see a view about huskies in our preview canvas!

Now if we go to our ContentView.Swift file we can add our ComplexView() to our body. Here we will add the redacted modifier to the ComplexView to redact all of the views inside of the container.

import SwiftUI

struct ContentView: View {
    var body: some View {
        ComplexView()
            .redacted(reason: .placeholder)
    }
}

We can also redact specific views in our ComplexView.swift file. Let’s say we wanted to show a placeholder for everything in our complex view except for our “Husky World!” text. We can simply go back to our ComplexView.swift file and add the .unredacted() modifier to our “Husky World!” text view.

struct ComplexView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Spacer()
            Image("husky")
                .resizable()
                .frame(width: 200, height: 200)
                .clipShape(Circle())
            Text("Husky World!")
                .font(.title)
                .fontWeight(.bold)
                // Added Unredacted
                .unredacted()
            Spacer()
            Text("A husky is a sled dog used in the polar regions. One can differentiate huskies from other dog types by their fast pulling-style. They represent an ever-changing crossbreed of the fastest dogs (the Alaskan Malamute, by contrast, pulled heavier loads at a slower speed). Humans use huskies in sled-dog racing. Various companies have marketed tourist treks with dog sledges for adventure travelers in snow regions. Huskies are also kept as pets, and groups work to find new pet homes for retired racing and adventure-trekking dogs.")
            HStack() {
                Spacer()
                Button("More Huskies!") {
                    print("Huskies!!!!")
                }
                .padding()
                .font(.system(size: 32))
                Spacer()
            }
            Spacer()
        }
        .padding()
    }
}

Now when we look at our preview for our Complex view we should see everything redacted except “Husky World!”.

It is good to note that buttons are not disabled when using the redacted modifier. This means users can still tap on buttons while the placeholder for the button is being shown. You will need to use a @State or @Enviorment variable to handle disabling any buttons in your view so that button actions won’t get triggered as your redacted view is being shown.

The example code for this post can be found on my GitHub.

Thanks for reading my post and I hope this helps in your next SwiftUI project.

Happy Coding!

Redact GIFs - Get the best GIF on GIPHY

How to Add Sign In with Apple to a SwiftUI Project

Sign in with Apple makes it safe and secure for users to login to websites and apps. We can implement Sign in with Apple easily with SwiftUI 2. For us to use Sign in with Apple in our app we are required to use iOS 14 or later and Xcode 12 or later.

The first thing we need to do is go to our project file which is located at the top of our navigator on the left side of Xcode. Then we need to go to the Signing & Capabilities tab. In the left corner of this tab we need to click on the + Capability button and type in Sign in with Apple. Then press enter to add it to our project.

Now let’s navigate to our ContentView.swift file to get started.

We need to open our library to get a Sign in with Apple Button for our project. We can use the keyboard shortcut Command + Shift + L to bring up our library. In the search bar we can type in Sign in with Apple and drag and drop our Sign in with Apple button into either our code or onto our canvas.

Now you might have a error pop up that says Xcode can not find Sign in with Apple in our scope. All you have to do to get rid of this error is import AuthenticationServices at the top of our ContentView.swift file. Now let’s add the code in the example below to our project and we will walk through what we are implementing in our app.

import SwiftUI
import AuthenticationServices

struct ContentView: View {
    var body: some View {
        SignInWithAppleButton(
            // 1.
            onRequest: { request in
                request.requestedScopes = [.fullName, .email]
            },
            onCompletion: { result in
                switch result {
                // 2.
                case .success (let authenticationResults):
                print("Authorization successful! :\(authenticationResults)")

                // 3.
                case .failure(let error):
                    print("Authorization failed: " + error.localizedDescription)
                }
            }
            // 4.
        ).frame(width: 200, height: 50, alignment: .center)
    }
}

  1. Here we are specify what kind of information we would like to get from our user. In this example we are requesting the users full name and email address to be used in our app.
  2. If the request goes through successfully we can then gain access to the users information we requested. This would be the users full name and email address.
  3. If something goes wrong this is where we will be able to show a error message to our user and notify them that something has gone wrong with using Sign in with Apple.
  4. Lastly we are setting the frame of our button so our button won’t take up the whole screen.

Now when we run our app we should see a Sign in with Apple button.

When we tap on our button we should be prompted to authenticate using our full name and email.

We can switch the Sign in button to be a Continue with Apple button or a SignUp with Apple button by simply adding the following code at the top of our existing code.

import SwiftUI
import AuthenticationServices

struct ContentView: View {
    var body: some View {
        SignInWithAppleButton(
            // Add this below for Sign up with Apple button
            .signUp,
            
            onRequest: { request in
                request.requestedScopes = [.fullName, .email]
            },
            onCompletion: { result in
                switch result {
                case .success (let authenticationResults):
                print("Authorization successful! :\(authenticationResults)")
                case .failure(let error):
                    print("Authorization failed: " + error.localizedDescription)
                }
            }
        ).frame(width: 200, height: 50, alignment: .center)
    }
}
struct ContentView: View {
    var body: some View {
        SignInWithAppleButton(
            // Add this below for Continue with Apple button
            .continue,
            
            onRequest: { request in
                request.requestedScopes = [.fullName, .email]
            },
            onCompletion: { result in
                switch result {
                case .success (let authenticationResults):
                print("Authorization successful! :\(authenticationResults)")
                case .failure(let error):
                    print("Authorization failed: " + error.localizedDescription)
                }
            }
        ).frame(width: 200, height: 50, alignment: .center)
    }
}

That’s all there is to adding Sign in with Apple to our SwiftUI project. This blogs code can be found on my Github. If you want to see how we can implement Sign in with Apple in UIKit please check out my other blog post.

Thanks for reading and I hope this helps you on your next SwiftUI project!

꩜💻👨🏻‍💻 Happy Coding! 👨🏻‍💻💻꩜

How to Make a Sidebar Menu in SwiftUI

Sidebars are really useful for navigating on bigger screen such as iPad’s and the Mac’s. We are going to take a look at how we can implement sidebars into a multiplatform SwiftUI app.

The first thing we need to do is open a new Xcode project and create a new SwiftUI file. In this example we are going to name our file SideBar. Inside the Sidebar file we are going to replace the boilerplate code with the code below.

struct Sidebar: View {
    var body: some View {
        NavigationView {
            List {
                Label("Books", systemImage: "book.closed")
                Label("Tutorials", systemImage: "list.bullet.rectangle")
                Label("Video Tutorials", systemImage: "tv")
                Label("Contacts", systemImage: "mail.stack")
                Label("Search", systemImage: "magnifyingglass")
            }
            .listStyle(SidebarListStyle())
            .navigationTitle("Code")
        }

    }
}

In the above example we have created a simple list view with a few labels for our sidebar. Then we set our list with the sidebar style by simply adding the .listStyle(SidebarListStyle()) modifier. We also added a navigation view so we can have a nice title label and add navigation to our sidebar labels in the future.

Now we are going to run this code on our iPad simulator and rotate our simulator 90°. We can now see how our sidebar looks on a bigger iPad screen.

Let’s fill in the blank space of our example to see how this would look in a real app. First we will remove the NavigationView from our Sidebar.swift file. Then we will go into our ContentView file and add the following code below.

struct ContentView: View {
    var body: some View {
        NavigationView {
            Sidebar()
            List(0 ..< 20) {_ in
                Text("Book")
            }
            .navigationTitle("Book List")
        }
        
        
    }
}

Now when we run our application we get a mock book list next to our sidebar.

If we wanted to make this sidebar available on a Mac version of our app all we would need to do is add a few lines of code to our project.

The first thing we will need to do is extract out the code in between the NavigationView curly braces. We can do this by holding down the command button and clicking on List. Next scroll down and select Extract subview. We will rename our subview SideBarContent. Now we need to add #if, #else, and #endif under our NavigationView. This will allow us to specify how we want our sidebar layout to look for the different operating systems.

struct Sidebar: View {
    var body: some View {
        NavigationView {
            #if os(iOS)
            SideBarContent()
                .navigationTitle("Code")
            #else
            SideBarContent()
                .frame(minWidth: 200, idealWidth: 250,maxWidth: 300)
            #endif
        }

    }
}

The code above is allowing us to adapt our sidebar code to the different OS‘s (Operating Systems) our app is going to run on. We need to separate our code because there are different requirements for running our code on the different operating systems. In the first part of our #if statement we are checking to see if our OS is iOS and if it is we are then setting our sidebar with a navigation title. We need to add our .navigationTitle() modifier here because it is not supported in MacOS. In the #else statement all we need to do is set the minimum frame of our Mac app’s window. Lastly we end our if statement with the #endif.

Now we can run our sidebar code on both the iOS and MacOS (We need to have MacOS Big Sur installed on your Mac to run this on the Mac simulator).

That’s all there is too adding a native sidebar to a multiplatform SwiftUI app. Feel free to check out the example code here on my Github.

💻👨🏻‍💻 Happy Coding! 💻👨🏻‍💻

How to Show a ProgressView in SwiftUI

At WWDC 2020 Apple introduced ProgressView‘s to SwiftUI. A ProgressView allows us to show either a circular progress view or a linear progress view for when tasks are loading in our app.

The first type of progress view we will look at is the Indeterminate Progress view. The indeterminate progress view creates a spinning loading view. The example below shows how we can easily add this into our code.

ProgressView()

We can also add text to the progress view by adding a string inside the parentheses.

ProgressView("Loading...")

The second type of progress view we will look at is the Linear Progress view. Linear progress views are great for when we have something loading and we want to show the progress to our user.

To make a linear progress view we need to specify two parameters, the current value and the total value of our progress view.

ProgressView("Downloading…", value: 25, total: 100)  
    .padding()

As you can see in the above example all we did was add a current value of 25 and a total value of 100. This shows our current progress as the blue line at 25% of our total value which is 100%.

We can see how a linear progress view works by adding a simple timer that changes the current value over time until it reaches our total value.

struct LinearProgressView: View {
    @State private var currentValue = 0.0
    let timer = Timer.publish(every: 0.09, on: .main, in: .common).autoconnect()
    
    var body: some View {
        ProgressView("Downloading…", value: currentValue, total: 100)
            .padding()
            .onReceive(timer) { _ in
                if currentValue < 100 {
                    currentValue += 1
                }
            }
    }
}

In the example above we added a @State property called currentValue. This will update the current progress of our linear progress view. Then we created and added our timer to the .onReceive modifier. This will update our currentValue variable every time our timer hits 0.09 seconds. We also check to make sure that the currentValue never exceeds the total value of the progress view because this can cause problems in your code and Xcode will yell at us.

Lastly we can customize your linear progress view by changing the accent color of the progress bar with the .accentColor modifier and by changing the text color by using the .foregroundColor modifier.

struct LinearProgressView: View {
    @State private var currentValue = 0.0
    let timer = Timer.publish(every: 0.09, on: .main, in: .common).autoconnect()
    
    var body: some View {
        ProgressView("Downloading…", value: currentValue, total: 100)
            .padding()
            .onReceive(timer) { _ in
                if currentValue < 100 {
                    currentValue += 1
                }
            }
            .accentColor(.purple)
            .foregroundColor(.red)
    }
}

If you would like to play around with these progress view examples please check out the code for this blog on my Github.

Hope this helps shed some light on how to use progress views in SwiftUI!

💻 ꩜ Happy Programming! ꩜💻

How to Show a Menu When a Button is Pressed in SwiftUI

As of iOS 14 and SwiftUI 2 we can now add a pop out menu to any button in our app. We can implement this by using the new Menu keyword. Menus in some way are going to replace the current action sheets used in iOS apps. The problem with action sheets is they only show up at the bottom of our screen. This doesn’t look great on larger screens like the iPad and Mac. Menus allow the flexibility in where we would want to show pop out menus in our app instead of only showing them at the bottom of our screens.

Let’s take a look at the example below of how we can implement this in our application.

struct ContentView: View {
    var body: some View {
        
        Menu("Create") {
            Button("Cancel", action: {})
            Button("Search", action: {})
            Button("Add", action: {})
        }
    }
}

As you can see in the example above we use the Menu keyword and name our menu “Create“. Next inside the menu curly braces we can add as many buttons as we want. The order of how we place the buttons in the menu does matter. The first button we create will always be at the bottom and whatever button we create next will be on top of the pervious the button. Take a look at the example below.

As you can see in the above example once our create button is tapped we can see our menu of buttons appear in the specific order we placed them in our code.

We can also have menus inside of menus.

struct ContentView: View {
    var body: some View {
        
        Menu("Create") {
            Button("Cancel", action: {})
            Menu("More") {
                    Button("Rename", action: {})
                    Button("Developer Mode", action: {})
                }
            Button("Search", action: {})
            Button("Add", action: {})
        }
    }
}

Now if we tap on the Create button and then tap our More button we now get presented with our new menu inside of our main menu.

Lastly we can have customized text and icons in our menu by adding a label to our menu.

struct ContentView: View {
    var body: some View {
        
        Menu {
            Button("Cancel", action: {})
            Button("Search", action: {})
            Button("Add", action: {})
        } label: {
            Label("Create", systemImage: "plus.circle")
        }
    }
}

As you can see in the above example now we can add a custom title and icon to our menu button.

There is a great WWDC video that can help explain menus if you are still confused.

I hope this helps you better understand how to use the menus in iOS 14 and SwiftUI 2.

💻👨🏻‍💻 Happy coding! 💻👨🏻‍💻