I'm a beginner at swift and I'm using a ViewModel and I'm trying to fetch data from an API I've setup locally as follows:
//
// LocationsViewModel.swift
//
//
import Foundation
import MapKit
import SwiftUI
class LocationsViewModel: ObservableObject {
// all loaded locations
@Published var locations: [Location]
// Current location on map
@Published var mapLocation: Location {
didSet {
updateMapRegion(location: mapLocation)
}
}
// current region on map
@Published var mapRegion: MKCoordinateRegion = MKCoordinateRegion()
// show list of locations
@Published var showLocationsList: Bool = false
// show location detail via sheet
@Published var sheetLocation: Location? = nil
let mapSpan = MKCoordinateSpan(
latitudeDelta: 0.1,
longitudeDelta: 0.1
)
init() {
LocationsDataService.fetch()
let locations = LocationsDataService.locations
self.locations = locations
self.mapLocation = locations.first!
self.updateMapRegion(location: locations.first!)
}
private func updateMapRegion(location: Location) {
withAnimation(.easeInOut) {
mapRegion = MKCoordinateRegion(
center: location.coordinates,
span: mapSpan
)
}
}
func toggleLocationsList() {
withAnimation(.spring()) {
showLocationsList.toggle()
}
}
func showNextLocation(location: Location) {
withAnimation(.easeInOut) {
mapLocation = location
showLocationsList = false
}
}
}
However this is throwing me an error on the following two lines;
LocationsDataService.fetch()
let locations = LocationsDataService.locations
Which are:
Instance member 'fetch' cannot be used on type 'LocationsDataService'; did you mean to use a value of this type instead?
Instance member 'locations' cannot be used on type 'LocationsDataService'; did you mean to use a value of this type instead?
My data service is as follows;
//
// LocationsDataService.swift
//
import Foundation
import MapKit
class LocationsDataService {
let token = "2|asOnUG27uCrcVuGxOO65kS25mX0nUSls5ApfemQy";
@Published var locations: [Location] = []
func fetch() {
guard let url = URL(string: "http://localhost:8080/api/locations") else {
print("Invalid url...")
return
}
var urlRequest = URLRequest( //2
url: url
)
urlRequest.setValue( //3
"Bearer \(token)",
forHTTPHeaderField: "Authentication"
)
urlRequest.setValue( //4
"application/json;charset=utf-8",
forHTTPHeaderField: "Content-Type"
)
let task = URLSession.shared.dataTask(with: urlRequest) { [weak self] data, _, error in
guard let data = data, error == nil else {
return
}
do {
let apiLocations = try JSONDecoder().decode([Location].self, from: data)
DispatchQueue.main.async {
self?.locations = apiLocations
}
} catch {
print(error)
}
}
task.resume()
}
}
This is my location model;
//
// Location.swift
//
//
import Foundation
import MapKit
import CoreLocation
struct Location: Identifiable, Equatable, Codable {
static func == (lhs: Location, rhs: Location) -> Bool {
return lhs.id == rhs.id
}
let name: String
let cityName: String
// let coordinates: CLLocationCoordinate2D
let description: String
let imageNames: [String]
let link: String
var id: String {
// create computed variable for id
name + cityName
}
let latitude: Double
let longitude: Double
var coordinates: CLLocationCoordinate2D {
return CLLocationCoordinate2D(latitude: CLLocationDegrees(latitude), longitude: CLLocationDegrees(longitude))
}
}
Any ideas how I can fetch the data from the API in my service class and use it in the LocationsViewModel
?
By calling LocationsDataService.fetch()
you are calling static function fetch which doesn't exist. You need an instance of LocationsDataService
, so basically you need to instantiate it and then call fetch()
like this
let dataService = LocationsDataService()
dataService.fetch()
It is the same story with locations
property.
Otherwise you can make fetch
and locations
static on LocationsDataService
but usually that is something you don't want.