Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Rocky48

macrumors newbie
Original poster
Apr 22, 2021
1
0
I am trying to learn Xcode using iOS 14 Programming for Beginners (Packt Pub).

I am now working on the part that adds the pins to the MapView, but when I run the app the map button is unresponsive (Runtime error).

I get the following message in the Debug area:
__2021-07-25 16:08:23.919240+0100 EatOut[1420:21167] Metal API Validation Enabled

Could not cast value of type 'Swift.Array<EatOut.RestaurantItem>' (0x1c31e3588) to '__C.MKAnnotation' (0x1c31e3888).

2021-07-25 16:08:24.003987+0100 EatOut[1420:21167] Could not cast value of type 'Swift.Array<EatOut.RestaurantItem>' (0x1c31e3588) to '__C.MKAnnotation' (0x1c31e3888). Could not cast value of type 'Swift.Array<EatOut.RestaurantItem>' (0x1c31e3588) to '
I'm sure that this message means a lot to you experts, but as a beginner I haven't a clue! The runtime error is in this func, in the final line:
Code:
func addMap(_ annontations: [RestaurantItem]) {

            mapView.setRegion(manager.currentRegion(latDelta: 0.5, longDelta: 0.5), animated: true)

            mapView.addAnnotation(manager.annotations as! MKAnnotation)
I get a build error on the final line if I do not apply the fix, which adds the as! MKAnnotation. Is the error in the .plist or does the code need altering? Please can you help?

Here is the full code for the swift file:
Code:
/  MapViewController.swift
//  EatOut
//
//  Created by Tony Hudson on 15/07/2021.
//

import UIKit
import MapKit

class MapViewController: UIViewController, MKMapViewDelegate {
    let manager = MapDataManager()

    var selectedRestaurant: RestaurantItem?

    @IBOutlet weak var mapView: MKMapView!

    override func viewDidLoad() {
        initialise()

        func initialise() {
            manager.fetch { (annotations) in addMap(annotations)
            }
        }

        func addMap(_ annontations: [RestaurantItem]) {
            mapView.setRegion(manager.currentRegion(latDelta: 0.5, longDelta: 0.5), animated: true)
            mapView.addAnnotation(manager.annotations as! MKAnnotation)

        }
    }
}
Here is the RestaurantItem.swift code:
Code:
//  RestaurantItem.swift
//  EatOut
//
//  Created by Tony Hudson on 12/07/2021.
//

import UIKit
import MapKit

class RestaurantItem: NSObject, MKAnnotation {
    var name: String?
    var cuisines: [String] = []
    var lat: Double?
    var long: Double?
    var address: String?
    var postalCode: String?
    var state: String?
    var imageURL: String?
    var restaurantID: Int?

    init (dict: [String: AnyObject]) {
        if let lat = dict["lat"] as? Double { self.lat = lat}
        if let long = dict["long"] as? Double { self.long = long}
        if let name = dict["name"] as? String { self.name = name}
        if let cuisines = dict["cuisines"] as? [String] { self.cuisines = cuisines}
        if let address = dict["address"] as? String { self.address = address}
        if let postalCode = dict["postalCode"] as? String { self.postalCode = postalCode}
        if let state = dict["state"] as? String { self.state = state}
        if let image = dict["imageURL"] as? String { self.imageURL = image}
        if let id = dict["id"] as? Int { self.restaurantID = id}
}

    var coordinate: CLLocationCoordinate2D {
        guard let lat = lat, let long = long else {
            return CLLocationCoordinate2D()
        }
    return CLLocationCoordinate2D(latitude: lat, longitude: long)
}
    var title: String? {
    return name
    }
    var subtitle: String? {
        if cuisines.isEmpty { return ""}
        else if cuisines.count == 1 { return cuisines.first}
        else { return cuisines.joined(separator: ", ")
        }
    }

}
Here is the MapDataManager.swift code:
Code:
//  MapDataManager.swift
//  EatOut
//
//  Created by Tony Hudson on 12/07/2021.
//

import Foundation
import MapKit

class MapDataManager: DataManager {
    fileprivate var items: [RestaurantItem] = []
    var annotations: [RestaurantItem] {
        return items
    }
    func fetch(completion: (_ annotations: [RestaurantItem]) -> ()){
    if items.count > 0 { items.removeAll() }
        for data in load(file: "MapLocations") {
    items.append(RestaurantItem(dict: data))
    }
    completion(items)
    }

    func currentRegion(latDelta: CLLocationDegrees, longDelta: CLLocationDegrees) -> MKCoordinateRegion {
        guard let item = items.first else {
            return MKCoordinateRegion()
        }
    let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: longDelta)
        return MKCoordinateRegion(center: item.coordinate, span: span)
    }
}
Please can anybody help?
 

coffeeplease

macrumors 6502
Sep 28, 2019
491
343
There is a forum for this: https://forums.macrumors.com/forums/ios-mac-tvos-watchos-programming.135/

You're using the singular version when you're trying to add multiple annotations.

Swift:
// expects a single MKAnnotation, given [MKAnnotation]
// won't build
mapView.addAnnotation(manager.annotations)

// force casting [MKAnnotation] to a single MKAnnotation
// crashes during runtime, since this is not what it expects
mapView.addAnnotation(manager.annotations as! MKAnnotation)

// expects [MKAnnotation], works as expected
mapView.addAnnotations(manager.annotations)

Generally, you won't want to force cast as you're telling the compiler "trust me".
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.