SwiftUI: Making Views More Dynamic and Reusable

How to use parameters and bindings to make views more dynamic, reusable, and testable

In this series, we’re learning about SwiftUI by making a profile page. And so far, we’ve accomplished a very simple—yet stylish—profile for ourselves in just a few paragraphs of code!

To get caught up, you can check out parts 1 and 2 of the series here:

Returning to the screenshot of our profile page above— this is just a static profile page for ourselves. Today, we’ll cover how we can make our UI more dynamic by allowing it to take in parameters and Bindings so we can make it work for any person we pass in!

Parameterizing our Custom View

First, let’s identify the areas we want to parameterize:

  • Name
  • Subtitle
  • Description
  • Picture

Knowing that, let’s pull out the currently hardcoded values and replace them with variables/parameters:

And:

Then, we swap out our hardcoded values in our views (e.g. Text(name)). After we do all of this, we should see that nothing has changed in our live preview. Great!

Passing Down Values

Ultimately, we want to make our ProfilePage reusable, which means passing our dynamic information into the View. Let’s see how we can do this by passing in our profile picture name from ContentView.

First, remove the value of profilepic in ProfilePage and declare it as a String: var profilepic: String

Then, in our ContentView, you’ll see we have an error in our ProfilePage() declaration. It’s expecting a value to be passed in for profilepic. So let’s change it to: ProfilePage(profilepic: “profilepic”). Running in live preview should again yield no new results, but no errors either.

We’ve officially set up the dynamic parts of our app with variables/parameters and have even set up a mechanism for passing values from one View to another. As we look at ProfileInformation though, we see that to achieve our goal, we’d need to pass 3 variables through 2 levels ofour View hierarchy. This could lead to duplicate code. The solution to this problem? Classes.

Creating a Class

Using a class will help us model the data we want to use in our views. It will group our information, which will allow us to create multiple instances of our class, allowing us to easily set up multiple profiles.

So let’s get started by right-clicking our AwesomeSwiftUI folder in the Project Navigator and adding a new Swift file. Let’s name it “Profile”. Once the file opens, let’s create a new class called Profile and give it our 4 variables:

While we’re at it, let’s also make a quick constructor so we can easily make Profiles:

Now, let’s head back to our ContentView. There, let’s declare a new Profile using our initializer:

In ProfilePage, let’s replace var profilepic: String with var profile: Profile. This means we’ll need to change our declaration to ProfilePage(profile: profile). Lastly, where we were calling profilepic, we simply add profile before it, like this: Image(profile.profilepic).

Running our live preview, again we should see no changes to our UI, but also no errors.

Now, to pass our info down to ProfileInformation, we can either pass the info from our profile into the params we already have set up:ProfileInformation(name: profile.name, …).

Or we could remove those and continue passing our Profile in, having ProfileInformation accept Profile instead and adding profile, where we called our variables:

Yet again, running our live preview should result in no UI changes, but also no errors.

Testing Reusability

Now that we have a flow of values going down through the hierarchy of our views, lets test it’s reusability! And what better way than by creating another Profile… in the same view!

That’s right! Create a new profile in ContentView called profile2 and fill it out with new data (note: unless you add another picture to our Assets.xcassets, then just use the same String as our other profiles picture).

Then, in our body, wrap our ProfilePage(profile: profile) in a VStack, and then add ProfilePage(profile: profile2) inside the stack!

Behold! We see double!

This shows just a taste of how incredibly flexible and reusable SwiftUI can be. If we simply provide the data, using a view over and over is as simple as feeding it that data. We’ll show how that becomes even more powerful when we talk about lists, navigation, and detail views!

Here’s the completed code for this tutorial:

import SwiftUI

struct ContentView: View {
    var profile: Profile = Profile(name: "Danny", subtitle: "Awesome iOS Developer", description: "Danny loves SwiftUI and thinks it's the future of iOS Development!", profilePic: "profilepic")
    var profile2: Profile = Profile(name: "George", subtitle: "An OK iOS Developer", description: "George should love SwiftUI and think that it's the future of iOS Development!", profilePic: "profilepic")
    
    var body: some View {
        VStack {
            ProfilePage(profile: profile)
            ProfilePage(profile: profile2)
        }
    }
}

struct ProfileInformation: View {
    var profile: Profile
    
    var body: some View {
        VStack{
            Text(profile.name)
                .font(.largeTitle)
            Text(profile.subtitle)
                .font(.title)
            Text(profile.description)
                .font(.body)
        }
    }
}

struct ProfilePage: View {
    var profile: Profile
    
    var body: some View {
        VStack {
            Image(profile.profilePic)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .clipShape(Circle())
                .overlay(Circle().stroke(Color.gray, lineWidth: 10))
            ProfileInformation(profile: profile)
        }
    .padding()
    }
}

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

Tip for the Road: Utilizing PreviewProvider

Let’s say we wanted to make our view totally dynamic by not having ANY hardcoded values inside our UI code (e.g. the way we have profile initialized in ContentView). This is where the PreviewProvider struct that we’ve been ignoring down in the bottom of our code comes into play.

The sole purpose of the PreviewProvider is to feed any necessary parameters into our view so the live preview can render using that data (even if the source of data changes in our actual application code. We’ll get into that in the future.).

To demonstrate, copy profile along with its instantiation and paste it into ContentView_Previews. Then remove the instantiation in ContentView. This will produce an error back in the PreviewProvider because it’s expecting you to pass a Profile into ContentView. Do so by adding profile as the parameter: ContentView(profile: profile).

One more time, our live preview should look exactly the same with no build errors. The major difference now is no data lives inside any of our Views, making them truly dynamic.

Fritz

Our team has been at the forefront of Artificial Intelligence and Machine Learning research for more than 15 years and we're using our collective intelligence to help others learn, understand and grow using these new technologies in ethical and sustainable ways.

Comments 0 Responses

Leave a Reply

Your email address will not be published. Required fields are marked *

wix banner square