0

I am trying to build a SwiftUI tvOS app. As you can see here, I am trying to create a SwiftUI View using a UIViewControllerRepresentable, specifically for the DDDevicePickerViewController.

However, I noticed that there is no DDDevicePickerViewControllerDelegate while I was trying to implement it, which is needed according to Paul Hudson's tutorial. How can I use the DevicePickerView in SwiftUI?

I tried to use this code to create it, so when I use it, I just get a black screen with no errors in the logs:

import Foundation
import SwiftUI
import DeviceDiscoveryUI

public struct DDevicePickerView: UIViewControllerRepresentable {
    let viewController: DDDevicePickerViewController

    public init() {
        // Create the view controller for the device picker.
        let devicePicker = DDDevicePickerViewController(browseDescriptor: .applicationService(name: "TicTacToe"),
                                                              parameters: applicationServiceParameters())
        self.viewController = devicePicker!
    }

    public func makeUIViewController(context: Context) -> DDDevicePickerViewController {
        let gkVC = viewController
        return gkVC
    }

    public func updateUIViewController(_ uiViewController: DDDevicePickerViewController, context: Context) {
        return
    }
}
Saamer
  • 4,687
  • 1
  • 13
  • 55
  • Also related to this question: https://stackoverflow.com/questions/74647385/unable-to-connect-watchos-to-the-tvos-with-the-networking-framework – Saamer Dec 15 '22 at 13:27
  • 1
    Having a delegate is not necessary for a `UIViewRepresentable`. That tutorial only shows a delegate because the image picker has it. You should update your question to show what you have tried and how it wasn't working if you ran into actual problems. – Dávid Pásztor Dec 15 '22 at 13:32
  • @DávidPásztor thanks for the feedback, updated with code, and result of code – Saamer Dec 15 '22 at 13:37
  • 1
    That UIViewControllerRepresentable implementation is not correct – malhal Dec 15 '22 at 21:35
  • @malhal what's wrong with it? Can you please share a resource I can follow? – Saamer Dec 15 '22 at 22:55
  • makeUIViewController needs to init the VC or get it out of a coordinator. In SwiftUI, the structs shouldn't store objects because they are immediately lost. – malhal Dec 15 '22 at 23:23
  • @malhal I init the VC inside the init of the View but it doesn’t work even when I init the VC in makeUIViewController. How would I be able to get it out of a coordinator? I have no delegate for that view controller. – Saamer Dec 16 '22 at 14:34
  • I believe you don't need a coordinator here you can use a view controller. In `makeUIViewController` return a vanilla `UIViewController`. Add an `@Binding var isPresented: Bool` and in `updateUIViewController` if the bool is true then init a devicePicker and do viewController.show(devicePicker), if the bool is false then dismiss it. That's how we normally do it however the result being async is going to make it harder. – malhal Dec 16 '22 at 15:07
  • I've added some code in an answer to demonstrate this – malhal Dec 16 '22 at 15:14
  • @malhal I have no idea how you found DevicePicker, but it's worked! It's also the most generic, least descriptive name – Saamer Dec 21 '22 at 13:51

1 Answers1

1

SwiftUI already has a wrapper DevicePicker

But if you want to wrap it yourself, start with something like this and then you just have to figure out how to get the async endpoint result. It is quite unusual to have view controllers be async like this.

import Foundation
import SwiftUI
import DeviceDiscoveryUI

public struct DevicePicker: UIViewControllerRepresentable {
    @Binding var isPresented: Bool

    public func makeUIViewController(context: Context) -> UIViewController {
        UIViewController()
    }

    public func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        if isPresented {
             if uiViewController.presentedViewController != nil {
                 return
             }
             
             let = picker DDDevicePickerViewController(browseDescriptor: .applicationService(name: "TicTacToe"), parameters: applicationServiceParameters())
              uiViewController.present(picker, animated: !context.transaction.disablesAnimations)
        }
        else {
            uiViewController.presentedViewController?.dismiss()
        }
    }
}
malhal
  • 26,330
  • 7
  • 115
  • 133
  • ! Thanks a lot. I was getting a runtime non crash error (https://stackoverflow.com/questions/26022756/warning-attempt-to-present-on-whose-view-is-not-in-the-window-hierarchy-s) at `uiViewController.present` so I had to wrap it in DispatchQueue.main.async {...}. Either way the app gets stuck on a black screen after the present -,- So that did do anything either It might be that its waiting for the `let endpoint = try await picker.endpoint` to do some work? There's no documentation anywhere of this being done in swiftUI but I might have to give up – Saamer Dec 16 '22 at 21:35
  • Yeh working around this async thing is going to be a challenge. I found one example on Github but it's for UIKit https://github.com/TheFirstPrototype/AppleTV-NetworkDiscovery-Swift/blob/aca9e517e31f2b2710de1469ad5b37bb9ecd2e9d/BuildingACustomPeerToPeerProtocol/TicTacToe/TicTacToe-tvOS/Views/PeerListViewController.swift – malhal Dec 16 '22 at 22:57
  • yeah that’s my code lol It’s from the WWDC example code which uses UIKit – Saamer Dec 17 '22 at 04:32