Ad

Swift Get Data From Service In View Model

- 1 answer

Hi 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??

Ad

Answer

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.

Ad
source: stackoverflow.com
Ad