How to pass Textfield value to view controller through button click in Swift UI

Here is a little sample. Like I said I don’t have Google Ads in anything right now but it should be straightforward

import SwiftUI
struct TestAdsView: View {
    @StateObject var vm: AdsScreenViewModel = AdsScreenViewModel()
    var body: some View {
        VStack{
            Text(vm.adStatus.rawValue)
            TextField("adId", text: $vm.adUnitId)
            Button("load Ads", action: {
                vm.loadAds()
            })
            //You might have to play with the position of this.
            AdsScreenView_UI(viewModel: vm).frame(width: 0, height: 0)
        }
    }
}
//This is the source of truth the user input will be held here
class AdsScreenViewModel: ObservableObject, MyAdsViewModelProtocol{
    ///reference to UIKit
    var uiViewController: MyAdsViewControllerProtocol? = nil
    
    @Published var adUnitId: String = ""
    @Published var adStatus: AdStatus = .unknown
    //MARK: MyAdsViewControllerProtocol
    func loadAds() {
        print(#function)
        uiViewController?.loadAds()
    }
    
    func setAdStatus(adStatus: AdStatus) {
        print(#function)
        self.adStatus = adStatus
    }
    func getAdId() -> String {
        print(#function)
        return adUnitId
    }
    
}
struct AdsScreenView_UI: UIViewControllerRepresentable{
    @ObservedObject var viewModel: AdsScreenViewModel
    func makeUIViewController(context: Context) -> some AdsScreenViewController {
        print(#function)
        return AdsScreenViewController(viewModel: viewModel)
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        print(#function)
    }
}
//This can mirror the google sample
class AdsScreenViewController: UIViewController, MyAdsViewControllerProtocol {
    ///SwiftUI Delegate
    var viewModel: MyAdsViewModelProtocol
    
    init(viewModel: MyAdsViewModelProtocol) {
        print(#function)
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: .main)
        //Link between UIKit and SwiftUI
        self.viewModel.uiViewController = self
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        print(#function)
        viewModel.setAdStatus(adStatus: .initialized)
        //Put the current code you have here
    }
    //MARK: MyAdsViewModelProtocol
    func loadAds() {
        print(#function)
        print("ad id \(viewModel.getAdId())")
        viewModel.setAdStatus(adStatus: .loading)
        //You would load here not in viewDidLoad
    }
}
//Protocols aren't needed but it makes the code reusable and you can see the connection protocol = interface
protocol MyAdsViewModelProtocol{
    ///Reference to the google view controller
    var uiViewController: MyAdsViewControllerProtocol? { get set }
    
    ///Tells the viewController to load the ad
    func loadAds()
    ///Retrieves the AdId
    func getAdId() -> String
    ///Sets the Ad Status
    func setAdStatus(adStatus: AdStatus)
}
protocol MyAdsViewControllerProtocol: UIViewController{
    ///Reference to the SwiftUI ViewModel
    var viewModel: MyAdsViewModelProtocol { get set }
    ///Tells Google to load the ad
    func loadAds()
}
enum AdStatus: String{
    case initialized
    case loading
    case unknown
    case error
}

struct TestAdsView_Previews: PreviewProvider {
    static var previews: some View {
        TestAdsView()
    }
}

enter image description here

Leave a Comment