Introduction#

Github this day 3 shows

  • Network request with async await
  • Json decodable
  • AsyncImage
  • OpenSearch backend
Screen Shot 2022-11-15 at 10 40 23

Json Decodable#

Define the data models

struct MyNote: Codable {
let DocumentTitle: String
let DocumentURI: String
}
struct MySource: Codable {
let _id: String
let _source: MyNote
}
extension MySource: Identifiable {
var id: String {return _id}
}

request error handler

enum ApiError : Error {
case badRequest
case badJson
}

Network Request#

the ViewModel which perform the api call

class OpenSearchViewModel : ObservableObject {
@Published var sources = [MySource]()
init(){
self.sources = []
}
func fetchNotes () async throws {
// url
guard let url = URL(string: $OPENSEARCH_URI) else {return}
let (data, response) = try await URLSession.shared.data(for: URLRequest(url: url))
// handle response error
guard (response as? HTTPURLResponse)?.statusCode == 200 else {throw ApiError.badRequest}
// handle parse error
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
throw ApiError.badJson
}
guard let hits = json["hits"] as? [String: Any] else {
throw ApiError.badJson
}
guard let ahits = hits["hits"] as? [Any] else {
throw ApiError.badJson
}
do {
let jsonData = try JSONSerialization.data(withJSONObject: ahits, options: [])
let result = try JSONDecoder().decode([MySource].self, from: jsonData)
Task {@MainActor in
self.sources = result
}
} catch {
throw ApiError.badJson
}
}
}

Update UI#

search text as input textfield and present the list of opensearch results

struct OpenSearchView: View {
@StateObject var viewModel = OpenSearchViewModel()
@State private var searchText = ""
var body: some View {
NavigationView{
List {
ForEach(viewModel.notes) {note in
NavigationLink(destination: Text(note._source.DocumentExcerpt)) {
VStack {
Text(note._source.DocumentTitle)
Text(note._source.DocumentExcerpt)
}
}
}
}
.searchable(text: $searchText, prompt: "Look for keywords")
.onSubmit(of: .search) {
viewModel.getNotes(query: searchText)
// searchText = ""
}
.navigationTitle("Searchable")
}
}
}

and the main app

@main
struct SwiftUI30DayApp: App {
var body: some Scene {
WindowGroup{
OpenSearchView()
}
}
}