UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Project 11 Challenge

Forums > 100 Days of SwiftUI

I'm trying to complete the first challenge at the end of project 11.

The challenge is... "Right now it’s possible to select no genre for books, which causes a problem for the detail view. Please fix this, either by forcing a default, validating the form, or showing a default picture for unknown genres – you can choose."

So, I have chosen to try to make it show a default Image when the book does not have a genre selected.

I have added the image that I want to use to my asset catalogue and it has the file name "UnknownGenre.jpg"

Mainly, I'm looking at this part of the code in my DetailView.swift file, but I have included the full code from that file below for context.

This is the first project where we learn to use CoreData and Book is an entity in the data model.

ZStack(alignment: .bottomTrailing) {
                    Image(self.book.genre ?? "UnknownGenre")
                        .frame(maxWidth: geometry.size.width)

                    Text(self.book.genre?.uppercased() ?? "UNKNOWN GENRE")
                        .font(.caption)
                        .fontWeight(.black)
                        .padding(8)
                        .foregroundColor(.white)
                        .background(Color.black.opacity(0.75))
                        .clipShape(Capsule())
                        .offset(x: -5, y: -5)
}

This is the way that we were instructed to create the ZStack in this view originally, but the ?? operators don't seem to do the job that they are intended to do here. If no genre was entered when the book was created, then self.book.genre still seems to hold an empty string rather than nil, which results in my program trying to find an image named "" (empty string) in my assets catalogue, and using "" (empty string) in the Text view.

I made it work by force unwrapping the genre, then checking if it is equal to the empty sting and using the ternary operator to allow me to set the string to what I need it to be...

ZStack(alignment: .bottomTrailing) {
                    Image(self.book.genre! == "" ? "UnknownGenre" : self.book.genre ?? "UnknownGenre")
                        .frame(maxWidth: geometry.size.width)

                    Text(self.book.genre! == "" ? "UNKNOWN GENRE" : self.book.genre?.uppercased() ?? "UNKNOWN GENRE")
                        .font(.caption)
                        .fontWeight(.black)
                        .padding(8)
                        .foregroundColor(.white)
                        .background(Color.black.opacity(0.75))
                        .clipShape(Capsule())
                        .offset(x: -5, y: -5)
}

but this just seems like a really messy solution, and I think that there must be some better way of doing this.

Any suggestions would be appreciated.

import CoreData
import SwiftUI

struct DetailView: View {
    @Environment(\.managedObjectContext) var moc
    @Environment(\.presentationMode) var presentationMode
    @State private var showingDeleteAlert = false

    let book: Book

    static let entryDateFormat: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        return formatter
    }()

    var body: some View {
        GeometryReader { geometry in
            VStack {
                ZStack(alignment: .bottomTrailing) {
                    Image(self.book.genre ?? "UnknownGenre")
                        .frame(maxWidth: geometry.size.width)

                    Text(self.book.genre?.uppercased() ?? "UNKNOWN GENRE")
                        .font(.caption)
                        .fontWeight(.black)
                        .padding(8)
                        .foregroundColor(.white)
                        .background(Color.black.opacity(0.75))
                        .clipShape(Capsule())
                        .offset(x: -5, y: -5)
                }

                Text(self.book.title ?? "Unknown title")
                    .font(.largeTitle)

                Text(self.book.author ?? "Unknown author")
                    .font(.title)
                    .foregroundColor(.secondary)

                Text("\"\(self.book.review ?? "No review")\"")
                    .padding()

                Text(self.book.date ?? Date(), formatter: Self.entryDateFormat)
                    .foregroundColor(.blue)

                RatingView(rating: .constant(Int(self.book.rating)))
                    .font(.largeTitle)
                    .padding()
            }

        }
        .alert(isPresented: $showingDeleteAlert) {
            Alert(title: Text("Delete Book"), message: Text("Are you sure?"), primaryButton: .destructive(Text("Delete")) {
                    self.deleteBook()
                }, secondaryButton: .cancel()
            )
        }
        .navigationBarItems(trailing: Button(action: {
            self.showingDeleteAlert = true
        }) {
            Image(systemName: "trash")
        })
    }

    func deleteBook() {
        moc.delete(book)
        try? self.moc.save()
        presentationMode.wrappedValue.dismiss()
    }
}

struct DetailView_Previews: PreviewProvider {
    static let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)

    static var previews: some View {
        let book = Book(context: moc)
        book.title = "Text book"
        book.author = "Test author"
        book.genre = "Fantasy"
        book.rating = 4
        book.review = "This was a great book; I really enjoyed it."
        book.date = Date()

        return NavigationView {
            DetailView(book: book)
        }
    }
}

2      

I would create a computed property in an extension on Book that returns "Unknown Genre" if genre is an empty string or nil. Something llike this:

extension Book {
    var genreImage: String {
        if let genre = genre {
            return genre
        } else {
            return "Unknown genre"
        }
    }
}

2      

It turns out that he teaches us this technique in the next project (Project 12)

Thanks.

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.