Categories
Uncategorized

How to parse JSON in Swift 5

In this tutorial, we talk about a very important concept when it comes to working with APIs: How to parse JSON in Swift. We’ll talk about how to download JSON data from an URL and how to make use of it by encoding it. We learn all of this the easy way with using the Codable protocol!

What we want to achieve 🚀

Supposed we have an API that allows us to download images via their url’s and the usernames of the uploader, we want to create an app that displays that image with the according username. It should look similar to this:

The JSON data we use for this looks like this:

In this tutorial, we start with learning how to download the JSON data and how to parse it so we can extract the image URL’s and the usernames.

Creating the Swift data model for our JSON data 🛠

Take a look at the JSON data above. It contains three entries (001, 002, and 003), where each has one imageURL and one uploaderName. To parse the JSON, we first need to define suitable data models for storing the parsed data later on.

Our data model we start with is the one we use for the entries’ content, the image data. We use a struct containing an imageURL and uploaderName property for this.

struct Image: Codable {
    let imageURL: String
    let uploaderName: String
}

Our second data model is for the entries itself. The only property of this model is a dictionary, where the key is the name of the entry (e.g. 001) and the value an instance of our just created Image model.

struct Entry: Codable {
    let images: [String: Image]
}

You see that we needed to create two different “model layers”, just as our JSON data has two “layers”.

Tip: Especially when it comes to more complex JSON data, you can use a tool such as quicktype, which automatically creates data models out of your JSON, ready for being used in your Swift project.

Downloading the JSON data ⬇️

Now we are ready to download our JSON data. I uploaded my sample JSON data to the web using this free tool. You can do this too if you want and then use the generated URL for retrieving the data. In real life you would use the API url from now on.

We start with creating a Swift URL object out of our URL.

if let url = URL(string: "http://www.json-generator.com/api/json/get/cfpeTpOFrC?indent=2") {
    //
}

We can then retrieve the JSON data from the URL like this:

if let url = URL(string: "http://www.json-generator.com/api/json/get/cfpeTpOFrC?indent=2") {
   URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        //...
       }
   }.resume()
}

Parsing the JSON data in Swift💡

To decode the JSON data we have to initialize a JSONDecoder.

if let url = URL(string: "http://www.json-generator.com/api/json/get/cfpeTpOFrC?indent=2") {
   URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        let jsonDecoder = JSONDecoder()
       }
   }.resume()
}

We can now parse the JSON by using the Decoder and our created data models. We do this inside a try-catch block to print out the error when something goes wrong with the parsing process.

if let url = URL(string: "http://www.json-generator.com/api/json/get/cfpeTpOFrC?indent=2") {
   URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        let jsonDecoder = JSONDecoder()
        do {
            let parsedJSON = try jsonDecoder.decode(Entry.self, from: data)
        } catch {
            print(error)
        }
       }
   }.resume()
}

At this point the parsedJSON property is assigned to an instance of our Entry class. This means that the parsedJSON contains a dictionary, where the values are instances of the Image class.

Therefore we can cycle through every Image instance of our Entry’s dictionary and grab the imageURL and uploaderName of each one.

if let url = URL(string: "http://www.json-generator.com/api/json/get/cfpeTpOFrC?indent=2") {
   URLSession.shared.dataTask(with: url) { data, response, error in
     if let data = data {
         let jsonDecoder = JSONDecoder()
         do {
             let parsedJSON = try jsonDecoder.decode(Entry.self, from: data)
             for image in parsedJSON.images {
                 print(image.value.imageURL)
                 print(image.value.uploaderName)
            }
        } catch {
            print(error)
        }
       }
   }.resume()
}

We could now use this data for downloading the images via the image URLs and displaying the according uploader user names. How to do this with SwiftUI is explained in our Mastering SwiftUI book where we use the Flickr API to parse image data from the user’s location.

Conclusion 🎊

Great, we just learned how to retrieve and parse JSON data from the web by using the Swift Codeable protocol and our own data models. You are now able to work with API’s that provide data via JSON.

I hope you enjoyed this tutorial! If you want to learn more about SwiftUI, make sure you check out our free SwiftUI Basics eBook and our other tutorials! Also make sure you follow us on Instagram and subscribe to our newsletter to not miss any updates, tutorials and tips about SwiftUI and more!

Categories
Uncategorized

Drawing in SwiftUI – A Comprehensive Guide

Hello and welcome to a new tutorial! Today’s tutorial is all about drawing in SwiftUI. We’ll learn how to draw custom graphics, vectors and forms by using the SwiftUI graphics API. First, we are going to take a look at SwiftUI’s built-in shapes and how we can modify them. Then we’re going to compose our own shapes by using custom paths. By the end of this step-by-step tutorial, you’ll be able to:

  • Use and modify prebuilt shapes like Rectangles, Circles etc. for your SwiftUI app
  • Draw your own, custom shapes by using SwiftUI paths
  • Design complex shapes, for example to draw your app icon or use custom badges for your SwiftUI app

Here are some of the designs we’re going to achieve:

Basic Shapes 🔵⬛️

SwiftUI provides us with some basic shapes we can use for drawing in our app. These are:

  • Rectangle
  • RoundedRectangle
  • Ellipse
  • Circle
  • Capsule 

You can use them by simply initialising them within your SwiftUI view. 

struct ShapeSandbox: View {
    var body: some View {
        RoundedRectangle(cornerRadius: 20)
    }
}

By default however, the Shape doesn’t know anything about its supposed size and position. This is why as long as we provide the shape with a .frame it tries to fill out the whole view.

So let’s provide our RoundedRectangle with a certain width and height. We also define another fill color than the default black. 

RoundedRectangle(cornerRadius: 20)
            .frame(width: 250, height: 100)
            .foregroundColor(.purple)

Maybe you want your shape not to be filled out with a certain but rather want to create some kind of border out of it. You can do this by using the .stroke modifier (as the first one!) with choosing a certain lineWidth for your border.

RoundedRectangle(cornerRadius: 20)
            .stroke(lineWidth: 10)
            .frame(width: 250, height: 100)
            .foregroundColor(.purple)

It’s even possible to use another stroke style, for example to use a dashed-line as your border. For example like this:

RoundedRectangle(cornerRadius: 20)
            .stroke(style: StrokeStyle(lineWidth: 7, lineCap: .square, dash: [15], dashPhase: 2))
            .frame(width: 250, height: 100)
            .foregroundColor(.purple)

Of course, you can use the discussed “techniques” for all other shapes!

Drawing own shapes by using paths 🖌

Let’s take a quick look at what paths in SwiftUI are. In a nutshell, you can imagine a path like a set of drawing instructions, including lines, curves and other segments like arcs. This why a shape is doing nothing different than using a specific path to define its appereance. 

Okay, let’s use this knowledge to draw a square by using a Path. To do this, create a new SwiftUI view and call it, for intance, PathSandbox.swift.

Let’s insert a Path instance followed by its corresponding closure. Inside the closure we can actually define how our Path should go. By default again, SwiftUI will fill out resulting view with a black color. While drawing the Path however, I personally prefer to only see the outer border for having a better overview of the resulting Path. This is why I’m applying the .stroke() we got familiar with before starting drawing the Shape.

struct PathSandbox: View {
    var body: some View {
        Path { path in

        }
            .stroke()
    }
}

Let’s draw our square by adding several lines to our Path. We can do this by using absolute x- and y-coordinates. Before drawing the first line, we move the “cursor” right upper corner of our imaginary square. Then we’re adding a line which points to the lower right corner and another line pointing to the lower left corner.

Path { path in
            path.move(to: CGPoint(x: 200, y: 0))
            path.addLine(to: CGPoint(x: 200, y: 200))
            path.addLine(to: CGPoint(x: 0, y: 200))
        }
            .stroke()

You see that two lines got added to our SwiftUI! Let’s finish our square by adding a third line pointing to the upper left corner. We could close the rectangle by adding a last line pointing to where we started, but we can also closing the Path “automatically” by using the .closeSubPath modifier.

Path { path in
            path.move(to: CGPoint(x: 200, y: 0))
            path.addLine(to: CGPoint(x: 200, y: 200))
            path.addLine(to: CGPoint(x: 0, y: 200))
            path.addLine(to: CGPoint(x: 0, y: 0))
            path.closeSubpath()
        }
            .stroke()

Now that we are done with defining our square path, we can fill it out by deleting the .stroke modifier again!

var body: some View {
        Path { path in
            //...
        }
    }

Which results in the following view:

Since, as you already know, a Shape also consists of a Path. For reusability purposes, we can simply convert our Square Path to such a Shape by declaring a struct which adopts the Shape protocol. 

struct MySquare: Shape {
    
}

The single requirement for the Shape protocol is having a path function which draws the actual shape. We can simply use the Path we just created like this:

struct MySquare: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()

        path.move(to: CGPoint(x: 200, y: 0))
        path.addLine(to: CGPoint(x: 200, y: 200))
        path.addLine(to: CGPoint(x: 0, y: 200))
        path.addLine(to: CGPoint(x: 0, y: 0))
        path.closeSubpath()

        return path
    }
}

We now use our MySquare Shape inside our SwiftUI view!

struct PathSandbox: View {
    var body: some View {
        MySquare()
    }
}

However, our MySquare Shape still uses the absolute coordinates we defined earlier. Instead, we want to make it dynamic so that we can transform it by adding a .frame modifier to the MySquare instance inside our SwiftUI view.

We can achieve the rect parameter of our MySquare’s path function. The rect is like a invisible scratchpad inside which we can draw our square and which can gets transformed by passing a certain frame to it when initialising the actual shape.

So let’s exchange the fixed coordinates by the corresponding points of the invisible rect.

struct MySquare: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()

        path.move(to: CGPoint(x: rect.size.width, y: 0))
        path.addLine(to: CGPoint(x: rect.size.width, y: rect.size.width))
        path.addLine(to: CGPoint(x: 0, y: rect.size.width))
        path.addLine(to: CGPoint(x: 0, y: 0))
        path.closeSubpath()

        return path
    }
}

Let’s talk through it very quick. We just move our cursor to the very upper right edge of our invisible rectangle. Then we tell our path to draw a line to another point by using the rect’s width for both (remember, we want to ensure it’s a square) x- and y-coordinates. Then we’re going back to the lower left corner, followed by the upper left corner before closing the subpath. Awesome, now our MySquare Shape is dynamic and we can adjust its size by using the .frame modifier!

struct PathSandbox: View {
    var body: some View {
        MySquare()
            .frame(width: 250, height: 250)
    }
}

Drawing more complex SwiftUI shapes  👨‍🎨

Okay, now that we know how to create our own shapes we practice our SwiftUI drawing skills by creating more complex shapes. Eventually, we’ll rebuild the logo of our website.

To do this, create a new SwiftUI view called BBLogo. The icon should simply consist of a black background with two B letters on it. We start by inserting a ZStack into or BBLogo view so that all views inside it get stacked on top of each other. For the background we use the rounded rectangle shape we got familiar with earlier.

struct BBLogo: View {
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 20)
                .frame(width: 200, height: 200)
        }
    }
}

Next, we draw the B-Letter Shape. Let’s declare a struct conforming to the Shape protocol.

struct LetterB: Shape {
    
    func path(in rect: CGRect) -> Path {
        Path { path in
            
        }
    }
}

To instantly see what we’re drawing we already initialize a LetterB Shape inside our BBLogo view and apply it with a stroke, white color and a certain frame.

ZStack {
            RoundedRectangle(cornerRadius: 20)
                .frame(width: 200, height: 200)
            LetterB()
                .stroke(lineWidth: 12)
                .foregroundColor(.white)
                .frame(width: 100, height: 100)
        }

Let’s start drawing the LetterB Shape! First, we move the “cursor” to the middle of the rect’s upper edge. Then we add a line pointing to the upper left edge of the invisible rectangle. Next, we draw a line to middle of the rect’s left edge followed by a line pointing to center of the rect.

Path { path in
            path.move(to: CGPoint(x: rect.size.width/2, y: 0))
            path.addLine(to: CGPoint(x: 0, y: 0))
            path.addLine(to: CGPoint(x: 0, y: rect.size.width/2))
            path.addLine(to: CGPoint(x: rect.size.width/2, y: rect.size.width/2))
        }

Your preview should look like this now:

Before drawing on, we move our “cursor” back to the middle of the rect’s left edge. Then we draw a line to the lower left edge. Our last line points the middle of the rect’s lower edge.

Path { path in
            path.move(to: CGPoint(x: rect.size.width/2, y: 0))
            path.addLine(to: CGPoint(x: 0, y: 0))
            path.addLine(to: CGPoint(x: 0, y: rect.size.width/2))
            path.addLine(to: CGPoint(x: rect.size.width/2, y: rect.size.width/2))
            path.move(to: CGPoint(x: 0, y: rect.size.width/2))
            path.addLine(to: CGPoint(x: 0, y: rect.size.width))
            path.addLine(to: CGPoint(x: rect.size.width/2, y: rect.size.width))
        }

To complete the letter B we have to add two halved circles to our Shape. We do this by adding two arcs to our Path. When adding arcs, we need to specify the arcs center, as well as the start and end angle. Therefore we write:

Path { path in
            //...
            path.addArc(center: CGPoint(x: rect.size.width/2, y: rect.size.width*(3/4)), radius: rect.size.width/4, startAngle: .degrees(90), endAngle: .degrees(270), clockwise: true)
            path.addArc(center: CGPoint(x: rect.size.width/2, y: rect.size.width/4), radius: rect.size.width/4, startAngle: .degrees(90), endAngle: .degrees(270), clockwise: true)
        }

Awesome, we are finished with creating our LetterB Shape!

We complete the BBLogo by stacking another BLetter Shape on top of the background and the existing one and offsetting it a little bit.

ZStack {
            RoundedRectangle(cornerRadius: 20)
                .frame(width: 200, height: 200)
            LetterB()
                .stroke(lineWidth: 12)
                .foregroundColor(.white)
                .frame(width: 100, height: 100)
            LetterB()
                .stroke(lineWidth: 12)
                .foregroundColor(.white)
                .frame(width: 100, height: 100)
                .offset(x: 40)
        }


That’s it! By building our website’s logo we learned how to create more complex shapes.

Drawing curved shapes in SwiftUI ⤴️🎨

Last but not least, we’ll take a look at how to create curved shapes. By doing this, we’ll be able to create cool shapes like this raindrop icon!

Let’s create new SwiftUI view. Below this, declare a struct called Raindrop and conforming to the Shape protocol.

struct Raindrop: Shape {
    
    func path(in rect: CGRect) -> Path {
        Path { path in

        }
    }
}

Again we start with initialising the Raindrop Shape inside our SwiftUI view and applying a .stroke and .frame to it.

var body: some View {
        Raindrop()
            .stroke(lineWidth: 4)
            .frame(width: 200, height: 200)
    }

Inside our Raindrop’s path we start moving our “cursor” to the middle of the rect’s upper edge. 

Path { path in
            path.move(to: CGPoint(x: rect.size.width/2, y: 0))
        }

Next we want to draw a right curve downwards. To do this, we use the addQuadCurve method. This function adds a so-called Bézier curve to the path. For such a Bézier curve we first need to define an ending point for the curve but to actually do the curve we need to provide the Bézier curve with a control point. Such a control point is used to calculate the strength and direction of the curve. You don’t need to know the calculus for this (nor do i). Just take a look at the following infographic to get a feeling for this.

You see that the direction and strength of the curve depends on where we place the corresponding control point. The further away we place the control point, the more the curve is bent.

Let’s us this knowledge for our addQuadCurve function. We tell the path that the curve should end at the middle of the rect’s lower edge and we place the control point at the lower right edge of the rect

Path { path in
            path.move(to: CGPoint(x: rect.size.width/2, y: 0))
            path.addQuadCurve(to: CGPoint(x: rect.size.width/2, y: rect.size.height), control: CGPoint(x: rect.size.width, y: rect.size.height))
        }

Lets finish our Raindrop Shape by drawing another curve pointing to where we started drawing.

Path { path in
            path.move(to: CGPoint(x: rect.size.width/2, y: 0))
            path.addQuadCurve(to: CGPoint(x: rect.size.width/2, y: rect.size.height), control: CGPoint(x: rect.size.width, y: rect.size.height))
            path.addQuadCurve(to: CGPoint(x: rect.size.width/2, y: 0), control: CGPoint(x: 0, y: rect.size.height))
        }

Awesome, we’re done with our Raindrop shape! Now we can fill out this Shape inside our SwiftUI view by exchanging the .stroke modifier. For example, you can fill the shape with a gradient like this:

Raindrop()
            .fill(LinearGradient(gradient: Gradient(colors: [.white, .blue]), startPoint: .topLeading, endPoint: .bottom))
            .frame(width: 200, height: 200)

Now you’re Raindrop Shape should look like this:

Conclusion 🎊

That’s it! You learned how to use prebuilt shapes and how to create your own by working with Paths. You learned how to use dynamic values instead of fixed coordinates and how to add arcs and curves to your Path. With this knowledge you should be able to create your own drawings and use them inside your SwiftUI app!

I hope you enjoyed this tutorial! If you want to learn more about SwiftUI, make sure you check out our free SwiftUI Basics eBook and our other tutorials! Also make sure you follow us on Instagram and subscribe to our newsletter to not miss any updates, tutorials and tips about SwiftUI and more!

Categories
Uncategorized

How to create and style Buttons in SwiftUI

Wanna know how to create and customize a Button in SwiftUI that triggers specific code when being touched? Here you go! 👇

Step 1: Create the Object you want inside your Button, for example a Text. If you want more than one object inside your Button, you can group them using, for instance, a HStack. You can now apply modifiers for customizing the created objects.

import SwiftUI

struct ContentView : View {
    var body: some View {
        HStack {
            Text("ADD TO CART")
                .font(.custom("Avenir-Medium", size: 20))
                .foregroundColor(.black)
            Image(systemName: "cart")
                .foregroundColor(.black)
        }
    }
}

Step 2: If you nested multiple objects, you have to apply the overall modifiers like the Buttons frame size or background color to the Stack.  If you have only one object, simply apply these modifiers to the object itself.

struct ContentView : View {
    var body: some View {
        HStack {
            Text("ADD TO CART")
                .font(.custom("Avenir-Medium", size: 20))
                .foregroundColor(.black)
            Image(systemName: "cart")
                .foregroundColor(.black)
        }
            .frame(width: 220, height: 30)
            .padding()
            .background(Color.green)
            .cornerRadius(10.0)
    }
}

Step 3: Wrap everything into a Button. Insert the action parameter followed by curly braces. Inside these braces you can execute for example a print statement or a custom function.

import SwiftUI

struct ContentView : View {
    var body: some View {
        Button(action: {print("Button touched!")}) {
            HStack {
                Text("ADD TO CART")
                    .font(.custom("Avenir-Medium", size: 20))
                    .foregroundColor(.black)
            Image(systemName: "cart")
                    .foregroundColor(.black)
            }
                .frame(width: 220, height: 30)
                .padding()
                .background(Color.green)
                .cornerRadius(10.0)
        }
    }
}

Run your app by starting the live mode of the Preview Simulator!

mIf you liked this tutorial, feel free to check out our Mastering SwiftUI eBook. In this book, we also created a To-Do app by using the mentioned Core Data functionalities!

I hope you enjoyed this tutorial! If you want to learn more about SwiftUI, check out our other tutorials! Also make sure you follow us on Instagram and subscribe to our newsletter to not miss any updates, tutorials and tips about SwiftUI and more!

Categories
Uncategorized

Xcode 11 Tutorial For Beginners

In this series, you will learn everything you need to know to get started with Xcode, quickly & easily.

The whole article was updated for Xcode 11, including the new main feature, SwiftUI.

What you will learn in this part of the series:

  • What Xcode is and what you need it for
  • How Xcode is structured and what you need for running it
  • Tips for an efficient usage of Xcode

What is Xcode used for and what do I need for running it?

Xcode is Apple’s in-house IDE (integrated development environment). When you develop iOS apps, it is the software you work with most often. Therefore, it is very important to have a profound knowledge of Xcode and to master the basics from scratch.

In Xcode you set up the user interface of your app, organize and write the code that makes your app run. Xcode also offers you the possibility to run and test your app on a virtual simulator on your Mac (and of course on a real iOS device).

Fortunately, Xcode is available completely free of charge in the Mac App Store. On Windows PC’s Xcode is not available. Although it may be possible to run OSX on a virtual machine (“Hackintosh”), i strongly recommend you to buy a Mac if you don’t have one yet. This is more than useful in the long run if you want to develop iOS apps.

Xcode is Apple’s in-house IDE for the development of iOS apps. It is free, but only available for Mac.

Creating a Xcode Project and choosing the User Interface

To get in touch with the IDE, just open Xcode and click on “Create a new Xcode Project” (btw: “Playgrounds”, the option below creating a new project, is an option to test new concepts and ideas quickly and easily). Next, click on “Single View App” and then on “Next” and give your app any name you want.

At this point, you can choose which User Interface mode you want to work with. You can either use SwiftUI or Storyboards.

  • Storyboards: Until now, the most common way to build iOS apps was to use storyboards. Simplified, you created the UI by dragging and dropping, arranging and constraining elements and by connecting them to your code.
By working with storyboards you create the UI by dragging and dropping, arranging and constraining elements and by connecting them to your code.
  • SwiftUI: SwiftUI is THE new feature that got shipped with Xcode 11. It’s a completely new way to build iOS apps and follows a declarative approach, meaning that with SwiftUI you describe how your interface should look like and what it should do. To get started with SwiftUI, check out this quick introduction to this new framework and make sure you read our free tutorials!
SwiftUI follows a declarative syntax approach, meaning that we describe in code how our interface should look

Should I choose Storyboards or SwiftUI?

When you are new into iOS development, you are probably struggling whether you should work with SwiftUI or stay with Storyboards. To give you some advice, we wrote a an article for, that might be helpful.

Unterstanding the Interface 💡

To understand the basic layout of Xcode, create a new Single View app by using SwiftUI, as we described above. Don’t worry understanding the basics of Xcode is not depended on the User Interface mode you choose, so even if you want to work with Storyboards, starting with this example is fine.

What you see now may feels a bit overwhelming. Don’t worry, this feeling is perfectly fine and will pass when you understand the basics of how Xcode is constructed.

Fortunately, the interface is designed pretty straight forward. The interface you see now basically consists of 5 sections:

The five main sections: Toolbar(1), Navigator Area(2), Editor Area(3), Utility Area(4), Debug Area(5)

The Toolbar 🛠

Xcodes toolbar
The right three buttons of the toolbar allows you to show/hide the different areas

The right three buttons of the toolbar allows you to show/hide the different areas

The toolbar allows you to access the basic Xcode settings (don’t mix this up with your app projects settings) and perform several operations. On the left side of the toolbar you can select the device on which you want to run your app, for instance on any simulator. The field in the middle tells you when Xcode is working on something. The area on the right is responsible for the Xcode view modes, more on that later.

The Navigator Area 🔍

The Navigator Area helps you finding your way around your project and organize your code and resources. By default the “Project Navigator” is selected, probably the most important mode of the navigator. Here are the different parts of your app’s code listed. The more complex your app becomes, the more files your project will contain. To keep track, you can create „groups“ (Xcodes name for folders) and move the files as you like. Where you are placing files within your project usually has no effect on the logic of your code or the behavior of the app.

The Navigation Area helps you to organize your app project and to find your way around it

When you click on a file, it will open in the Editor Area where you can, you guessed it, edit it. If not already done, click on “ContentView.swift” to display this file in the Editor Area. Swift files are the heart of every iOS App. In these you write the code that makes your app run.

The Editor Area ✍️

Here you write the code and compose the interface of your app. The appearance of the Editor Area depends on which file type is opened, especially and on whether you are working with SwiftUI or storyboards.

The Utility Area 🧰

Similar to the Editor Area, the appearance of the Utility Area depends on what file type you have just selected. Here you can access, for instance meta data, references, etc. of files or/and their components. This area is especially important when editing storyboards.

Especially the Utilty Area often confuses beginners, because the use of this area depends on the particular situation. But the more you work with Xcode the more you get a feeling for it. You’ll see, it’s much easier than it looks at first sight, I promise!

The Debug Area 👷‍♀️

When you run your app, you will find all relevant information about errors etc. that Xcode provides you. This area becomes very important when it comes to finding and fixing errors and bugs of your app.

Often the output is very long, so you can use the filter to easily find certain output.

Basic Shortcuts

If you begin working with Xcode more often, you will notice that different things like frequently showing and hiding different areas can inferiore your workflow. In order to save time you should remember and use at least the most important shortcuts. Soon you will be memorizing them and save a lot of time. Here are some first, important ones to start with:

  • ⌘+0: Show/Hide Navigator Area
  • ⌘+⌥+0: Show/Hide Utility Area
  • ⌘+SHIFT+Y: Show/Hide Debug Area

Shortcuts are enormously helpful to optimize your workflow and save time

Conclusion 🎊

Congratulations! You now know the basics of Xcode.

Don’t feel insecure if you are a little overwhelmed. You’ll see, the more you deal with it, the more comprehensible everything becomes.

“The secret of getting ahead is getting started.”

Mark Twain

Do not hesitate to ask in the comments if anything is unclear. We will reply as soon as possible (:

Categories
Uncategorized

Core Data and SwiftUI – Saving, retrieving, updating and deleting persistent data

Hello and welcome to a new tutorial! Today we will learn how to use the Core Data framework to store and manage persistent data. The integration of Core Data into SwiftUI projects is surprisingly easy. By creating a useful app for a small pizza restaurant, we’re going to talk through all basic CRUD operations (Create, Read, Update and Delete Data) used in Core Data.

In this tutorial, we will explore:

  • How Core Data and SwiftUI work together
  • Creating and updating Core Data objects
  • How to update views when stored data gets updated
  • Using SwiftUI property wrappers for fetching Core Data objects

We will create a simple app for a pizza restaurant that waiters can use to take and manage orders.

The finished app will look like this:

Setting up Core Data in SwiftUI 🛠

To get started, open Xcode 11 and create a new Single view app and choose SwiftUI as the User Interface mode. Make sure that you check the “Use Core Data” box. This will automatically set up the initial Core Data implementation for our app!

When your project is created, you see that Xcode automatically generated a .xcdatamodeld file for us. Inside this file, we can visually design our model for managing persistent data as you’ll see in a moment.

Xcode also generated some interesting lines of code for us. Click on the SceneDelegate.swift file and take a look at the scene function inside the SceneDelegate class. The scene function handles booting up our initial view, which is the ContentView by default. Because we check marked “Use Core Data” when creating our SwiftUI project, Xcode applied some modifications to this function.

Take a look at this line of code within the scene function:

let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

This function retrieves the managed object context for us. A managed object context is like an in-memory scratchpad. It’s the place where objects are created, fetched, updated, deleted, and saved back to the persistent store of the device where the app runs on.

Let’s jump to the next line of code:

let contentView = ContentView().environment(\.managedObjectContext, context)

This line initializes the ContentView which is then set as the root view in the lines below. So far, so good. But what does this environment thing do?

Before our ContentView gets launched as the root view, it feeds the environment’s managedObjectContext key with our context we just created. The environment is the place where system-wide settings are stored in, e.g Calendar, Locale, ColorScheme and now also our created context (our app’s scratchpad). Each of this setting has its own key, in our case, it’s the .managedObjectContext key.

Now every view in our app can use the “context scratchpad” to retrieve, update and store objects from. We just have to use the managedObjectContext environment key for accessing it, as you will see later on.

Don’t worry if you are not familiar with this, the only thing you need to remember is that we can use the managedObjectContext now for fetching and saving our orders and you’ll see how easy it is in a moment.

Defining our data model 🛠

As said, in the project navigator, you can see a .xcdatamodeld file. In this file, we create and manage the entities of our CoreData data model for our SwiftUI app. In case you are not familiar with core data entities: You can think of an entity as a representation of a class, and an attribute, as a property of that class. The only entity we need for our app is for holding the different orders. Create the entity by clicking on the large plus sign at the bottom and then double-click on the created Entity and rename it to Order.

We need to know the following information about each order: The type of pizza the customer ordered, how many slices he wants to eat and the number of the table the customer is sitting at. Each order should also have an unique id and a status attribute for keeping track of whether the order is already completed. For the id we use the UUID type (this automatically creates a unique id for us), for the numberOfSclices we select Integer16 and for the rest String.

But let’s hold on a second and reconsider choosing String as the status attribute’s type. The order’s status should only be “Pending”, “Preparing” and “Completed”. Wouldn’t be using an enum the better choice for this? Unfortunately, we can’t create and use an enum inside the .xcdatamodeld file itself. But as said, by creating and designing the Order entity, Core Data created a corresponding class under the hood. We can access and modify this class by clicking on the Order entity, going to the Xcode toolbar and selecting Editor-“Create NSObjectManagedSubclass”.

After creating the subclass, Xcode generated two files for us. The Order+CoreDataClass.swift file holds the class itself and the Order+CoreDataProperties.swift contains its properties inside an extension.

After we created our data model’s subclass, we need to tell Xcode that the data model is no longer defined by the visual builder in our .xcdatamodeld file only, but manually defined by the corresponding subclass we just created. To do this, open the .xcdatamodeld file, click on the Order entity and open the data model inspector. Then choose “Manual/None” as the codegen mode.


At this point, we can remove the question marks from the String-type properties since we don’t want them to be optionals. We can also adopt the Identifiable protocol (this will make it easier for us the use Order instances inside the ContentView’s List) and since we have an id property we already conform to this.

extension Order: Identifiable {

    //...
    @NSManaged public var pizzaType: String
    @NSManaged public var status: String
    @NSManaged public var tableNumber: String

}

Below the OrderExtension we can declare our Status enum with the three different cases.

enum Status: String {
    case pending = "Pending"
    case preparing = "Preparing"
    case completed = "Completed"
}

If we now try to use the Status enum as the status’ data type, we will get an error.

You see that @NSManagedObject properties can’t be used with enums directly. But how else can we save the status of an order in Core Data? Here’s a workaround: We go ahead with creating an NSManaged status property but not of our Status type. Instead it should be a String. Next, we add another, regular variable called orderStatus. Because it’s not a NSManaged property, it can be of the type Status. We assign a setter and getter to our orderStatus, so that when this property is set, it will also set the NSManaged property accordingly and by using a getter, we try to convert the status string to an Status case when retrieving it.

extension Order: Identifiable {

    //...
    @NSManaged public var status: String
    @NSManaged public var tableNumber: String
    
    var orderStatus: Status {
        set {status = newValue.rawValue}
        get {Status(rawValue: status) ?? .pending}
    }

}

Awesome, we’re now finished with creating the Core Data model for our SwiftUI app!

Composing our UI 🎨

Important: Before moving on with composing our ContentView, we need to make sure that its preview can access the managedObjectContext as well. Otherwise the SwiftUI preview will fail when why try to implement CoreData functionality inside it. To do this, we repeat the steps as we did in the SceneDelegate.swift file in the beginning:

struct ContentView_Previews: PreviewProvider {
    
    static var previews: some View {
        
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        
        return ContentView().environment(\.managedObjectContext, context)
    }
}

Now our ContentView preview is able to manage CoreData requests!


The ContentView of our pizza restaurant app should contain a list of all orders already taken which the corresponding waiter can manage. Since we can’t store any data yet, we are using only a test list for now.

struct ContentView: View {
    var body: some View {
        List {
            Text("Sample order")
        }
    }
}

We also want to add a navigation bar to our app. To do so, we wrap our List into a NavigationView and use the .navigationBarTitle modifier.

NavigationView {
            List {
                Text("Sample order")
            }
                .navigationBarTitle("My Orders")
        }

The navigation bar should contain a button the waiter can use to add a new order.

List {
     Text("Sample order")
            }
                .navigationBarTitle("My Orders")
                .navigationBarItems(trailing: Button(action: {
     print("Open order sheet")
                }, label: {
    Image(systemName: "plus.circle")
                        .resizable()
                        .frame(width: 32, height: 32, alignment: .center)
                }))

The preview canvas should look like this so far:

When we tap on the button we want to open a second view. For this, we create a new SwiftUI file and name it OrderSheet. We want to display the OrderSheet as a modal view. To do this, we add a State to our ContentView to control when the OrderSheet should be displayed.

struct ContentView: View {
@State var showOrderSheet = false
    var body: some View {
       //...
    }
}

To display the OrderSheet as a modal view, we use the .sheet modifier.

List {
    Text("Sample order")
            }
//...
                .sheet(isPresented: $showOrderSheet) {
OrderSheet()
                }

Whenever the showOrderSheet state is true the OrderSheet overlays the ContentView. Now we can toggle the showOrderSheet State from our navigation bar button.

.navigationBarItems(trailing: Button(action: {
                    self.showOrderSheet = true
                }, label: {
                    Image(systemName: "plus.circle")
                        .resizable()
                        .frame(width: 32, height: 32, alignment: .center)
                }))


For our OrderSheet view’s body, we’ll be using the Form view in SwiftUI and a picker with the different pizza options. The number of slices that the customer wishes to order will be represented by a stepper. Finally, we use a text field where the user can write the table number for the order.

We want to save the data after the user taps on the “Add Order” button.

For the OrderSheet’s UI, you can use copy & paste the following code:

struct OrderSheet: View {
       
    let pizzaTypes = ["Pizza Margherita", "Greek Pizza", "Pizza Supreme", "Pizza California", "New York Pizza"]
    
    @State var selectedPizzaIndex = 1
    @State var numberOfSlices = 1
    @State var tableNumber = ""
    
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Pizza Details")) {
                    Picker(selection: $selectedPizzaIndex, label: Text("Pizza Type")) {
                        ForEach(0 ..< pizzaTypes.count) {
                                Text(self.pizzaTypes[$0]).tag($0)
                        }
                    }
                    
                    Stepper("\(numberOfSlices) Slices", value: $numberOfSlices, in: 1...12)
                }
                
                Section(header: Text("Table")) {
                    TextField("Table Number", text: $tableNumber)
                        .keyboardType(.numberPad)
                    
                }
                
                Button(action: {
                    print("Save the order!")
                }) {
                    Text("Add Order")
                }.navigationBarTitle("Add Order")
                
            }
        }
    }
}

The OrderSheet’s preview should now look like this:

Saving orders 🆕

Great, we’re done with our app’s UI, but nothing gets saved and persisted yet. To change this we need to access to the managed object context first to persistently save a created order. Since, as we saw in the beginning, the managed object context is injected in our environment, we can simply access it by using the @Environment property wrapper inside our OrderSheet above its States.

@Environment(\.managedObjectContext) var managedObjectContext

Bug-Alert 🐞: Unfortunately there’s a bug in the current version of SwiftUI. Since we injected into the SceneDelegate’s scene function, the managed object context should be accessible globally by all views by using the corresponding environment property. However, doing this inside popover views like our OrderSheet won’t work properly. What we have to do is to pass the managedObjectContext that gets initialised inside the scene function downwards to our OrderSheet. Thus, we have to use the @Environment property inside our ContentView, too …

@Environment(\.managedObjectContext) var managedObjectContext
    
@State var showOrderSheet = false
//…

… and pass it to the OrderSheet inside the .sheet modifier:

.sheet(isPresented: $showOrderSheet) {
    OrderSheet().environment(\.managedObjectContext, self.managedObjectContext)
                }

Now that our OrderSheet has access to the device’s “scratchpad” we are ready to create an Order instance inside our “Add order Button”. But first, we want to make sure, that the tableNumber String is not empty by using a guard statement.

Button(action: {
guard self.tableNumber != "" else {return}
let newOrder = Order(context: self.managedObjectContext)
                    newOrder.pizzaType = self.pizzaTypes[self.selectedPizzaIndex]
                    newOrder.orderStatus = .pending
                    newOrder.tableNumber = self.tableNumber
                    newOrder.numberOfSlices = Int16(self.numberOfSlices)
                    newOrder.id = UUID()
                }) {
Text("Add Order")
                }

Then we’re trying to save the created order. If that fails, we print the corresponding error.

//...
newOrder.id = UUID()
do {
  try self.managedObjectContext.save()
  print("Order saved.")
 } catch {
  print(error.localizedDescription)
  }

After the new order got saved, we want to close the OrderSheet modal view. We can do this by inserting the following environment property into our OrderSheet.

@Environment (\.presentationMode) var presentationMode

By referring to this property we can manually close the modal view:

do {
try self.managedObjectContext.save()
print("Order saved.")
self.presentationMode.wrappedValue.dismiss()
} catch {
print(error.localizedDescription)
}

Okay, let’s run our app to see if that works. Note that the preview canvas isn’t able to simulate Core Data’s functionality. Therefore, we need to run the app in the regular simulator. Click on the navigation bar button and fill out the OrderSheet form. Then click on “Add Order”. We saved the created order and dismisses the OrderSheet. However, our ContentView’s List is still displaying its sample row.

Fetching and displaying stored orders 📖

To change this, our ContentView needs to read out the saved orders. Achieving this functionality is quite simple by using a @FetchRequest. Below the ContentView’s environment property insert the following lines of code:

@FetchRequest(entity: Order.entity(),
                  sortDescriptors: [],
                  predicate: NSPredicate(format: "status != %@", Status.completed.rawValue))
    
    var orders: FetchedResults<Order>

The @FetchRequest permanently reads out the persistent storage for fetching stored orders from it. With the predicate argument, we filter out all orders already completed since we don’t want them to display in our ContentView’s List. The @FetchRequest then passes the retrieved orders to the orders property. Whenever we save a new order the @FetchRequest will notice and add it to the orders data set. Similar to the State functionality, this causes the ContentView to rebuild itself.

Now we’re ready to display the fetched data inside our List, like this:

List {
                ForEach(orders) { order in
                    HStack {
                        VStack(alignment: .leading) {
                            Text("\(order.pizzaType) - \(order.numberOfSlices) slices")
                                .font(.headline)
                            Text("Table \(order.tableNumber)")
                                .font(.subheadline)
                        }
                        Spacer()
                        Button(action: {print("Update order")}) {
                            Text(order.orderStatus == .pending ? "Prepare" : "Complete")
                                .foregroundColor(.blue)
                        }
                    }
                }
            }

Hint: The reason why we use a ForEach loop inside the List instead of inserting the orders data set in the List itself, will become clear when it comes to deleting orders.

When we run our app again, we see that our @FetchRequest successfully retrieves the just saved order from the persistent storage.

Updating orders 🔄

The button on each row’s right side can be used to update the status of the particular order. When we add a new order, its status is pending. Therefore the button reads “Prepare”. When the user taps on the button we want to update the status to preparing and the button should read “Complete”. When the user taps again, we want the order’s status to be completed, which causes the @FetchRequest to filter the order out.

To implement this functionality, we add the following function below our ContentView’s body.

func updateOrder(order: Order) {
let newStatus = order.orderStatus == .pending ? Status.preparing : .completed
        managedObjectContext.performAndWait {
            order.orderStatus = newStatus
try? managedObjectContext.save()
        }
    }

We can call the updateOrder function from our row’s button with passing the particular order instance:

Button(action: {self.updateOrder(order: order)}) {
Text(order.orderStatus == .pending ? "Prepare" : "Complete")
                                .foregroundColor(.blue)
                        }

Now we can run the app and tap on the “Prepare” button to mark the currently pending order as prepared. If we then click on “Complete”, the Order will be filtered out and eventually removed from our List.

Deleting orders from the persistent storage 🗑

Deleting stored data is almost as simple as updating it. All we have do is to delete the particular object from the managed object context, and then, since the @FetchRequest will automatically detect that the object was deleted, it will update our ContentView accordingly and remove the row from the table with a nice default animation.

To let the user delete rows we add the .onDelete modifier to the ForEach row. We can’t apply this modifier to Lists, this is why we use a ForEach loop inside the List.

ForEach(orders) { order in
                    //...
                }
                    .onDelete { indexSet in
                        for index in indexSet {
                            self.managedObjectContext.delete(self.orders[index])
                        }
                    }

The .onDelete modifier detects the row(s) the user wants to delete by swiping and uses there index/indices to remove the corresponding order entries from the managedObjectContext.

If we run the application now, we can see that we can easily delete the order by swiping a row.


Conclusion 🎊

That’s it, we’re finished with our small pizza restaurant app! We learned how we can use Core Data in SwiftUI to store data persistently. We talked through all basic CRUD operations: Creating, reading, updating and deleting data. We also understood what a managedObjectContext is and how we can fetch stored data by using SwiftUI’s @FetchRequest.

We’ve uploaded the whole source code of this app to GitHub.

mIf you liked this tutorial, feel free to check out our Mastering SwiftUI eBook. In this book, we also created a To-Do app by using the mentioned Core Data functionalities!

I hope you enjoyed this tutorial! If you want to learn more about SwiftUI, check out our other tutorials! Also make sure you follow us on Instagram and subscribe to our newsletter to not miss any updates, tutorials and tips about SwiftUI and more!

Categories
Uncategorized

How to parse JSON with Swift 5

In this tutorial, we talk about a very important concept when it comes to working with APIs: Parsing JSON. We’ll talk about how to download JSON data from an URL and how to make use of it by encoding it. We learn all of this the easy way with using the Codable protocol!

What we want to achieve 🚀

Supposed we have an API that allows us to download images via their url’s and the usernames of the uploader, we want to create an app that displays that image with the according username. It should look similar to this:

The JSON data we use for this looks like this:

In this tutorial, we start with learning how to download the JSON data and how to parse it so we can extract the image URL’s and the usernames.

Creating the data model for our JSON data 🛠

Take a look at the JSON data above. It contains three entries (001, 002, and 003), where each has one imageURL and one uploaderName. To parse the JSON, we first need to define suitable data models for storing the parsed data later on.

Our data model we start with is the one we use for the entries’ content, the image data. We use a struct containing an imageURL and uploaderName property for this.

struct Image: Codable {
    let imageURL: String
    let uploaderName: String
}

Our second data model is for the entries itself. The only property of this model is a dictionary, where the key is the name of the entry (e.g. 001) and the value an instance of our just created Image model.

struct Entry: Codable {
    let images: [String: Image]
}

You see that we needed to create two different “model layers”, just as our JSON data has two “layers”.

Tip: Especially when it comes to more complex JSON data, you can use a tool such as quicktype, which automatically creates data models out of your JSON, ready for being used in your Swift project.

Downloading the JSON data ⬇️

Now we are ready to download our JSON data. I uploaded my sample JSON data to the web using this free tool. You can do this too if you want and then use the generated URL for retrieving the data. In real life you would use the API url from now on.

We start with creating a Swift URL object out of our URL.

if let url = URL(string: "http://www.json-generator.com/api/json/get/cfpeTpOFrC?indent=2") {
//
}

We can then retrieve the JSON data from the URL like this:

if let url = URL(string: "http://www.json-generator.com/api/json/get/cfpeTpOFrC?indent=2") {
   URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
//...
       }
   }.resume()
}

Parsing the JSON data 💡

To decode the JSON data we have to initialize a JSONDecoder.

if let url = URL(string: "http://www.json-generator.com/api/json/get/cfpeTpOFrC?indent=2") {
   URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
let jsonDecoder = JSONDecoder()
       }
   }.resume()
}

We can now parse the JSON by using the Decoder and our created data models. We do this inside a try-catch block to print out the error when something goes wrong with the parsing process.

if let url = URL(string: "http://www.json-generator.com/api/json/get/cfpeTpOFrC?indent=2") {
   URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
let jsonDecoder = JSONDecoder()
do {
let parsedJSON = try jsonDecoder.decode(Entry.self, from: data)
        } catch {
print(error)
        }
       }
   }.resume()
}

At this point the parsedJSON property is assigned to an instance of our Entry class. This means that the parsedJSON contains a dictionary, where the values are instances of the Image class.

Therefore we can cycle through every Image instance of our Entry’s dictionary and grab the imageURL and uploaderName of each one.

if let url = URL(string: "http://www.json-generator.com/api/json/get/cfpeTpOFrC?indent=2") {
   URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
let jsonDecoder = JSONDecoder()
do {
let parsedJSON = try jsonDecoder.decode(Entry.self, from: data)
for image in parsedJSON.images {
print(image.value.imageURL)
print(image.value.uploaderName)
            }
        } catch {
print(error)
        }
       }
   }.resume()
}

We could now use this data for downloading the images via the image URLs and displaying the according uploader user names. In the next tutorial (dropping in the very next days) we’ll see how to do this in SwiftUI. If you want to know how to download images in UIKit, take a look at this tutorial!

Conclusion 🎊

Great, we just learned how to retrieve and parse JSON data from the web by using the Codeable protocol and our own data models. You are now able to work with API’s that provide data via JSON.

If you want to see more, make sure you follow us on Instagram and subscribe to our newsletter to not miss any updates, tutorials and tips about SwiftUI and more!

Have any questions about this article? Write it in the comments below 👇

Categories
Uncategorized

How to use SwiftUI in a Playground

Xcode Playgrounds are a great way to test new ideas quickly and easily. You might have wondered if it is also possible to use SwiftUI views in a playground. And yes, it’s actually very straightforward 👇

Step 1: After you have created a playground, the first step is to import the necessary frameworks.

import UIKit
import PlaygroundSupport
import SwiftUI

Step 2: Next, you can create your SwiftUI view.

struct MyView: View {
    var body: some View {
        VStack {
            Text("Hello World!")
                .font(.title)
            Text("This a SwiftUI view")
        }
    }
}

Step 3: Before we can display this view, we need to convert it into an UIViewController. We do this by using an UIHostingController.

let viewController = UIHostingController(rootView: MyView())

Step 4: Finally, we use this view controller for the live view of our playground.

PlaygroundPage.current.liveView = viewController

If we run our playground now, we get our SwiftUI view displayed in a kind of preview simulator!

If you want to learn more about SwiftUI, make sure you follow us on Instagram and subscribe to our newsletter to not miss any updates, tutorials and tips!

Categories
Uncategorized

How to prevent the keyboard from hiding your SwiftUI view

To deliver a good user experience, you have to make sure that the user can see what he is typing. Therefore, it’s crucial to prevent the toggled keyboard from hiding your view’s content.


We can achieve this by offsetting the whole SwiftUI view by the height of the toggled keyboard. We could use a fixed value for this, but keep in mind that for example, the keyboard height of an iPhone 8 differs tremendously from the height of an iPad Pro. So how can we access the keyboard’s heigh and use it for offsetting our SwiftUI view? Well, here you go:

If you want to learn how to create a login screen like the one you see in the preview video, take a look at this tutorial or download the finished project.

Step 1: Create a new Swift file called KeyboardResponder and import the SwiftUI framework. Then create a class that adopts the ObservableObject protocol. Our KeyboardResponder needs to be observable so we can update the observing views when the keyboard got toggled.⠀

import Foundation
import SwiftUI

class KeyboardResponder: ObservableObject {

}

Step 2: For keeping track of the keyboard’s current height, we declare a @Published property and assign it to 0 by default. 0 represents that the keyboard is not opened. Whenever the keyboard shows up we will update the published property with the actual keyboard height which will cause the observing views to update themselves.⠀

class KeyboardResponder: ObservableObject {

    @Published var currentHeight: CGFloat = 0

}

Step 3: To achieve this, we can use the system’s built-in keyboardWillShowNotification and keyboardWillHideNotifaction. To handle these notifications, we need to initialise a NotificationCenter first.⠀

class KeyboardResponder: ObservableObject {
//...
    
var _center: NotificationCenter

    init(center: NotificationCenter = .default) {
        _center = center
    }
}

Step 4: Then we tell our notification center to listen to the system’s keyboardWillHide/Show notifications. Whenever the keyboards gets shown/hidden now, the Notification Center will execute the function targeted by the specific selector.⠀

init(center: NotificationCenter = .default) {
        _center = center
    //4. Tell the notification center to listen to the system keyboardWillShow and keyboardWillHide notification
        _center.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        _center.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }

Step 5: Until now, we didn’t implement the functions called by the selectors. Let’s change this by adding these to our KeyboardResponder. Whenever the keyboard gets toggled now, our Notification Center notices and calls the corresponding function. Inside this function, we update the currentHeight property by referring to the keyboard size. As said this will cause all observing views to update themselves.

@objc func keyBoardWillShow(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            withAnimation {
               currentHeight = keyboardSize.height
            }
        }
    }
@objc func keyBoardWillHide(notification: Notification) {
        withAnimation {
           currentHeight = 0
        }
    }

Step 6: To observe the KeyboardResponder we have to initialise it inside our SwiftUI view as an @ObservedObject.

struct ContentView: View {
//...
    
@ObservedObject var keyboardResponder = KeyboardResponder()
var body: some View {
//...
    }
}

Step 7: Finally we tell our view to have the same offset as the keyboard is high by referring to the currentHeight variable of the observed KeyboardResponder.

var body: some View {
        NavigationView {
//...
        }
            .offset(y: -keyboardResponder.currentHeight*0.9)
    }

If you now run your app and open the keyboard, for example by tapping on a text field, the whole content moves up so we’re able to read what we’re writing.

That’s it! I hope you enjoyed this brief SwiftUI tutorial! 🍀

You can download the full source code here.

By the way, did you already download our FREE SwiftUI Basics eBook? Make sure you click the link in the bio to check it out 🚀

Categories
Uncategorized

How to create a custom Tab Bar in SwiftUI

Welcome! In SwiftUI it’s super easy to add a simple tab bar to our app. However, we are limited to the default tab bar design that Apple provides us with. But the in-house tab bar can get boring and sometimes doesn’t offer the functionality we need for our app. In this tutorial, we will learn how to create our own fully customizable tab bar. We will not use a UITabBarController for this but implement the navigation logic only with SwiftUI and from scratch.

Our finished tab bar will look like this:


With the knowledge from this article you should be able to create almost any tab bar with SwiftUI!

Let’s get started! After opening Xcode 11 and creating a new SwiftUI Single View app, we can start preparing our custom tab bar. For the purpose of this tutorial, we can use the default ContentView.swift file.

Preparing our ContentView 👨‍💻

By using our tab bar, we will later be able to jump between two different views, a “Home” view and a “Settings” view. By default, the former should be displayed. This view should simply consist of a text reading “Home”. For this, we can adapt the default “Hello Word” Text accordingly.

struct ContentView: View {
    var body: some View {
       Text("Home")
    }
}

The tab bar should be displayed at the bottom of the screen. Therefore, we have to wrap our text view into a VStack. To make sure that the text is always centered, we add two spacers.

VStack {
            Spacer()
            Text("Home")
            Spacer()
        }

We are almost finished with the preparations. Finally, we just want to have the capability to know how high and wide the screen of the particular device the app runs on is. We need to know this in order to dynamically adjust the size of the tab bar. For this purpose, we wrap our VStack into a GeometryReader.

GeometryReader { geometry in
            VStack {
                Spacer()
                Text("Home")
                Spacer()
            }
        }

The GeometryReader reads out its parent view’s size dimensions. In our case, that’s the overall superview, the one that covers the entire screen.

We’re done with our preparations. Now it’s time to actually design our custom tab bar!

Designing our Tab Bar 🎨🖌

Our tab bar should contain three different icons that are arranged horizontally. Therefore, we insert a HStack into our VStack.

VStack {
    Spacer()
    Text("Home")
    Spacer()
    HStack {
                    
    }
}

The first icon is the “house” icon from the SF Symbols cataloge. The view around the icon should be a third as wide as the whole screen. We also apply some padding.

HStack {
    Image(systemName: "house")
        .resizable()
        .aspectRatio(contentMode: .fit)
        .padding(20)
        .frame(width: geometry.size.width/3, height: 75)
      }

Next, we can tell our HStack that it should always be a tenth of the height of the screen. We also create a white background with a smooth shadow effect.

HStack {
        //...
        }
            .frame(width: geometry.size.width, height: geometry.size.height/10)
            .background(Color.white.shadow(radius: 2))

Our tab bar slowly takes shape. However, it is (depending on the device the app runs on) slightly away from the bottom edge. This is because, by default, a SwiftUI view’s content stays inside the so-called safe area. This prevents our tab bar from reaching the lower edge of our screen. But we can explicitly tell our SwiftUI view to exceed the safe area’s lower boundary by adding the following modifier to the overall VStack:

GeometryReader { geometry in
            VStack {
                //...
                }
                    //...
            }.edgesIgnoringSafeArea(.bottom)
        }

Now our tab bar seamlessly attaches to the bottom of the device

Next, we can add the gear icon to our tab bar:

HStack {
        Image(systemName: "house")
             //...
        Image(systemName: "gear")
            .resizable()
            .aspectRatio(contentMode: .fit)
            .padding(20)
            .frame(width: geometry.size.width/3, height: 75)
        }

Between the home and the gear icon, we want to place a slightly shifted plus button. We create this by adding a ZStack between the two icons. The first view inside this ZStack should simply be a white circle:

ZStack {
      Circle()
          .foregroundColor(Color.white)
          .frame(width: 75, height: 75)
        }

On top of this circle, we stack our actual plus icon:

ZStack {
        Circle()
            .foregroundColor(Color.white)
            .frame(width: 75, height: 75)
        Image(systemName: "plus.circle.fill")
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(width: 75, height: 75)
            .foregroundColor(.blue)
      }

To make sure that the whole plus button is placed slightly above the tab view, we apply the .offset modifier to our ZStack. Our ZStack should be shifted upwards by the half the height of the tab bar. Since our tab view is one tenth as high as the screen, we write:

ZStack {
          //...
        }
          .offset(y: -geometry.size.height/10/2)

Awesome, we are already done with designing our own, custom tab bar. It’s simple as that! Your app preview should now look like this:

Implementing the navigation logic ⛓

Now it’s time to write the code for the navigation logic. How to navigate between views independently in SwiftUI is shown in detail in this tutorial. Therefore, we’ll keep it brief in the following.

To be able to switch back and forth between different views, we need a kind of a “manager” that tells our ContentView which view it should display (either the “Home” or the “Settings” view). For this purpose, we create a new File-New-File and select Swift file. We call this file ViewRouter.

Next, we import the SwiftUI and the Combine framework and create a class named ViewRouter that adapts the ObservableObject protocol.

import Foundation
import SwiftUI
import Combine

class ViewRouter: ObservableObject {
    
   
    
}

Within our ViewRouter, we need a variable which we can use to keep all observers up to date, which view should currently be displayed. Thus, we declare a variable currentPage. By default, we want to display the “home” view.

class ViewRouter: ObservableObject {
    
    @Published var currentView = "home"
    
}

With the @Published property wrapper, we notify all observing views to update themselves whenever the currentView variable changes.

That’s it! We can now use a ViewRouter instance within our ContentView and observe it by initializing it as an @ObservedObject.

struct ContentView: View {
    
    @ObservedObject var viewRouter = ViewRouter()
    
    var body: some View {
        //...
    }
}

As said, make sure you read this tutorial, if you don’t really understand the navigation logic we are using!

Depending on the viewRouter’s currentView variable we want to show either our “Home” text view or another simple Text view reading “Settings”. Therefore, we replace our current Text within the two spacers with the following statement.

if self.viewRouter.currentView == "home" {
                    Text("Home")
                } else if self.viewRouter.currentView == "settings" {
                    Text("Settings")
                }

We can now navigate to the settings view by adding a tap gesture to the gear icon that assigns the viewRouter’s currentView variable to “settings”.

 Image(systemName: "gear")
        //...
        .onTapGesture {
            self.viewRouter.currentView = "settings"
         }

Due to the @Published property wrapper’s functionality, this causes our observing ContentView to rebuild itself with eventually showing us the “Settings” Text view!

Let’s add the corresponding tap gesture to our house icon:

Image(systemName: "house")
        //...
        .onTapGesture {
              self.viewRouter.currentView = "home"
        }

Awesome, we’re now able to jump between the different views by tapping the tab bar’s home and gear icon!


To indicate the user which view is currently being shown, we can conditionally highlight the corresponding tab bar icon. For example, when our home view is currently being shown, we want the home icon to be black, otherwise we want it to be gray. We can achieve this by adding the following modifier to it:

Image(systemName: "house")
        //...
        .foregroundColor(self.viewRouter.currentView == "home" ? .black : .gray)
        .onTapGesture {
            self.viewRouter.currentView = "home"
        }

Let’s do the same for our gear icon!

Image(systemName: "gear")
    //...
    .foregroundColor(self.viewRouter.currentView == "settings" ? .black : .gray)
    .onTapGesture {
        self.viewRouter.currentView = "settings"
    }

Awesome, our tab bar now indicates which view is currently being shown!

Creating the pop up menu ➕

Last but not least, we want to display a cool pop-up menu when the user taps the plus sign icon.

For keeping track of whether the menu should be displayed, we add an according @State property to our ContentView, right below our @ObservedObject variable.

@State var showPopUp = false

When this State is true, the menu should be shown on top of our tab bar (we will shift its position in a moment). Therefore, we wrap our HStack into a ZStack.

ZStack {
    HStack {
      //...
      }
    }

For the menu, you can add the following struct right below your ContentViews_Previews struct:

struct PlusMenu: View {
    var body: some View {
        HStack(spacing: 50) {
            ZStack {
                Circle()
                    .foregroundColor(Color.blue)
                    .frame(width: 70, height: 70)
                Image(systemName: "camera")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .padding(20)
                    .frame(width: 70, height: 70)
                    .foregroundColor(.white)
            }
            ZStack {
                Circle()
                    .foregroundColor(Color.blue)
                    .frame(width: 70, height: 70)
                Image(systemName: "photo")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .padding(20)
                    .frame(width: 70, height: 70)
                    .foregroundColor(.white)
            }
        }
    }
}

If you want to learn, how floating menus can be created in SwiftUI, take a look at this tutorial!

Depending on the showPopUp State, we can now initialise the PlusMenu on top of our tab bar.

ZStack {
    if self.showPopUp {
        PlusMenu()
    }
    HStack {
        //...
    }
}

We shift the PlusMenu above our tab bar by using the .offset modifier again.

PlusMenu()
    .offset(y: -geometry.size.height/6)

We want to toggle the State by tapping on the plus icon. For this purpose, we use a tap gesture again.

ZStack {
    //...
  }
      .offset(y: -geometry.size.height/10/2)
      .onTapGesture {
          self.showPopUp.toggle()
      }

When we now tap on the plus icon of our tab bar, the floating menu gets displayed! We can animate this by wrapping the toggle statement inside a withAnimation statement.

withAnimation {
    self.showPopUp.toggle()
}

To replace the default fade animation with a catchier one, we can add the .transition modifier to our PlusMenu’s view with choosing the .scale option:

var body: some View {
        HStack(spacing: 50) {
            //...
        }
            .transition(.scale)
    }

Tap the plus icon again, to see how it looks!

Hint: Maybe your live preview doesn’t display the animation properly. In this case, run your app in the regular simulator.

Additionally, we want to rotate our plus icon when tapping on it. To do this, add the following modifier to the Image view inside the according ZStack.

Image(systemName: "plus.circle.fill")
     //...
     .rotationEffect(Angle(degrees: self.showPopUp ? 90 : 0))

Depending on whether the plus menu is being shown, this modifier rotates the plus icon by 90 degrees.

Our whole plus menu animation should now look like this:

Conclusion 🎊

Awesome, we’re now finished with creating our own, custom tab bar in SwiftUI! We’ve learned how to design a tab bar’s UI, how to implement a proper navigation logic and how to animate a cool pop-up menu. With this knowledge, you should be able to create your own, custom tab bar!

We’ve uploaded the whole source code of this app to GitHub.

I hope you enjoyed this tutorial! If you want to learn more about SwiftUI, check out our other tutorials! Also make sure you follow us on Instagram and subscribe to our newsletter to not miss any updates, tutorials and tips about SwiftUI and more!

Categories
Uncategorized

Stretchy Header and Parallax Scrolling in SwiftUI

Welcome! In this tutorial, we are going to create a sticky/stretchy header in SwiftUI. At the same time, we are learning how to equip certain views with a parallax scrolling effect. We are using those features to create a nice looking blog post screen as you see them in several news apps.

This is what we are going to achieve in this tutorial:


If not already done, create a new Xcode 11 project and choose Single View app. Make sure you’ve selected SwiftUI as the interface mode and create a new project. For the following steps, you can use the default ContentView.swift file.

We’ll use some images for our project, which you can download here and here. You can also usa a portrait image like this one. Note that the format and dimensions of the header image can affect the parallax effect, which you maybe already noticed in the preview video above.

Import the images into your Assets.xcassets folder and make sure you name them correctly.

Setting up the basic content layout 👨‍🎨

Before we get started with implementing our sticky header, we are going to design the basic layout of our blog post screen.

The whole content in our ContentView should be scrollable. For this purpose, we replace the default “Hello World” Text view with a ScrollView.

struct ContentView: View {
var body: some View {
        ScrollView {
        }
    }
}

Inside the ScrollView, we will place the headline and the content texts of our blog post screen. All of those objects should be aligned vertically. Therefore, we insert a VStack into our ScrollView and choose .leading as the alignment mode.

ScrollView {
    VStack(alignment: .leading) {

    }
}

Above the headline of our article, we want to contribute the author by naming him and showing his picture. To do this, we insert a HStack into our VStack. We fill this HStack with a small, rounded image and another VStack containing the author’s name as a Text.

VStack(alignment: .leading) {
                HStack {
                    Image("journalist")
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: 60, height: 60)
                        .clipped()
                        .cornerRadius(10)
VStack(alignment: .leading) {
                Text("Article by")
                            .font(.custom("AvenirNext-Regular", size: 15))
                            .foregroundColor(.gray)
Text("Johne Doe")
                            .font(.custom("AvenirNext-Demibold", size: 15))
                    }
                }
                  .padding(.top, 20)
            }

Hint: We are using “Avenir-Next” as a custom font family.To learn more about using custom fonts in SwiftUI, check out this instagram post.

This is what your app preview should show so far:

Below this HStack, we can now insert the headline and some meta information, for instance the date when the post was published and its reading length.

VStack(alignment: .leading) {
                HStack {
                    //...
                }
                    .padding(.top, 20)
                Text("Lorem ipsum dolor sit amet")
                    .font(.custom("AvenirNext-Bold", size: 30))
                    .lineLimit(nil)
                    .padding(.top, 10)
                Text("3 min read • 22. November 2019")
                    .font(.custom("AvenirNext-Regular", size: 15))
                    .foregroundColor(.gray)
                    .padding(.top, 10)
            }

Now we can insert the actual content into our VStack. For this purpose, we declare a constant outside of our ContentView that holds the post’s content as a multi-lined string.

let articleContent =

"""
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
At vero eos et accusam et justo duo dolores et ea rebum.
Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
"""

We can now insert a Text by referring to that constant.

//...
                Text("3 min read • 22. November 2019")
                    .font(.custom("AvenirNext-Regular", size: 15))
                    .foregroundColor(.gray)
                    .padding(.top, 10)
                Text(articleContent)
                    .font(.custom("AvenirNext-Regular", size: 20))
                    .lineLimit(nil)
                    .padding(.top, 30)

Let’s define a smaller width for the whole VStack:

VStack(alignment: .leading) {
                //...
            }
                .frame(width: 350)

Great, we are already done with setting up the basic layout of our blog post section. This is how your app should look like so far:

Next, we are going to implement our header image.

Implementing the Parallax Scrolling Header ↕️

On top of the VStack in our ScrollView, we wanna add our article’s header image. For making it stretchy and for implementing the parallax effect when scrolling we need to keep track of to current ScrollView’s position. For this purpose we can use a GeometryReader.

ScrollView {
            GeometryReader { geometry in
                
            }
            VStack(alignment: .leading) {
                //...
            }
                .frame(width: 350)
        }

The GeometryReader not only reads out the dimensions of its parent views (the ScrollView) but also allows us to keep track of the current ScrollView’s position. Inside the GeometryReader we insert an image view. The image should be as high and as wide as the GeometryReader.

GeometryReader { geometry in
                Image("header")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: geometry.size.width, height: geometry.size.height)
                    .clipped()
            }

Thus, we have to provide the GeometryReader with a certain frame.

GeometryReader { geometry in
                Image("header")
                    //...
            }
                .frame(height: 400)

Hint: When we are just determining the height, the GeometryReader view will be as wide as possible.

In order for our header image to fill out the whole screen, we have to tell our ScrollView that it should reach to the top, meaning that it should exceed to so-called safe area. We can do this, by adding the following modifier to our ScrollView:

ScrollView {
            //...
        }
            .edgesIgnoringSafeArea(.top)

Awesome, this is what your preview should now look like

When scrolling down the header remains static, meaning that we’ve not created any parallax effect yet. Fortunately, doing this is pretty simple. We just need to tell our header image view to continuously offset its position while we are scrolling up and down. We can do this by adding the following modifier right before the .clipped modifier.

Image("header")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: geometry.size.width, height: geometry.size.height)
                    .offset(y: geometry.frame(in: .global).minY/9)
                    .clipped()

We are now determining the vertical offset of our header image by checking the currently (vertical) position of our ScrollView. We are accessing the current vertical position by referring to the minY value of our GeometryReader. By doing this, we make sure our parallax view “moves” accordingly while we are scrolling. By the way: You can divide the minY value by a certain factor to control the magnitude of the parallax effect. In our example, we are dividing by the value 7.⠀

When we now run our app in live mode and scroll down, we notice that the header is also “moving” continuously. A pretty neat parallax effect!

Making our header stretchy 🦒

To make our header image stretchy, we need to find a way to expand the height of it and pushing it to the top when scrolling “over the top” of our ScrollView.

The first thing we have to do is to recognize when the upper boundary of the ScrollView is exceeded. We can this out by using the minY value of our GeometryReader again. If the upper boundary of the ScrollView is exceeded, the vertical scroll position must be positive. In the initial state, the minY value is zero and as we scroll down it gets more and more negative.

We only want to show our existing header image with the parallax effect while scrolling downwards. So only if the minY of our GeometryReader is negative or zero .

GeometryReader { geometry in
                VStack {
                    if geometry.frame(in: .global).minY <= 0 {
                        Image("header")
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: geometry.size.width, height: geometry.size.height)
                        .offset(y: geometry.frame(in: .global).minY/9)
                        .clipped()
                    }
                }
            }

Hint: We have to wrap the if-statement into a container view in order to work. In our example, we use a VStack, but you could also use any other container view types.

If where are scrolling “over the top”, meaning when the GeometryReader’s minY is positive, we want to show our image header as well but with some modifications, we need for making it stretchy.

if geometry.frame(in: .global).minY <= 0 {
                        //...
                    } else {
                        Image("header")
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: geometry.size.width, height: geometry.size.height)
                            .clipped()
                    }

The first modification we need is to make our header image the higher the further we scroll “over the top”. We can achieve this by adding the current minY value of our ScrollView’s GeometryReader to the height of our Image’s .frame modifier.

Image("header")
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: geometry.size.width, height: geometry.size.height + geometry.frame(in: .global).minY)
                            .clipped()

If we now run our app in the live preview and start scrolling over the upper boundary of the ScrollView, our header begins stretching. However, it also reaches into our blog posts content.


Instead we want our header to be “glued” to the top of our screen. We can do this by offsetting it depending on the ScrollView’s position like this:

Image("header")
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: geometry.size.width, height: geometry.size.height + geometry.frame(in: .global).minY)
                            .clipped()
                            .offset(y: -geometry.frame(in: .global).minY)

If we now run our app again, the header not only stretches while scrolling over the upper boundary but also stays on the very top. In addition, when the maximum size of the header’s image is reached, it begins zooming in. This is due to the .fill option we have chosen for our Image’s .aspectRatio modifier.

Conclusion 🎊

Awesome, we just learned how to apply a scrolling parallax effect to our SwiftUI app. By working with the GeometryReader’s minY value, that we used for reading out the current vertical scroll position, we also saw how to add a stretchy header to our app.

The complete source code for the app can be found here.

I hope you enjoyed this tutorial! If you want to learn more about SwiftUI, check out our other tutorials! Also make sure you follow us on Instagram and subscribe to our newsletter to not miss any updates, tutorials and tips about SwiftUI and more!