Set limit to adding items in SwiftUI?

Viewed 68

Hi there I just started learning Swift and I've been wondering how to add a limit on how many items you can add to the list, for example, a maximum of 3.

I hope someone can help me with this.

I followed this tutorial series https://youtu.be/SMt4_WUdKag?list=PLwvDm4VfkdpheGqemblOIA7v3oq0MS30i

This is the ItemStore file:

class ItemStore: ObservableObject {
    
    static var shared = ItemStore()
    static var preview = ItemStore()

    @Published var items: [ItemModel] = []{
        
        didSet {
            
            saveItems()
            
        }
        
    }
 
    let itemsKey: String = "items_list"
    
    init() {
        
        getItems()
        
    }
    
    func getItems() {
        guard
            let data = UserDefaults.standard.data(forKey: itemsKey),
            let savedItems = try? JSONDecoder().decode([ItemModel].self, from: data)
        else {return}
        
        self.items  = savedItems
        
    }
    
    func deleteItem (indexSet: IndexSet){
        items.remove(atOffsets: indexSet)
    }
    
    func moveItem (from: IndexSet, to: Int) {
        items.move(fromOffsets: from, toOffset: to)
        
    }
    
    func addItem (title: String) {
        
        let newItem = ItemModel(title: title, isCompleted: false)
        items.append(newItem)
        
    }
    
    func updateItem( item: ItemModel) {
        
        if let index = items.firstIndex(where: { $0.id == item.id}) {
            items [index] = item.updateCompletion()
 
        }
 
    }

    func saveItems() {
        if let encodedData = try? JSONEncoder().encode(items) {
            UserDefaults.standard.set(encodedData, forKey: itemsKey)
 
        }     
    }      
}

This is the AddView code:

struct AddView: View {
    @Environment(\.presentationMode) var presentationMode
    @EnvironmentObject var store: ItemStore
    @State var textFieldText: String = ""
    
    
    @State var alerTitle: String = ""
    @State var showAlert: Bool = false
    
    
    var body: some View {
        ScrollView {
            VStack {
                TextField("Type Here", text: $textFieldText)
                    
                    .padding(.horizontal)
                    .frame(height: 55)
                    .background(Color(UIColor.secondarySystemBackground))
                    .cornerRadius(10)
                    .font(.body.weight(.medium))
                
                Button(action: saveButtonPressed, label: {
                    Text("Save".uppercased())
                        .foregroundColor(.white)
                        .font(.headline)
                        .fontWeight(.bold)
                        .frame(height: 66.0)
                        .frame(maxWidth: .infinity)
                        .background(Color("AccentColor"))
                        .cornerRadius(10)
                    
                })
        }
        .padding(14)
    }
        .navigationTitle("Add an Item ✍️ ")
        .alert(isPresented: $showAlert, content: getAlert)
        
    }
        
        func saveButtonPressed() {
            if textIsAppropriate() {
            
            
            listViewModel.addItem(title: textFieldText)
            presentationMode.wrappedValue.dismiss()
            
            }
        }
    
    func textIsAppropriate() -> Bool {
        if textFieldText.count < 1 {
            alerTitle = "You can't leave the task empty! "
            showAlert.toggle()
            return false
        }
        return true
    }
    func getAlert() -> Alert {
        return Alert(title: Text(alerTitle))
    }
}




struct AddView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            AddView()
        }
        .environmentObject(ItemStore.preview)   
    }
}
3 Answers

I looked at the tutorial you were following. The easiest way to set a limit would be to add .disabled(items.count > 3) to the end of the NavigationLink that you use to navigate to the add screen. Something like this:

NavigationLink(destination: AddView()) {
  Text("Add")
}
.disabled(items.count > 3)

You can simply control this case with if else statement.

func addItem (title: String) {
    if items.count < 3 { //allow to store 3 maximum
       let newItem = ItemModel(title: title, isCompleted: false)
       items.append(newItem)
    }
}

I would suggest something that aligns with the tutorial you are following, So this tutorial is about MVVM architecture so for this kind of logic it's better to keep it in the ViewModel so it will be clean and maintainable.

So first in your view model, you should create a constant for the max number of items.

let maxNumberOfItems = 3

Now you create a new property

@Published var canAddMoreItems: Bool = false

and update it when the array gets updated

@Published var items: [ItemModel] = [] {
  didSet {
    saveItems()
    updateCanAddMoreItems()
  }
}

func updateCanAddMoreItems() {
  canAddMoreItems = items.count < maxNumberOfItems
}

And now before adding a new item you can check

func addItem(title: String) {
  guard canAddMoreItems else {
    return
  }

  let newItem = ItemModel(title: title, isCompleted: false)
  items.append(newItem)
}

And to update the UI (Disable the button maybe?)

Button()
.disabled(!viewModel.canAddMoreItems)

Related