Search code examples
swiftswiftuimkmapviewmapbox

How to define a custom closure that should run when an instance of a struct that uses a custom initializer is run (SwiftUI)


So in MapView, I need to have a custom initializer as defined below.

import SwiftUI
import Mapbox

struct MapView: UIViewRepresentable {
    var configure: (MGLMapView) -> () = { _ in }
    @Binding var showMoreDetails: Bool
    var VModel: ViewModel
    var token: String

    init(showMoreDetails: Binding<Bool>, VModel: ViewModel, token: String) {
        self.VModel = VModel
        self.token = token
        _showMoreDetails = showMoreDetails
    }

    //This function creates the actual view of the map
    func makeUIView(context: Context) -> MGLMapView {
        let map = MGLMapView()
        DispatchQueue.main.async {
            map.delegate = context.coordinator
            self.configure(map)
        }
        return map
    }

    //This function is called whenever there is a change in the map's state
    //Go over the functionality of this with TJ.
    func updateUIView(_ uiView: MGLMapView, context: Context) {   
    }
}

In ContentView, I'd like to initialize the view as follows:

import SwiftUI
import Mapbox

struct ContentView: View {
    @ObservedObject var VModel: ViewModel = ViewModel()
    @State private var showMoreDetails: Bool = true
    @State private var token: String = "test"
    @State private var centerToUser: () -> () = { }
    var body: some View {
        ZStack {
        MapView(showMoreDetails: $showMoreDetails, VModel: VModel, token: token) { map in
                    self.centerToUser = {
                        print("run code!")
                    }
                }
        Button("test", action: centerToUser)
        }
    }
}

I believe the problem is in the custom initializer I defined in the MapView struct--When I remove that closure next to it in ContentView it works. How can I edit the custom initializer so that it also accepts a closure when the MapView is initialized?


Solution

  • If you add explicit initializer then all properties you want to configure should be in that initializer as well

    So the solution is

    struct MapView: UIViewRepresentable {
        @Binding var showMoreDetails: Bool
        var VModel: ViewModel
        var token: String
        var configure: (MGLMapView) -> ()       // declare
    
        init(showMoreDetails: Binding<Bool>, VModel: ViewModel, token: String, 
                            configure: @escaping (MGLMapView) -> () = { _ in }) { // last
            self.VModel = VModel
            self.token = token
            self.configure = configure           // << initialize !!
            _showMoreDetails = showMoreDetails
        }
    
        // .. other code