Search code examples
wordpressswiftuiwordpress-rest-apiswiftui-foreach

How do I fetch the post id's for Wordpress posts using their Rest API with SwiftUI


I'm trying to include some blog posts from my WordPress site into my app. I've tested the API using the individual post Ids for each post and I got it to load the data in my view. However, I'm now trying to fetch an array but it doesn't seem to be fetching the IDs related to the posts to populate the view. What am I doing wrong here?

   let content: MarkdownData
@State var beholdarticles: BeholdArticle?
@State private var htmlContent = ""





let articles: [BeholdArticle] = []



private func loadArticle() {
    
    guard let url = URL(string: "https://behold.hagleyparksda.com/wp-json/wp/v2/posts") else {
        return
        
    }
    URLSession.shared.dataTask(with: url) {data, response, error in
        
        guard let data = data else { return }
        if let decodedData = try? JSONDecoder().decode(BeholdArticle.self, from: data){
            
            DispatchQueue.main.async {
                self.beholdarticles = decodedData
            }
        }
    }.resume()
}

This is the ForEach loop

var body: some View {
    
    ScrollView (.horizontal) {
       
           
      
        ForEach(articles) { item in
            ZStack {
                     
                        if beholdarticles?.thumbnail != nil {
                            
                            WebImage(url: URL(string: beholdarticles!.thumbnail)!)
                                .resizable()
                                .aspectRatio(contentMode: .fill)
                                .frame(width:350, height: 450)
                            
                        } else {
                            
                            Image("behold_imagecard")
                                    .resizable()
                                    .aspectRatio(contentMode: .fill)
                        }
                        
                        
                        HStack {
                            VStack (alignment: .leading) {
                                Text(beholdarticles?.title.rendered ?? "Loading...")
                                        .font(.system(size: 30, weight: .bold))
                                        .foregroundColor(Color.white)
                                    .frame(width: 270)
    //                            Text(beholdarticles?.title.rendered ?? "Loading...")
    //                                    .font(.system(size: 18, weight: .regular))
    //                                    .foregroundColor(Color.white)
    //                                .frame(width: 270)
                            }
                            Spacer()
                        }
                        
                            
                         
                        VStack {
                            Spacer()
                            HStack {
                                Image(uiImage: #imageLiteral(resourceName: "healthicon"))
                                Text("Spirituality")
                                    .font(.system(size: 23))
                                    .foregroundColor(Color.white)
                                Spacer()
                            }
                            .background(VisualEffectBlurView(blurStyle: .systemUltraThinMaterial))
                        }.onAppear{
                            loadArticle()
                        }
                    
                   
            } .frame(width:350, height: 450)
            .shadow(color: Color(#colorLiteral(red: 0.2549019754, green: 0.2745098174, blue: 0.3019607961, alpha: 1)) .opacity(0.2), radius: 20, x: /*@START_MENU_TOKEN@*/0.0/*@END_MENU_TOKEN@*/, y:14)
            .cornerRadius(30)
        }
        
      
        }

My data model

struct BeholdArticle: Decodable, Identifiable  {

var id: Int
var slug: String
var link: String
var thumbnail: String
var title: BeholdArticleTitle
var content: BeholdArticleContent

enum CodingKeys: String, CodingKey {

    case thumbnail = "jetpack_featured_media_url"
    case slug, link, title, content
    case id = "id"
}
}

struct BeholdArticleTitle: Decodable {

var rendered: String
 }

struct BeholdArticleContent: Decodable {

var rendered: String
}

I'm simply trying to populate my loop with the data, but it doesnt seem to be grabbing the ID's from api call. Need some help here

I still have the image URL wrapped in a conditional statement. At current the images wont come through. How do I adjust this to the updated setup?

  if beholdarticles?.thumbnail != nil {
                                
                                WebImage(url: URL(string: item.thumbnail)!)
                                    .resizable()
                                    .aspectRatio(contentMode: .fill)
                                    .frame(width:350, height: 450)
                                
                            } else {
                                
                                Image("behold_imagecard")
                                        .resizable()
                                        .aspectRatio(contentMode: .fill)
                            }

Here's the updated image code:

ForEach(articles) { article in
                ZStack {
                         
                            if article.thumbnail != nil {
                                
                                WebImage(url: URL(string: article.thumbnail)!)
                                    .resizable()
                                    .aspectRatio(contentMode: .fill)
                                    .frame(width:350, height: 450)
                                
                            } else {

                                Image("behold_imagecard")
                                        .resizable()
                                        .aspectRatio(contentMode: .fill)
                            }

Solution

  • If you want to decode the JSON from that URL endpoint, you're going to need to use [BeholdArticle].self instead of BeholdArticle.self, since it's an array of data.

    Also, instead of calling loadArticle on each element of the ForEach (which will never get called, since it doesn't have data in it at the beginning), call it at the top level of the view.

    Here's a pared down example:

    struct ContentView: View {
        @State private var articles : [BeholdArticle] = []
        
        var body: some View {
            ScrollView {
                VStack (alignment: .leading) {
                    ForEach(articles) { article in
                        Text(article.title.rendered)
                            .multilineTextAlignment(.leading)
                        if let thumbnailURL = URL(string: article.thumbnail) {
                           WebImage(url: thumbnailURL)
                               .resizable()
                               .aspectRatio(contentMode: .fill)
                               .frame(width:350, height: 450)
                        }
                        
                    }
                }
            }
            .onAppear {
                loadArticles()
            }
        }
        
        private func loadArticles() {
            guard let url = URL(string: "https://behold.hagleyparksda.com/wp-json/wp/v2/posts") else {
                return
            }
            URLSession.shared.dataTask(with: url) {data, response, error in
                
                guard let data = data else { return }
                if let decodedData = try? JSONDecoder().decode([BeholdArticle].self, from: data){
                    
                    DispatchQueue.main.async {
                        self.articles = decodedData
                    }
                }
            }.resume()
        }
    }