This is my API response structure in postman
{
"errorCode": 0,
"status": "ok",
"message": "App Layout",
"menus": [
{
"menuID": 1,
"icon": "https://api/AppIcons/svg/Students.svg
"title": "Students", //------------------------------------------Top menu
"childMenus": [//---------------------------- Menu's > childMenu
{
"chMenuID": 1,
"menuID": 0,
"title": "Student Profile",
"icon": "https://api/AppIcons/svg/Students.svg
"childMenus": null
},
{
"chMenuID": 41,
"menuID": 0,
"title": "Update Record",
"icon": "https://api/AppIcons/svg/Students.svg
"childMenus": [//---------------- Menu's > childMenu > subChildMenu
{
"sbChMenuID": 1,
"chMenuID": 0,
"title": "Class Promotion",
"icon": "https://api/AppIcons/svg/Students.svg
},
]
]
}
{
"menuID": 2,
"title": "Staff",
"icon": "https://api/AppIcons/svg/Students.svg
"childMenus": [
{
"chMenuID": 4,
"menuID": 0,
"title": "Staff Profile",
"icon": "https://api/AppIcons/svg/Students.svg
"childMenus": null
},
{
"chMenuID": 6,
"menuID": 0,
"title": "Staff Leave",
"icon": "https://api/AppIcons/svg/Students.svg
"childMenus": null
},
{
"chMenuID": 62,
"menuID": 0,
"title": "Staff Attendance",
"icon": "https://api/AppIcons/svg/Students.svg
"childMenus": null
},
Code: here i need if title
matches in any hierarchy i need that menu in filteredModuel but with my code only any one coming.
1)in above response if i search for keyword "staf"
then i need [staff profile, staff leave, staff attendance]
2)if i search for "profil"
then i need [staff profile, student profile]
how to get this kind of filter... please guide me
struct SearchView: View {
@State private var searchKeyword: String = ""
@State private var filteredModuel: [AppMenu] = []
@State private var selectedIndex: Int? = 4
@State private var navigateModule: Bool = false
@State private var moduleId: String = ""
var body: some View {
VStack(spacing: 0) {
// in tabview i will show filterDataModuel()
filterDataModuel()
// some other code
}
}
private func filterDataModuel() {
let lowercasedKeyword = searchKeyword.lowercased()
filteredModuel = []
if let menus = viewModelSidemenu.layout?.menus {
for menu in menus {
let filteredChildMenus = menu.childMenus?.filter { childMenu in
let childTitleMatches = childMenu.title?.lowercased().contains(lowercasedKeyword) ?? false
return childTitleMatches
} ?? []
let titleMatches = menu.title?.lowercased().contains(lowercasedKeyword) ?? false
if !filteredChildMenus.isEmpty {
filteredModuel.append(contentsOf: filteredChildMenus)
}
if titleMatches && filteredChildMenus.isEmpty {
filteredModuel.append(menu)
}
}
}
}
@ViewBuilder func moduelView(moduel: AppMenu) -> some View {
VStack {
let urlStr = moduel.icon ?? ""
URLImageView(url: urlStr, placeholder: "NoProfilePic", width: 100, height: 100, renderingMode: .template)
.foregroundStyle(.black)
.padding()
Text(moduel.title ?? " ")
.font(.calibriBold(with: 16))
.foregroundStyle(Color.hex1E1D0E)
.multilineTextAlignment(.center)
}
}
}
model for this above response
// MARK: - AppLayoutModel
struct AppLayoutModel: Codable {
let status: String?
let isAuthenticated: Bool?
let menus: [AppMenu]?
let errorCode: Int?
enum CodingKeys: String, CodingKey {
case status, isAuthenticated, menus,
case userInfo, errorCode
}
}
struct AppMenu: Codable, Identifiable {
let title, icon: String?
let menuID, chMenuID: Int?
let slNo: Int?
var childMenus: [AppMenu]?
var uniqueID: Int {
return menuID ?? -1
}
var id: Int {
return uniqueID
}
}
As I see it your filter function can be simplified to
private func filterDataModuel() {
filteredModuel = []
guard let menus = viewModelSidemenu.layout?.menus else { return }
for menu in menus {
var result = menu.childMenus?.filter { $0.menuTitle.localizedCaseInsensitiveContains(searchKeyword) } ?? []
if result.isEmpty, menu.menuTitle.localizedCaseInsensitiveContains(searchKeyword) {
result.append(menu)
}
filteredModuel.append(contentsOf: result)
}
}
That is first filter the child menus on the search word and if nothing was found then filter on the title of the current menu object.
To simplify the filtering code somewhat I added a computed property to AppMenu
var menuTitle: String {
return title ?? ""
}
Another option here is to move the function to the view model instead and slightly change it so it returns the result
func filterDataModuel(searchKeyword: String) -> [AppMenu]