Just starting out with SwiftUI and i’m building a small macOS app that would replicate an in-house app store. So far I have implemented an API call, Model, ViewModel and View, based on several online courses I have followed. This has enabled me to display a list of categories on the side bar, where that data feeds from a a json formatted API.
The issue I have is no matter what I try I cannot get rid of duplicates. As several apps are associated with the ‘Browsers’ category it lists Browsers in the list several times, where Ideally it should be listed just the once.
I have scoured forms and within a test playground I can use a removingDuplicates() extension which works with some dummy dats, but it appears anything I try to do with my actual code throws errors.
I have my code refactored but I will paste the key areas here to hopefully highlight my issue.
Any pointers would be appreciated.
// API call
enum NetworkError: Error {
case invalidServerResponse
}
class Api {
func fetchApps() async throws -> [Application] {
let url = Constants.Urls.liveBlueprintURL
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer " + Constants.apiBearer, forHTTPHeaderField: "Authorization")
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw NetworkError.invalidServerResponse
}
let application = try JSONDecoder().decode([Application].self, from: data)
return application
}
}
// Model
struct Application: Decodable {
let appName: String?
let category: String?
}
// ListViewModel
@MainActor
class CategoryListViewModel: ObservableObject {
var appState: AppState
init(appState: AppState) {
self.appState = appState
}
var applications: [CategoryViewModel] {
appState.application
}
// Function that gets the applications
func getApps() async {
do {
// Use the Api service
let apiCall = try await Api().fetchApps()
appState.application = apiCall.map(CategoryViewModel.init)
} catch {
//Catch any errors if there are any
print(error)
}
}
}
// CategoryViewModel
struct CategoryViewModel: Identifiable {
private var application: Application
init(application: Application) {
self.application = application
}
let id = UUID()
var category: String! {
application.category
}
}
// CategoryListView
struct CategoryListView: View {
let applications: [CategoryViewModel]
var body: some View {
List {
Text("Header")
Divider()
Spacer()
ForEach(applications) { application in
Text(application.category)
}
}
}
}
This is an example of the extension I used within a swift Playground using some dummy data.
extension Array where Element: Hashable {
//Returns a new array without duplicates,
func removingDuplicates() -> [Element] {
var addedDict = Set<Element>()
return filter {
addedDict.insert($0).inserted
}
}
//Modifies the array in-place to remove duplicates
mutating func removeDuplicates() {
self = self.removingDuplicates()
}
}
var category = ["Browser", "Browser", "Productivity", "Productivity", "Utilities"]
let categoryList = category.removingDuplicates()
print(categoryList)
>Solution :
change your removeduplicate as follows:
extension Array where Element == CategoryViewModel {
func removingDuplicates() -> [Element] {
var addedDict = Set<String>()
return filter {
if let category = $0.category, addedDict.insert(category).inserted {
return true
}
return false
}
}
}
and call it
var applications: [CategoryViewModel] {
appState.application.removingDuplicates()
}