M6L7-8 : Links, Groups, and Fixing Errors

Finding myself struggling quite a bit with the foibles of Xcode, whether it be taking an age to fire up the simulator, the simulator not playing well with the certificates from Proxyman, or Xcode not talking properly with GitHub, it’s with some relief that the most recent two lessons have been about creating the views.

These lessons have been more about covering familiar ground, reinforcing elements we’ve learned about and adding new ones.

The 10 Element Limit

SwiftUI apparently has a ten element limit to a view, making it difficult to create more complicated views such as the “City Sights” app we’re creating. The solution, we learned in Lesson 8 of Module 6 from CodeWithChris, is to use a new Group type to contain them, such as:

Group { 
    Text(business.name!)
        .font(.largeTitle)
        .padding()
    if business.location!.displayAddress != nil {
        ForEach(business.location!.displayAddress!, id: \.self) { displayLine in
            Text(displayLine)
                .padding(.horizontal)
        }
    }
    Image("regular_\(business.rating ?? 0)")
        .padding()
    Divider()

    HStack {
        Text("Phone:")
            .fontWeight(.bold)
        Text(business.displayPhone ?? "")
        Spacer()
        Link("Call", destination: URL(string: "tel:\(business.phone ?? "")")!)
    }
    .padding()
    Divider()

    HStack {
        Text("Reviews:")
            .fontWeight(.bold)
        Text(String(business.reviewCount ?? 0))
        Spacer()
        Link("Read", destination: URL(string: "\(business.url ?? "")")!)
    }
    .padding()
    Divider()

    HStack {
        Text("Website:")
            .fontWeight(.bold)
        Text(business.url ?? "")
            .lineLimit(1)
        Spacer()
        Link("Visit", destination: URL(string: "\(business.url ?? "")")!)
    }
    .padding()
    Divider()
} 

URL Links

Something new we learned today was how to convert URL and other links into clickable format for the user to click.

For URLs, it’s pretty simply just doing this:

Link("Visit", destination: URL(string: "\(business.url ?? "")")!)

(Note the final ! is required because URL() needs to be “unwrapped”.)

For a telephone number that will launch the phone app on the user’s iPhone, we use this:

Link("Call", destination: URL(string: "tel:\(business.phone ?? "")")!)

We haven’t yet used any other variants but, according to the official Apple documentation, the following are also available:

Link("Email", destination: URL(string: "mailto:\(business.email ?? "")")!)
Link("FaceTime", destination: URL(string: "facetime://\(business.email ?? "")")!)
Link("FaceTime audio", destination: URL(string: "facetime-audio://\(business.email ?? "")")!)
Link("SMS", destination: URL(string: "sms:\(business.phone ?? "")")!)

This is not an exhaustive list, and there are others for Map links, YouTube links, Appstore links, etc, and it’ll be quite fascinating to see this in operation when we get to them.

Debugging

As a point of note that I am actually learning something, I was pleased that I was able to correct an Xcode error that didn’t show up in Chris’ lesson until he tried to run the app in Xcode.

Not only did I understand what the error was telling me, but I also knew how to fix it, re:

The error is telling us that business.reviewCount is an integer (number), and the Text() element expects a string. On spotting the error later, Chris said he believed SwiftUI was capable enough to work out what was wanted, but I think we’re all starting to get a grip on the foibles of Swift not doing what we expect it to do by this time.

Knowing this, the solution was fairly simple. We handle business.reviewCount like an integer first, re:

business.reviewCount ?? 0

This code is known as the nil coalescing operator, which means to return the bit nefore the ?? if it exists and is not null/nil, if the bit after the ?? if it doesn’t or is null/nil. We then wrap whatever integer return we get in String() to convert the output into a string, re:

String(business.reviewCount ?? 0)

So the output is now either the integer expressed as a String, or zero expressed as a String. And now Swift is happy because Text() is able to display it without error, because it’s a string.

Okay, it’s only a little thing but I’m pleased that I was able to understand the error and “fix it on the fly” because it proves that some of this is starting to sink in.