0

When I press on a map pin, the detail view on a different View Controller for the selected restuarant’s pin is not shown. I think this is occurring because of the Task block I'm using.

Its an iOS application, with a minimum deployment of iOS 18.2.

I’m not using a do-catch block in this Task block.

Why is this happening? How do I fix this?

I think the didSelect mapView protocol function isn't being called because after the Task block runs, the rest of the code is not executed.

And I've tried placing that didSelect mapView protocol function, as well as the function for showing the detail view that is part of a different View Controller at the end of the Task block, and I used @MainActor for both functions, and it still didn't work.

I’ve included a simplified reproducible example below, and above this, just the function with the Task block. I've also included the print statements from the console when the minimum reproducible example is run.

Also the detail view just shows a generic text label right now when a map pin is selected.

Code:

Just the function with the Task block:

import UIKit

func startTaskOfGettingInitialMapVenueData() {
        
        //Get venues data.
        Task {
            guard let mapVenues = try? await getMapVenues() else {return}
            
            self.mapVenues = mapVenues
            
            //Print Check.
            print("Print Check. At the end of the Task block, and still within the Task block. mapVenues:", mapVenues)
            
            self.showMapVenuesAsPins(mapVenues: self.mapVenues)
            
        }

MapViewContoller.swift:

import UIKit
import MapKit

class MapViewController: UIViewController, MKMapViewDelegate {

    @IBOutlet var mapView: MKMapView!
    
    var mapVenues: [InitialMapVenue] = []
    
    //var mapVenues: [MapVenue] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //centerOfRegion is Downtown San Francisco.
        let centerOfRegion = CLLocationCoordinate2D(
            latitude: 37.77531597108412, longitude: -122.42265827001113)
        
        let region = MKCoordinateRegion(center: centerOfRegion, latitudinalMeters: 500, longitudinalMeters: 500)
        
        mapView.setRegion(region, animated: true)
        
        startTaskOfGettingInitialMapVenueData()
    }
    
    func startTaskOfGettingInitialMapVenueData() {
        
        //Get venues data.
        Task {
            guard let mapVenues = try? await getMapVenues() else {return}
            
            self.mapVenues = mapVenues
            
            //Print Check.
            print("Print Check. At the end of the Task block, and still within the Task block. mapVenues:", mapVenues)
            
            self.showMapVenuesAsPins(mapVenues: self.mapVenues)
            
        }
        
        //Print Check.
        print("Print Check. After the Task block, and right outside the Task block, and right before the for loop that shows each venue in venues as an annotation.")
            
    }
    
    func showMapVenuesAsPins(mapVenues: [InitialMapVenue]) {
        
        //Print Check.
        print("Print Check. Inside showMapVenuesAsPins function, and before the for loop.")
        
        for element in mapVenues {
            mapView.addAnnotation(element)
        }
        
    }
    
    func getMapVenues() async throws -> [InitialMapVenue]  {
        
        let generatedMapVenues = [InitialMapVenue(name: "Rich Table", address: "199 Gough St, San Francisco, CA 94102", latitude: 37.774891876134795, longitude: -122.4227253023382, coordinate: CLLocationCoordinate2D(latitude: 37.774891876134795, longitude: -122.4227253023382)), InitialMapVenue(name: "The Bird", address: "406 Hayes St, San Francisco, CA 94102", latitude: 37.77696798656497, longitude: -122.42325276271241, coordinate: CLLocationCoordinate2D(latitude: 37.77696798656497, longitude: -122.42325276271241))]
        
        return generatedMapVenues
    }
    
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

        let restaurantPinAnnotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "MyMarker")
        restaurantPinAnnotationView.markerTintColor = UIColor.blue
        return restaurantPinAnnotationView
    }
    
    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
        
        guard let mapVenue = view.annotation as? InitialMapVenue else { return }

        presentDetailViewFromSelectedRestaurantPinFromMapModal(mapVenue: mapVenue)

    }
    
    private func presentDetailViewFromSelectedRestaurantPinFromMapModal(mapVenue: InitialMapVenue) {

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let detailViewForSelectedRestaurantFromMapVC = storyboard.instantiateViewController(identifier: "DetailViewForSelectedRestaurantFromMapViewController") as! DetailViewForSelectedRestaurantFromMapViewController

        let nav = UINavigationController(rootViewController: detailViewForSelectedRestaurantFromMapVC)
        nav.setNavigationBarHidden(true, animated: false)
        nav.modalPresentationStyle = .pageSheet

        if let sheet = nav.sheetPresentationController {
            sheet.detents = [.medium(), .large()]
            sheet.prefersGrabberVisible = true
            }

        detailViewForSelectedRestaurantFromMapVC.theMapVenue = mapVenue
        present(nav, animated: true, completion: nil)
    }
}


class InitialMapVenue: NSObject, MKAnnotation {
    var name: String?
    var address: String?
    var latitude: Double?
    var longitude: Double?
    var coordinate: CLLocationCoordinate2D
    
    var title: String? {
        return name
    }
    
    init(name: String?,
         address: String?,
         latitude: Double?,
         longitude: Double?,
         coordinate: CLLocationCoordinate2D) {
        self.name = name
        self.address = address
        self.latitude = latitude
        self.longitude = longitude
        self.coordinate = coordinate
    }
}

DetailViewForSelectedRestaurantFromMapViewController.swift:

import UIKit

class DetailViewForSelectedRestaurantFromMapViewController: UIViewController {
    
    var theMapVenue: InitialMapVenue!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
}

Print Statements from the console:

*Log Statement saying a certain file is missing.*

Print Check. After the Task block, and right outside the Task block, and right before the for loop that shows each venue in venues as an annotation.

*Log Statement.*

Print Check. At the end of the Task block, and still within the Task block. mapVenues: [<V2_CodeNotExecutingAfterTaskBlock_MinimumReproducibleExample.InitialMapVenue: 0x60000261a1c0>, <V2_CodeNotExecutingAfterTaskBlock_MinimumReproducibleExample.InitialMapVenue: 0x6000026064c0>]
Print Check. Inside showMapVenuesAsPins function, and before the for loop.
Print Check. Inside showMapVenuesAsPins function, and after the for loop.

*Log Statement.*

*Log Statement.*
5
  • you already asked this type of question before, here: stackoverflow.com/questions/79643071/… and I thought you would understand what a asynchronous function is. It is a function that does its work separately (in its own thread), while the program continue with the rest of the code after the Task{...}. Commented May 30 at 11:44
  • See also Apple docs Concurrency Commented May 30 at 11:51
  • Yes, sorry, my understanding of ‘Task {…}’ was incorrect (thinking it was code that happened while the code in the normal thread stopped executing, instead of it running in parallel). I’ve read the documentation you linked, however I’m still having trouble. Can you please point me in the right direction? If code is running in parallel to the asynchronous thread, then the detail view should be showing (and the ‘map view’ ‘didSelect’ protocol function should be triggered) however, I don’t think it is (will test this with a print statement soon). Commented May 30 at 17:47
  • The other commenter in my previous post about ‘Task {…}’ mentioned that I could just have an asynchronous method in the Task, and handle everything in there, however I have no need for that method to be asynchronous, since it doesn’t return anything, it only adds annotations to the map. If that method were async, it still seems like ‘mapView’ ‘did select’ protocol function wouldn’t trigger. Commented May 30 at 17:49
  • Please disregard my previous comment, I can no longer edit it, and found something else out. It seems like using an asynchronous method in the Task may be the solution here (as an async method was the first thing you mentioned in your comment, and after learning more about it). When trying out using an async method in the Task, and having everything handled in there, which was suggested by the other commenter in my previous post about ‘Task {…}’, I thought that an async method had to have a return value in order for it to by asynchronous, but I recently learned it does not. I’ll try this. Commented May 30 at 18:03

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.