Module 2 Lesson 16

Hello Chris, I have spent considerable amount of time trying to get this program to work. Can you assist please.
//
// Card.swift
// Quotes
//
// Created by Jaime Fernandes on 12/11/21.
//

import SwiftUI

struct Card: View {

var author:Author

var body: some View {
    
    ZStack {
        //Background Image
        Image(author.image)
            .resizable()
            .aspectRatio(contentMode: .fill)
            .cornerRadius(15)
        //Quote if there is at least 1
        if author.quotes.count > 0 {
            Text(author.quotes[0])
                .font(.largeTitle)
                .fontWeight(.bold)
        }
            // Name
        Text("- " + author.name)
    }
    .padding([.top, .leading], 20.0)
        .shadow(color: .black, radius: 10, x: 2, y: 2)
}
    .foregroundColor(Color.white)
    .frame(width: .none, height: 400, alignment: .centre)
    .clipped()
    .cornerRadius(15)
    .padding([.leading, .trailing])

}

struct Card_Previews: PreviewProvider {

static var previews: some View {
    Card(author: Author.testData())
}

}
//
// ContentView.swift
// Quotes
//
// Created by Jaime Fernandes on 12/11/21.
//

import SwiftUI

struct ContentView: View {

@ObservedObject var model = AuthorModel()

var body: some View {
    
    //NavigationView to go into detailview
    NavigationView {
        
        ScrollView {
            
            // VStack to group all the cards
            VStack(alignment: .leading, spacing: 20) {
                
                //Card for each author
                ForEach(model.authors) { a in
                    
                    //Link to detail view
                    NavigationLink(
                    destination: DetailView(author: a),
                                    label: {
                  //Each author card in the scrollview
                        Card(author: a)
            })
                }
        }
    }
            .navigationBarTitle("Authors")
}

}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
//
// DetailView.swift
// Quotes
//
// Created by Jaime Fernandes on 12/11/21.
//

import SwiftUI
import AVKit

struct DetailView: View {
var author: Author
var body: some View {

    //if no mediation set, can't display detail
    
    VStack(alignment: .leading, spacing: 20.0) {
        
        //Author name
        Text(author.name)
            .font(.largeTitle)
            .fontWeight(.bold)
        // Quotes
        ForEach (author.quotes, id: \.self) { quote in Text(quote)
            
        }
        Spacer()
    }.padding
}

}

struct DetailView_Previews: PreviewProvider {
static var previews: some View {
DetailView(author: Author.testData())
}
}

Can you post your JSON file, your data model (Author struct) and your AuthorModel.

To format the code nicely, place 3 back-ticks ``` on the line above your code and 3 back-ticks ``` on the line below your code. The 3 back-ticks must be the ONLY characters on the line. The back-tick character is located on the same keyboard key as the tilde character ~ (which is located below the Esc key). You can also highlight an entire code block and click the </> button on the toolbar to wrap the block for you.

This also makes it easier for anyone assisting as they can copy the code and carry out some testing.

Thanks for helping, looks I will have to wrap my head around both lessons 14, 15 and 16.```
//
// Card.swift
// Quotes
//
// Created by Jaime Fernandes on 12/11/21.
//

import SwiftUI

struct Card: View {

var author:Author

var body: some View {
    
    ZStack {
        //Background Image
        Image(author.image)
            .resizable()
            .aspectRatio(contentMode: .fill)
            .cornerRadius(15)
        //Quote if there is at least 1
        if author.quotes.count > 0 {
            Text(author.quotes[0])
                .font(.largeTitle)
                .fontWeight(.bold)
        }
            // Name
        Text("- " + author.name)
    }
    .padding([.top, .leading], 20.0)
        .shadow(color: .black, radius: 10, x: 2, y: 2)
}
    .foregroundColor(Color.white)
    .frame(width: .none, height: 400, alignment: .centre)
    .clipped()
    .cornerRadius(15)
    .padding([.leading, .trailing])

}

struct Card_Previews: PreviewProvider {

static var previews: some View {
    Card(author: Author.testData())
}

}

[{
“name”: “Charles Swindoll”,
“quotes”: [“We are all faced with a series of great opportunities brilliantly disguised as impossible situations.”,
“Each day of our lives we make deposits in the memory banks of our children.”,
“The remarkable thing is, we have a choice everyday regarding the attitude we will embrace for that day.”,
“When you have vision it affects your attitude. Your attitude is optimistic rather than pessimistic.”
],
“image”: “ambition”
},
{
“name”: “Mahatma Gandhi”,
“quotes”: [“Live as though you were to die tomorrow.”,
“Change yourself your are in control.”
],
“image”: “calm”
},
{
“name”: “Martin Luther King”,
“quotes”: [“The time is always right to do what is right.”,
“Darkness cannot drive out darkness; only light can do that.”,
“Injustice anywhere is a threat to justice everywhere.”,
“Our lives begin to end the day we become silent about things that matter.”
],
“image”: “focus”
},
{
“name”: “Gautam Buddha”,
“quotes”: [“Radiate boundless love towards the entire world.”,
“A disciplined mind brings happiness.”,
“Give even if you only have a little.”,

        "Conquer anger with non - anger.",
        "Those who cling to perceptions and views wander the world offending people.",
        "Drop by drop is the water pot filled."
    ],

    "image": "reflect"
}

]

//
//  QuotesModel.swift
//  Quotes
//
//  Created by Jaime Fernandes on 19/10/21.
//



import Foundation

class AuthorModel: ObservableObject {
    
    @Published var authors = [Author]()
    
    init() {
        //create an instance of data servicce and get the data
        
        self.authors = getLocalJson()
    }
    
    func getLocalJson() -> [Author] {
        
        //Get a path in app bundle
        let pathString = Bundle.main.path(forResource: "Data", ofType: "json")
        
        if let pathString = pathString {
    
            //Create a URL object
            let url = URL(fileURLWithPath: pathString)
            
            do {
                //Create Data Object
                let data = try Data(contentsOf: url)
                
                //Dwecode the Json data
                let decoder = JSONDecoder()
                var authors = try decoder.decode([Author].self, from: data)
                
                //Add UUDD to author instances {
                for index in 0..<authors.count {
                    authors[index].id = UUID()
                }
                return authors
            }
    
            catch {
                print("error")
            }
}
     return [Author]()
    }
}

There is a problem with your Card View in that you have modifiers added to the closing brace of the body property of the View rather than to a View inside the body property.

The frame size needs to be applied to the image and the aspectRatio should be set to .fit otherwise it looks a bit weird. Remember that the order in which modifiers are applied really matters.

Try this in place of what you have.

struct Card: View {
    var author:Author

    var body: some View {

        ZStack {
            //Background Image
            Image(author.image)
                .resizable()
                .frame(height: 400)
                .aspectRatio(contentMode: .fit)
                .cornerRadius(15)
            //Quote if there is at least 1
            VStack(alignment: .leading) {
                if author.quotes.count > 0 {
                    Text(author.quotes[0])
                        .font(.largeTitle)
                        .fontWeight(.bold)
                }
                // Name
                Text("- " + author.name)
            }
            .padding(.horizontal)
        }
        .foregroundColor(Color.white)
        .padding(.horizontal, 20.0)
        .shadow(color: .black, radius: 10, x: 2, y: 2)
    }
}

Thanks Chris, still failed to build, have an issue with Models

//
//  Quotes.swift
//  Quotes
//
//  Created by Jaime Fernandes on 19/10/21.
//


import Foundation

class Author: Identifiable, Decodable {
    
    var id:UUID
    var name:String
    var quotes:[String]
    var image:String
    
    static func testData() -> Author {
        
        let testAuthor = Author(id: UUID(), name: "Test Name", quotes: ["quote1", "quote 2"], image: "ambition")
        
        return testAuthor
    }
}
        

Without knowing what the error message is I’m taking a guess that you need to set your var id: UUID to be optional since the JSON code does not have that property. Set it to:

var id: UUID?

Your getLocalJson() function in your AuthorModel will set that value after the json has been decoded.

It might be best to change your Author Model to a struct rather than a class so that you get an initialiser for free and you wont get the errors associated with creating the testData. You can leave it as a class but you have to create a member wise initialiser so your Author code would be like this:

class Author: Identifiable, Decodable {

    var id:UUID?
    var name:String
    var quotes:[String]
    var image:String

    init(id: UUID? = nil, name: String, quotes: [String], image: String) {
        self.id = id
        self.name = name
        self.quotes = quotes
        self.image = image
    }

    static func testData() -> Author {

        let testAuthor = Author(id: UUID(), name: "Test Name", quotes: ["The remarkable thing is, we have a choice everyday regarding the attitude we will embrace for that day.", "quote 2"], image: "ambition")

        return testAuthor
    }
}

I took the liberty of creating a longer quote in your test data so that when you view the Card in the Preview canvas you get a better idea of the layout.

1 Like

Hello Chris, Appreciate you taking the time to sort this, project still does not build.

Error 1 in Models “Static func may only be declared on a type”
Error 2 in Card "Type ‘Author’ has no member ‘testData’
Error 3 in DetailView "Failed to produce diagnostic for expression; please submit a bug report (Swift.org - Contributing) and include the project

I guess this must be taking a lot of your time. Thanks

I don’t mind helping. That’s what I and the other moderators are here for so it’s all good.

So paste your Author.swift file here again so that I can see where you are at with the model.

Also, in which sections of code are you getting those errors? Provide screenshots of the errors.

//
//  Quotes.swift
//  Quotes
//
//  Created by Jaime Fernandes on 19/10/21.
//


import Foundation

class Author: Identifiable, Decodable {
    
    var id:UUID?
    var name:String
    var quotes:[String]
    var image:String
    
    init(id: UUID? = nil, name: String, quotes: [String], image: String) {
            self.id = id
            self.name = name
            self.quotes = quotes
            self.image = image
        
        static func testData() -> Author {

                let testAuthor = Author(id: UUID(), name: "Test Name", quotes: ["The remarkable thing is, we have a choice everyday regarding the attitude we will embrace for that day.", "quote 2"], image: "ambition")

                return testAuthor
            }
        }
    }

//
//  QuotesModel.swift
//  Quotes
//
//  Created by Jaime Fernandes on 19/10/21.
//



import Foundation

class AuthorModel: ObservableObject {
    
    @Published var authors = [Author]()
    
    init() {
        //create an instance of data servicce and get the data
        
        self.authors = getLocalJson()
    }
    
    func getLocalJson() -> [Author] {
        
        //Get a path in app bundle
        let pathString = Bundle.main.path(forResource: "Data", ofType: "json")
        
        if let pathString = pathString {
    
            //Create a URL object
            let url = URL(fileURLWithPath: pathString)
            
            do {
                //Create Data Object
                let data = try Data(contentsOf: url)
                
                //Dwecode the Json data
                let decoder = JSONDecoder()
                var authors = try decoder.decode([Author].self, from: data)
                
                //Add UUDD to author instances {
                for index in 0..<authors.count {
                    authors[index].id = UUID()
                }
                return authors
            }
    
            catch {
                print("error")
            }
}
     return [Author]()
    }
}
//
//  DetailView.swift
//  Quotes
//
//  Created by Jaime Fernandes on 12/11/21.
//

import SwiftUI
import AVKit

struct DetailView: View {
    var author: Author
    var body: some View {
        
        //if no mediation set, can't display detail
        
        VStack(alignment: .leading, spacing: 20.0) {
            
            //Author name
            Text(author.name)
                .font(.largeTitle)
                .fontWeight(.bold)
            // Quotes
            ForEach (author.quotes, id: \.self) { quote in Text(quote)
                
            }
            Spacer()
        }.padding
    }
}

struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        DetailView(author: Author.testData())
    }
}

//
//  ContentView.swift
//  Quotes
//
//  Created by Jaime Fernandes on 12/11/21.
//

import SwiftUI

struct ContentView: View {
    
    @ObservedObject var model = AuthorModel()
    
    var body: some View {
        
        //NavigationView to go into detailview
        NavigationView {
            
            ScrollView {
                
                // VStack to group all the cards
                VStack(alignment: .leading, spacing: 20) {
                    
                    //Card for each author
                    ForEach(model.authors) { a in
                        
                        //Link to detail view
                        NavigationLink(
                        destination: DetailView(author: a),
                                        label: {
                      //Each author card in the scrollview
                            Card(author: a)
                })
                    }
            }
        }
                .navigationBarTitle("Authors")
    }
}
    }

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
//
//  Card.swift
//  Quotes
//
//  Created by Jaime Fernandes on 12/11/21.
//

import SwiftUI

struct Card: View {
    
    var author:Author
    
    var body: some View {
        
        ZStack {
            //Background Image
            Image(author.image)
                .resizable()
                .frame(height: 400)
                .aspectRatio(contentMode: .fit)
                .cornerRadius(15)
            //Quote if there is at least 1
            VStack(alignment: .leading) {
            if author.quotes.count > 0 {
                Text(author.quotes[0])
                    .font(.largeTitle)
                    .fontWeight(.bold)
            }
                // Name
            Text("- " + author.name)
        }
        .padding(.horizontal)
    }
    
        .foregroundColor(Color.white)
        .padding(.horizontal, 20.0)
        .shadow(color: .black, radius: 10, x:2, y:2)
}

    struct Card_Previews: PreviewProvider {
        
        static var previews: some View {
            Card(author: Author.testData())
    }
}
}

Thanks Again

In your Author.swift file you have defined a static function inside the init(). Move that out of the initialiser to just after it like this:

class Author: Identifiable, Decodable {

    var id:UUID?
    var name:String
    var quotes:[String]
    var image:String

    init(id: UUID? = nil, name: String, quotes: [String], image: String) {
        self.id = id
        self.name = name
        self.quotes = quotes
        self.image = image
    }

    static func testData() -> Author {

        let testAuthor = Author(id: UUID(), name: "Test Name", quotes: ["The remarkable thing is, we have a choice everyday regarding the attitude we will embrace for that day.", "quote 2"], image: "ambition")

        return testAuthor
    }
}

In your DetailView the .padding modifier is incomplete on the closing brace of the VStack. It should be .padding().

If you embed the VStack inside a ScrollView you can remove the Spacer() you have added. Should you have more than a screen full of quotes for a given author you can then scroll up and down to see them all.

You will also find it helpful to reformat your code periodically so that the lines of code are indented in accordance with the parent child relationship between elements in your code. To do that, select all your code Command + A and then press Control + I to re-indent. This will certainly make it easier for you to identify mismatching braces and make the structure of your code easier to read.

Thanks Again Chris, I have a Failed to build again. The Errors are:

no rule to process file ‘/Users/jaimefernandes/Desktop/Quotes copy/Quotes/Data/authors.json’ of type ‘text.json’ for architecture ‘arm64’
ld: entry point (_main) undefined. for architecture arm64

clang: error: linker command failed with exit code 1 (use -v to see invocation)

Try cleaning your project. The keyboard shortcut to do that is: Shift + Command + K

Hello Chris, with the issues I have faced with this challenge, do you suggest that I stop and revisit the earlier lessons or can I proceed further as it has been almost a month working on this challenge?

I did clean the project but the errors persists.

That’s really your decision. It might be helpful to go over the tutorials just in case there is something that you missed or that it clarifies some things that you covered.

The error message is a bit puzzling regarding the json file. What name have you given your json file that you added to your Bundle?

Can you take a screenshot of your Xcode window that shows the “Project Navigator” panel on the left?

With the Mac ScreenShot App, you can capture the entire window, the entire screen or a section of the screen.

If you just want a small section, press Shift + Command + 4 which will change your mouse pointer into a set of crosshairs. Click and drag diagonally across the screen to capture the section you want. When you let go of the mouse button, the image will be saved to your Desktop and will be named something like “Screen Shot YYYY-MM-DD at HH.MM.SS am/pm”.

Other screen selection options are available by pressing Shift + Command + 5. An options bar will appear near the bottom of the screen. You can even video record your screen.

Chris Hope this OK, I am learning more than Swift with your instructions

So does that mean that your Json file is named authors.json? If that is the case then your code in AuthorModel needs to be updated at the line:

let pathString = Bundle.main.path(forResource: "Data", ofType: "json")

Needs to be:

let pathString = Bundle.main.path(forResource: "authors", ofType: "json")

Just did that and the error persists

Can you compress your project into a zip file and post it to DropBox or GoogleDrive?

To compress your project, select the top level folder in Finder (the one that contains Quotes.xcodeproj and the folder Quotes) and then right click and select Compress “YourFolderName”.

Copy it to Dropbox or GoogleDrive and share a link either here or private message me and share the link there.