WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

SOLVED: How to sort/filter out my API call to these menu settings in a list view?

Forums > SwiftUI

Hi,

This is my Menu i'd like to add to my list:

  Menu{
                       Button("Date Added", action: sortByDate)
                       Button("Creator", action: sortByCreator)
                       Button("Difficulty", action: sortByDifficulty)

            } label: {
                Label("Sort", systemImage: "line.3.horizontal.decrease.circle.fill")
            }

but the problem I am having is that my struct is like this:

struct RecipeList: View {
    let menu: RecipeSelection
    /// takes the passeed in value from the recipe selction on the home page and passes it to the list page to fetch the correct repecies.
  @StateObject var api = RecipeAPI()

    var body: some View {

        List {
            //HeaderImage
            Image("HeaderImages/\(menu.name)")
                .resizable()
                .frame(height: 250)
                .aspectRatio(contentMode: .fill)
                .listRowBackground(Color.black)
                .listRowInsets(EdgeInsets(.zero))
                .padding(.bottom, 10)

            //View Discription and Title Header
            Text(menu.name)
                .font(.largeTitle)
                .fontWeight(.bold)
                .listRowSeparator(.hidden)
                .listRowBackground(Color.black)

            Text("Explore \(menu.name) recipes from creators you love.")
                .listRowBackground(Color.black)

            Menu{
                       Button("Date Added", action: sortByDate)
                       Button("Creator", action: sortByCreator)
                       Button("Difficulty", action: sortByDifficulty)

            } label: {
                Label("Sort", systemImage: "line.3.horizontal.decrease.circle.fill")
            }

            //List of Recipes
            ForEach(api.recipes) { recipe in

                NavigationLink(destination: RecipesLanding(recipe: recipe)){
                    HStack{
                        AsyncImage(url: URL(string: "\(recipe.imageURL)")) { image in
                            image
                                .resizable()
                                .cornerRadius(8)
                                .frame(width: 130, height: 81)
                                .clipped()
                                .aspectRatio(contentMode: .fit)
                        } placeholder: {
                            Rectangle()
                                .fill(Color.gray)
                                .frame(width: 130, height: 81)
                                .cornerRadius(8)
                        }

                        VStack(alignment: .leading) {
                            Text(recipe.name)
                                .font(.headline)
                            Text(recipe.creator)
                        }
                    }
                }

            }
            .listRowBackground(Color.black)
            .padding(5)

        }
        .frame( maxWidth: .infinity)
        .edgesIgnoringSafeArea(.all)
        .listStyle(GroupedListStyle())

        .task {
            await api.fetchRecipes(for: menu)

        }
        .listRowBackground(Color.black)

    }

    func sortByDate() {}
    func sortByCreator() {}
    func sortByDifficulty() {}

}

How do I sort by Creator of my API call which is a string? Or By Date which is a Date type but It is formatted as you can see in the API below. and the diffilcuty?


@MainActor
class BreakfastAPI: ObservableObject {
    @Published var recipes: [Recipe] = []

        func fetchRecipes() async {

                let formatter = DateFormatter()
                formatter.dateFormat = "dd/MM/yyyy"

                do  {
                    let breakfastAPIURL = URL(string: URLData.breakfast.rawValue)!
                    async let items = try await URLSession.shared.decode([Recipe].self, from: breakfastAPIURL, dateDecodingStrategy: .formatted(formatter))
                    recipes = try await items
                    recipes = recipes.filter { $0.dateAdded <= Date.now }
                } catch {
                    print("Failed to fetch data!", error)
                }
            }

}

Best, Imran

   

Add this to BreakfastAPI

func sortByCreator() {
  recipes.sort { $0.creator < $1.creator }
}

You can work out the other ones

   

Hi,

So I tried that but the problem I am now having is that the function cannot be a found in scope.

function added to Recipe list view to bring in the function from the RecipeAPI:

func sortedByCreator() {
        sortByCreator()
    }

Menu:

   Menu{
                       Button("Date Added", action: sortByDate)
                       Button("Creator", action: sortedByCreator)
                       Button("Difficulty", action: sortByDifficulty)

            } label: {
                Label("Sort", systemImage: "line.3.horizontal.decrease.circle.fill")
            }

Thanks again...

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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

You do not need the method inside the RecipeList just do this

Button("Creator", action: api.sortedByCreator)

   

Hi,

So I tried that and this is the error I got:

Converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'

on all the buttons.

Best, Imran

   

Change

@StateObject var api = RecipeAPI()

to

@StateObject var api: RecipeAPI

init() {
    self._api = StateObject(wrappedValue: RecipeAPI())
}

   

It then throws this error:

Return from initializer without initializing all stored properties

   

let menu: RecipeSelection
@StateObject var api: RecipeAPI

init(menu: RecipeSelection) {
    self._api = StateObject(wrappedValue: RecipeAPI())
    self.menu = menu
}

   

Hey,

This error presists on all of them:

Converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'

   

Hi Imran,

In your code that you have given us you have

@MainActor
class BreakfastAPI: ObservableObject { // <- Is this RecipeAPI?
// rest of code

and

struct RecipeList: View {
  let menu: RecipeSelection

  @StateObject var api = RecipeAPI() // <- does not match the BreakfastAPI

So you need to gives us that. Where about is the error message? Does the RecipeAPI have @MainActor?

   

Hi,

So It's for my recipesAPI It has the @MainActor tag and I have done the above init to the view.

This is the error I am getting:

Converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'

on all the buttons on the Menu we created.

   

Sorry I am very confused about how the RecipeAPI and BreakfastAPI are connected! The methods(func) for your Button should be in the same class as @Published var recipes: [Recipe] = [] as they change it directly.

Try removing the @MainActor and see if you get an error about change the UI not on main thread, if not then you do not need it.

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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

Reply to this topic…

You need to create an account or log in to reply.

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.