I made a custom SearchBar with SwiftUI and put it in the view, having BookListViewModel
as @ObservedObject
, but how do it call fetchBooks()
from BookListViewModel
and show as list?
Here is my SearchView()
struct SearchView: View {
@ObservedObject var bookListVM : BookListViewModel
var body: some View {
VStack {
SearchBar(searchText: $bookListVM.keyword)
Text("\(bookListVM.keyword)")
if bookListVM.keyword.count > 0 {
List(bookListVM.books, id: \.id) { bookVM in
NavigationLink(
destination: BookDetailView(id: bookVM.id),
label: {
Text("\(bookVM.title)")
})
}
} else {
Text("Please Search Your Item")
Spacer()
}
}
}
}
and in the BookListViewModel
,
class BookListViewModel: ObservableObject {
@Published public private(set) var books: [BookViewModel] = []
private var cancellable: AnyCancellable?
@Published var keyword : String = ""
init() {
self.keyword = keyword
self.fetchBooks()
}
func fetchBooks() {
self.cancellable = Webservice().getSearchBooks(keyword: self.keyword).map { books in
books.map { BookViewModel(book: $0) }
}
.sink(receiveCompletion: { completion in
if case .failure(let err) = completion {
print("Retrieving data failed with error \(err)")
}
}, receiveValue: { bookViewModel in
self.books = bookViewModel
print(bookViewModel)
})
}
}
class BookViewModel: ObservableObject {
let book: Book
let id: String
var title: String
init(book: Book){
self.book = book
self.id = book.isbn13
self.title = book.title
}
}
and my Webservice
, function getSearchBooks()
func getSearchBooks(keyword: String) -> AnyPublisher<[Book], Error> {
URLSession.shared.dataTaskPublisher(for: EndPoint.books(keyword).url)
.receive(on: RunLoop.main)
.map{ $0.data }
.decode(type: BooksData.self, decoder: JSONDecoder())
.map{$0.books}
.catch { _ in Empty<[Book], Error>()}
.eraseToAnyPublisher()
}
What did I do wrong? Any advice will be appreciated. Thanks in advance!