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

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

2      

Add this to BreakfastAPI

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

You can work out the other ones

2      

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...

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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)

2      

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

2      

Change

@StateObject var api = RecipeAPI()

to

@StateObject var api: RecipeAPI

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

2      

It then throws this error:

Return from initializer without initializing all stored properties

2      

let menu: RecipeSelection
@StateObject var api: RecipeAPI

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

2      

Hey,

This error presists on all of them:

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

2      

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?

2      

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.

2      

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.

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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.