I come to you because I have the following problem:
I work with the 'GoogleMaps' cocoapods and I need to place several markers in a map by using the latitude, longitude and a codeID that I get from an API. I will present you guys 2 cases: the one that works (that uses 3 hard coded arrays mentioned before) and the one that I try to get from the API and that crashes no matter what I do. OK, the first case (the one that works) is this one:
import UIKit
import GoogleMaps
class ViewController: UIViewController {
// MARK: - Constants and variables
let lat: Double = 38.739429 // User's Latitude
let lon: Double = -9.137115 // User's Longitude
let zoom: Float = 15.0
// MARK: - Elements in the storyboard
@IBOutlet weak var googleMap: GMSMapView!
// MARK: - ViewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
googleMapsStuff()
}
// MARK: - Google maps method
func googleMapsStuff() {
googleMap.delegate = self
self.googleMap.isMyLocationEnabled = true // User's current position (blue dot on the map)
let arrayLat: [Double] = [38.739, 38.74, 38.741, 38.732, 38.7325, 38.733]
let arrayLon: [Double] = [-9.136, -9.135, -9.134, -9.137, -9.1375, -9.138]
//var arrayCompanyZoneID: [Int] = []
let camera: GMSCameraPosition = GMSCameraPosition.camera(withLatitude: lat, longitude: lon, zoom: self.zoom)
googleMap.camera = camera
for index in 0..<arrayLon.count {
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: arrayLat[index], longitude: arrayLon[index])
marker.title = "Marker number: \(index)"
marker.snippet = "Marker's Lat: \(arrayLat[index]), Marker's Lon: \(arrayLon[index])"
marker.map = self.googleMap
print("Index: \(index)")
}
}
}
And as you can see in the image, it all goes smoothly well:
The problem comes in the second case, when I try to fill the empty arrays (which it seems to do) when I connect to an API to get that data. This is the "failure" case:
struct MyInfo: Codable {
let id: String
let name: String
let x: Double // Longitude
let y: Double // Latitude
let licencePlate: String?
let range: Int?
let batteryLevel: Int?
let seats: Int?
let model: String?
let resourceImageId: String?
let pricePerMinuteParking: Int?
let pricePerMinuteDriving: Int?
let realTimeData: Bool?
let engineType: String?
let resourceType: String?
let companyZoneId: Int
let helmets: Int?
let station: Bool?
let availableResources: Int?
let spacesAvailable: Int?
let allowDropoff: Bool?
let bikesAvailable: Int?
}
class ViewController: UIViewController {
// MARK: - Constants and variables
let lat: Double = 38.739429 // User's Latitude
let lon: Double = -9.137115 // User's Longitude
let zoom: Float = 15.0
var arrayLat: [Double] = [] // [38.7395, 38.739, 38.74, 38.741, 38.732, 38.7325, 38.733]
var arrayLon: [Double] = [] // [-9.1365, -9.136, -9.135, -9.134, -9.137, -9.1375, -9.138]
var arrayCompanyZoneID: [Int] = [] // [1, 2, 3, 4, 5, 6, 7]
// MARK: - Elements in the storyboard
@IBOutlet weak var googleMap: GMSMapView!
// MARK: - ViewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
googleMap.delegate = self
self.googleMap.isMyLocationEnabled = true // User's current position (blue dot on the map)
let camera: GMSCameraPosition = GMSCameraPosition.camera(withLatitude: self.lat, longitude: self.lon, zoom: self.zoom)
googleMap.camera = camera
guard let urlAPI = URL(string: "https://apidev.meep.me/tripplan/api/v1/routers/lisboa/resources?lowerLeftLatLon=38.711046,-9.160096&upperRightLatLon=38.739429,-9.137115") else { return }
let task = URLSession.shared.dataTask(with: urlAPI) {(data, response, error) in
if error == nil {
guard let urlContent = data else { return }
do {
let JSONResult = try JSONDecoder().decode([MyInfo].self, from: urlContent) //JSONSerialization.jsonObject(with: urlContent, options: .mutableContainers)
print("JSON Result:", JSONResult)
for jsonData in JSONResult {
self.arrayLon.append(jsonData.x)
self.arrayLat.append(jsonData.y)
self.arrayCompanyZoneID.append(jsonData.companyZoneId)
}
print("-----------------")
print(type(of: JSONResult))
print("-----------------")
print("ArrayLon:", self.arrayLon)
print("ArrayLat:", self.arrayLat)
print("companyZoneId: ", self.arrayCompanyZoneID)
print("Count zoneid: ", self.arrayCompanyZoneID.count)
print("-----------------")
// MARK: - Place the multiple markers on the map
for index in 0..<self.arrayCompanyZoneID.count {
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: self.arrayLat[index], longitude: self.arrayLon[index])
marker.title = "Marker number: \(index)"
marker.snippet = "Marker's Lat: \(self.arrayLat[index]), Marker's Lon: \(self.arrayLon[index])"
marker.map = self.googleMap
print("Index: \(index)")
}
} catch {
print("JSON processing failed.")
}
} else {
print("Error serializing JSON:", error!)
}
}
task.resume()
}
And it doesn't matter what I do, the console always says:
"Terminating app due to uncaught exception 'GMSThreadException', reason: 'The API method must be called from the main thread' "
I also tried using the method
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
but it also says that the API method most be called from the main thread.
I'm so stuck here and I invested several hours to this issue but It only fails over and over.
I appreciate your advice and wisdom.
You need
DispatchQueue.main.async {
// MARK: - Place the multiple markers on the map
for index in 0..<self.arrayCompanyZoneID.count {
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: self.arrayLat[index], longitude: self.arrayLon[index])
marker.title = "Marker number: \(index)"
marker.snippet = "Marker's Lat: \(self.arrayLat[index]), Marker's Lon: \(self.arrayLon[index])"
marker.map = self.googleMap
print("Index: \(index)")
}
}
As URLSession.shared.dataTask
callback is in a background thread