0

I am trying to add annotations to my MapKit, however the array I append to is returning nil. I used this method before to populate CollectionView in previous ViewControllers and the array had values, now if I print the array it returns 0 values, I am sure that there should be data because It is printed in the console, however not being added to the array. Thoughts please?

My Manager Class:

class GoogleMapsAPIManager {

var bloodBanksArray = [BloodBanksModel]()


func fetchCity(latitude: CLLocationDegrees, longitude: CLLocationDegrees, raduis: Double){
    let urlString = "\(K.googleMapsAPI)&location=\(latitude),\(longitude)&radius=\(raduis)&key=\(K.apiKey)"
    print(urlString)
    performRequest(with: urlString)
}

//MARK: - Decoding JSON
func performRequest(with urlString: String){
    if let url = URL(string: urlString) {
        
        let session = URLSession(configuration: .default)
        
        let task = session.dataTask(with: url) { (data, response, error) in
            if error != nil {
                print(error!)
                return
            }
            
            if let safeData = data {
                self.parseJSON(bloodBankData: safeData)
            }
        }
        task.resume()
    }
}

func parseJSON(bloodBankData: Data) {
    let decoder = JSONDecoder()
    
    do {
        let decodedData = try decoder.decode(BloodBanksData.self, from: bloodBankData)
        for i in 0...decodedData.results.count - 1 {
            bloodBanksArray.append(BloodBanksModel(name: decodedData.results[i].name, photo: decodedData.results[i].photos?[0].photoReference ?? "ATtYBwJjqXlw3DMdL74SRtgG_GRA3LkAET6hDJXHWtkQMaOTo1B3Gx9jrDTXFLFabGStSxX8BiYdLAnknF7A9ynw33KKyUh5Oc55A9vXzo_6nd4mnk5Sx-iOqMNaw21dk0C424PWaio9GiogQaKecYxOT1q-bmj30syypZmyxkjF7r3-gFyC", open_now: decodedData.results[i].openingHours?.openNow ?? false, vincinity: decodedData.results[i].vicinity, rating: decodedData.results[i].rating, placeId: decodedData.results[i].placeId, lat: decodedData.results[i].geometry.location.lat, lng: decodedData.results[i].geometry.location.lng))
        }
        
        print("bossins \(bloodBanksArray)")
        
    } catch {
        print(error)
    }
}  
}

Model Class:

struct BloodBanksModel {
    let name: String
    let photo: String
    let open_now: Bool
    let vincinity: String
    let rating: Double
    let placeId: String
    let lat: Double
    let lng: Double
}

Data:

import Foundation

// MARK: - BloodBanksData
struct BloodBanksData: Codable {
    let results: [Result]

    enum CodingKeys: String, CodingKey {
        case results
    }
}

// MARK: - Result
struct Result: Codable {
    let geometry: Geometry
    let name: String
    let openingHours: OpeningHours?
    let photos: [Photo]?
    let rating: Double
    let vicinity: String
    let placeId: String

    enum CodingKeys: String, CodingKey {
        case geometry, name
        case openingHours = "opening_hours"
        case photos
        case rating
        case vicinity
        case placeId = "place_id"
    }
}

// MARK: - Geometry
struct Geometry: Codable {
    let location: Location
}

// MARK: - Location
struct Location: Codable {
    let lat, lng: Double
}

// MARK: - OpeningHours
struct OpeningHours: Codable {
    let openNow: Bool

    enum CodingKeys: String, CodingKey {
        case openNow = "open_now"
    }
}

// MARK: - Photo
struct Photo: Codable {
    let photoReference: String

    enum CodingKeys: String, CodingKey {
        case photoReference = "photo_reference"
    }
}

In my ViewController:

override func viewDidLoad() {
        super.viewDidLoad()
        
        //mapView.delegate = self
        
        locationManager.requestWhenInUseAuthorization()
        var currentLocation: CLLocation!
        
        if
            CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
                CLLocationManager.authorizationStatus() ==  .authorizedAlways
        {
            currentLocation = locationManager.location
            googleMapsAPIManager.fetchCity(latitude: currentLocation.coordinate.latitude, longitude: currentLocation.coordinate.longitude, raduis: 100000.0)
        }
        
        for location in googleMapsAPIManager.bloodBanksArray {
            let annotation = MKPointAnnotation()
            annotation.title = location.name
            annotation.coordinate = CLLocationCoordinate2D(latitude: location.lat, longitude: location.lng)
            annotations.append(annotation)
        }
        mapView.addAnnotations(annotations)
}
1
  • 1
    Hi @mohamad, You are calling 'fetchCity' function in your ViewController. It takes some time fill 'bloodBanksArray' array (Due to Server call). But before filling that you are using that array. You need to create callback in 'fetchCity' that inform you that you fetched the Data successfully and you can use 'bloodBanksArray' after that. Commented May 16, 2021 at 13:14

1 Answer 1

1

As I said in your previous question fetchCity works asynchronously, you have to add a completion handler

For convenience reasons I merged fetchCity, performRequest and parseJSON() into one method.

func fetchCity(latitude: CLLocationDegrees, longitude: CLLocationDegrees, raduis: Double, completion: @escaping () -> Void){
    let urlString = "\(K.googleMapsAPI)&location=\(latitude),\(longitude)&radius=\(raduis)&key=\(K.apiKey)"
    print(urlString)
    if let url = URL(string: urlString) {
        
        let session = URLSession(configuration: .default)
        
        let task = session.dataTask(with: url) { (data, _, error) in
            if let error = error { print(error); return }
            let decoder = JSONDecoder()
            do {
                let decodedData = try decoder.decode(BloodBanksData.self, from: data!)
                for i in 0..<decodedData.results.count {
                    bloodBanksArray.append(BloodBanksModel(name: decodedData.results[i].name, photo: decodedData.results[i].photos?[0].photoReference ?? "ATtYBwJjqXlw3DMdL74SRtgG_GRA3LkAET6hDJXHWtkQMaOTo1B3Gx9jrDTXFLFabGStSxX8BiYdLAnknF7A9ynw33KKyUh5Oc55A9vXzo_6nd4mnk5Sx-iOqMNaw21dk0C424PWaio9GiogQaKecYxOT1q-bmj30syypZmyxkjF7r3-gFyC", open_now: decodedData.results[i].openingHours?.openNow ?? false, vincinity: decodedData.results[i].vicinity, rating: decodedData.results[i].rating, placeId: decodedData.results[i].placeId, lat: decodedData.results[i].geometry.location.lat, lng: decodedData.results[i].geometry.location.lng))
                }
                print("bossins \(bloodBanksArray)")
                completion()
        
            } catch {
                print(error)
            }
        }
        task.resume()
    }
}

And call it

if CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
                CLLocationManager.authorizationStatus() ==  .authorizedAlways {
    currentLocation = locationManager.location
    googleMapsAPIManager.fetchCity(latitude: currentLocation.coordinate.latitude, longitude: currentLocation.coordinate.longitude, raduis: 100000.0) {
        for location in googleMapsAPIManager.bloodBanksArray {
            let annotation = MKPointAnnotation()
            annotation.title = location.name
            annotation.coordinate = CLLocationCoordinate2D(latitude: location.lat, longitude: location.lng)
            annotations.append(annotation)
        }
        mapView.addAnnotations(annotations)
    }
}

Be aware that Core Location works asynchronously, too.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much. Apologies that I did not understand what you said earlier, fixed the entire problem in the project now. Thank you.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.