How to present the STPPaymentOptionsViewController in swift ui - swift

.sheet(isPresented: $showSheet) {
STPPaymentOptionsViewController()
}
I run this code hoping to present the Stripe Payment Options View Controller in my content view and I get this error:
Instance method sheet(isPresented:onDismiss:content:) requires that STPAddCardViewController conform to View
I also tried to wrap the view into a UIViewRepresentable like so:
struct PaymentOptionsView: UIViewRepresentable {
func makeUIView(context: Context) -> STPPaymentOptionsViewController {
let config = STPPaymentConfiguration()
config.additionalPaymentOptions = .default
config.requiredBillingAddressFields = .none
config.appleMerchantIdentifier = "dummy-merchant-id"
return STPPaymentOptionsViewController(configuration: config, e: STPTheme(), customerContext: STPCustomerContext(), delegate: self as! STPPaymentOptionsViewControllerDelegate)
}
}
Then I get the error:
Type CheckOut.PaymentOptionsView does not conform to protocol UIViewRepresentable.

Considering that STPPaymentOptionsViewController inherits from ViewController you need to use UIViewControllerRepresentable instead.
You also need to implement the required delegate methods for the STPPaymentOptionsViewControllerDelegate.
struct PaymentOptionsView: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, STPPaymentOptionsViewControllerDelegate {
var control: PaymentOptionsView
init(_ control: PaymentOptionsView) {
self.control = control
}
// Implement required delegate methods here:
func paymentOptionsViewControllerDidCancel(_ paymentOptionsViewController: STPPaymentOptionsViewController) {
}
func paymentOptionsViewControllerDidFinish(_ paymentOptionsViewController: STPPaymentOptionsViewController) {
}
func paymentOptionsViewController(_ paymentOptionsViewController: STPPaymentOptionsViewController, didFailToLoadWithError error: Error) {
}
}
func makeUIViewController(context: UIViewControllerRepresentableContext<PaymentOptionsView>) -> STPPaymentOptionsViewController {
let config = STPPaymentConfiguration()
config.additionalPaymentOptions = .default
config.requiredBillingAddressFields = .none
config.appleMerchantIdentifier = "dummy-merchant-id"
return STPPaymentOptionsViewController(configuration: config, theme: STPTheme(), apiAdapter: STPCustomerContext(), delegate: context.coordinator)
}
func updateUIViewController(_ uiViewController: STPPaymentOptionsViewController, context: UIViewControllerRepresentableContext<PaymentOptionsView>) { }
}
Keep in mind you're also setting the delegate in the STPPaymentOptionsViewController incorrectly. You need to use context.coordinator rather than self as! STPPaymentOptionsViewControllerDelegate.

Related

how to use UIViewRepresentable Coordinator delegate

I'm using Pulley a maps drawer library which is written in UIKit in a SwiftUI project. I have a SwiftUI ListView that I'm using in the project via a UIHostingController but I want to disable scrolling when the drawers position is not open and to do that I'm pretty sure I need to use one of the delegate functions Pulley provides (drawerPositionDidChange) but I'm not sure how to use the delegate in the Coordinator or if I should even try to use the delegate, maybe I just need to use some type of state variable?
Delegate in the view controller
#objc public protocol PulleyDelegate: AnyObject {
/** This is called after size changes, so if you care about the bottomSafeArea property for custom UI layout, you can use this value.
* NOTE: It's not called *during* the transition between sizes (such as in an animation coordinator), but rather after the resize is complete.
*/
#objc optional func drawerPositionDidChange(drawer: PulleyViewController, bottomSafeArea: CGFloat)
}
This is the UIViewRepresentable where I'm trying to use the delegate.
import SwiftUI
struct DrawerPosition: UIViewControllerRepresentable {
#Binding var bottomSafeArea: CGFloat?
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> some UIViewController {
let vc = PulleyViewController()
vc.delegate = context.coordinator
return vc
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
// Updates the state of the specified view controller with new information from SwiftUI.
}
class Coordinator: NSObject, PulleyDrawerViewControllerDelegate {
var parent: DrawerPosition
init (_ parent: DrawerPosition) {
self.parent = parent
}
func drawerPositionDidChange(drawer: PulleyViewController, bottomSafeArea: CGFloat){
self.parent.bottomSafeArea = bottomSafeArea
}
}
}
the ListView where I want to disable the scroll.
import SwiftUI
struct ListView: View {
#State private var bottomSafeArea: CGFloat?
var body: some View {
ScrollViewReader { proxy in
VStack {
Button("Jump to #50") {
proxy.scrollTo(50)
}
List(0..<100, id: \.self) { i in
Text("Example")
.id(i)
}.scrollDisabled(bottomSafeArea == 0 ? true : false)
}
}
}
}
class ListViewVHC: UIHostingController<ListView> {
required init?(coder: NSCoder) {
super.init (coder: coder, rootView: ListView())
}
}
struct ListView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
Here is the correct way to set up a Coordinator:
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeUIViewController(context: Context) -> PullyViewController {
context.coordinator.pullyViewController
}
func updateUIViewController(_ uiViewController: PullyViewController, context: Context) {
// Updates the state of the specified view controller with new information from SwiftUI.
context.coordinator.bottomSafeAreaChanged = { bottomSafeArea in
self.bottomSafeArea = bottomSafeArea
}
}
class Coordinator: NSObject, PulleyDrawerViewControllerDelegate {
lazy var pullyViewController: PulleyViewController = {
let vc = PulleyViewController()
vc.delegate = self
return vc
}()
var bottomSafeAreaChanged: ((CGFloat) -> Void)?
func drawerPositionDidChange(drawer: PulleyViewController, bottomSafeArea: CGFloat){
bottomSafeAreaChanged?(bottomSafeArea)
}

Directly open new contact screen from app in SwiftUI

Im trying to open New Contact screen in my SwiftUI app but Im not getting Save\Done button.I haven't seen anybody doing this I really don't know answer why as it seems easy at first. This is my current code.
struct ContactsVC: UIViewControllerRepresentable{
typealias UIViewControllerType = CNContactViewController
func makeUIViewController(context: Context) -> CNContactViewController {
let store = CNContactStore()
let con = CNContact()
let vc = CNContactViewController(forNewContact: con)
vc.contactStore = store
vc.delegate = context.coordinator
return vc
}
func updateUIViewController(_ uiViewController: CNContactViewController, context: Context) {
}
class Coordinator: NSObject,CNContactViewControllerDelegate,UINavigationControllerDelegate{
var parent: ContactsVC
init(_ parent: ContactsVC) {
self.parent = parent
}
func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
I have requested authorisation when sending contact now I want to add it.

How can i access ViewController Class from Coordinator?

I am using a UIViewControllerRepresentable Class in my SwiftUI like this:
struct CalendarDayView: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIViewController(context: Context) -> DayViewController {
let dayView = DayViewController()
dayView.delegate = context.coordinator
return dayView
}
func updateUIViewController(_ dayView: DayViewController, context: Context) {
dayView.delegate = context.coordinator
}
typealias UIViewControllerType = DayViewController
class Coordinator: NSObject, DayViewDelegate {
var parent: CalendarDayView
init(_ parent: CalendarDayView) {
self.parent = parent
}
func dayViewDidSelectEventView(_ eventView: EventView) {
guard let ckEvent = eventView.descriptor as? EKWrapper else { return }
let ekEvent = ckEvent.ekEvent
endEventEditing() // Cannot find 'endEventEditing' in scope
}
}
}
In the dayViewDidSelectEventView() function i need access to the endEventEditing() function, coming from the DayViewController Class. I already try to pass the parent to the Coordinator, but that doesn't helpful. I can't access the endEventEditing() func over the parent variable like this:
parent.endEventEditing() // Value of type 'CalendarDayView' has no member 'endEventEditing'
So is there a way to access the DayViewController Class, generated by the makeUIViewController func inside Coordinator?
Can i use/pass the dayView variable
let dayView = DayViewController()
to my Coordinator so i can use functions coming from this Controller?
You can inject it in coordinator's property (having it weak to avoid cross-references).
Here is a demo:
struct CalendarDayView: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIViewController(context: Context) -> DayViewController {
let dayView = DayViewController()
dayView.delegate = context.coordinator
context.coordinator.viewController = dayView // << inject !!
return dayView
}
func updateUIViewController(_ dayView: DayViewController, context: Context) {
dayView.delegate = context.coordinator
}
typealias UIViewControllerType = DayViewController
class Coordinator: NSObject, DayViewDelegate {
weak var viewController: DayViewController? // << declare here !!
var parent: CalendarDayView
init(_ parent: CalendarDayView) {
self.parent = parent
}
func dayViewDidSelectEventView(_ eventView: EventView) {
guard let ckEvent = eventView.descriptor as? EKWrapper else { return }
let ekEvent = ckEvent.ekEvent
viewController?.endEventEditing() // << call !!
}
}
}

Swift - How can cardParams be obtained from STPPaymentOptionsViewController

I want to complete the addition of new payment method that I implemented in this SO question. This save and reuse stripe tutorial shows that cardParams and billingDetails need to be passed to STPPaymentMethodParams. How to I get the cardParams and billingDetails from STPPaymentOptionsViewController?
This is the code that produce the STPPaymentOptionsViewController and then display the STPAddCardViewController when add card button is pressed.
import Foundation
import SwiftUI
import Stripe
struct PaymentOptionsView: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, STPPaymentOptionsViewControllerDelegate {
var control: PaymentOptionsView
init(_ control: PaymentOptionsView) {
self.control = control
}
// Implement required delegate methods here:
func paymentOptionsViewControllerDidCancel(_ paymentOptionsViewController: STPPaymentOptionsViewController) {
}
func paymentOptionsViewControllerDidFinish(_ paymentOptionsViewController: STPPaymentOptionsViewController) {
}
func paymentOptionsViewController(_ paymentOptionsViewController: STPPaymentOptionsViewController, didFailToLoadWithError error: Error) {
}
}
func makeUIViewController(context: UIViewControllerRepresentableContext<PaymentOptionsView>) -> STPPaymentOptionsViewController {
let config = STPPaymentConfiguration()
// config.requiredBillingAddressFields = .none
config.appleMerchantIdentifier = "dummy-merchant-id"
return STPPaymentOptionsViewController(configuration: config, theme: STPTheme(), apiAdapter: STPCustomerContext(keyProvider: MyAPIClient()), delegate: context.coordinator)
}
func updateUIViewController(_ uiViewController: STPPaymentOptionsViewController, context: UIViewControllerRepresentableContext<PaymentOptionsView>) { }
}
STPPaymentOptionsViewController by design only returns you a PaymentMethod and not the raw card details.
Since the STPPaymentOptionsViewControllerDelegate returns you a STPPaymentOption, you can cast it to a STPPaymentMethod and then get the billingDetails for that PaymentMethod.
In your didSelect() delegate method, you can cast it like:
if let paymentMethod = paymentOption as? STPPaymentMethod {
print(paymentMethod.stripeId)
}
https://stripe.dev/stripe-ios/docs/Classes/STPPaymentMethod.html#/c:#M#Stripe#objc(cs)STPPaymentMethod(py)billingDetails

Opening Finder using Mac Catalyst

I'm trying to open the finder using Mac Catalyst and get images.
At first, I tried the below code, but Xcode says 'NSOpenPanel' is unavailable in Mac Catalyst .
private func selectFile() {
NSOpenPanel.openImage { (result) in
if case let .success(image) = result {
self.image = image
}
}
}
So I tried this solution, this time Compile was successful, but when I click the button to open finder I got this error message: Thread 1: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
My Final Code is below
import SwiftUI
struct ContentView: View {
var body: some View {
VStack{
Button("Choose file") {
let picker = DocumentPickerViewController(
supportedTypes: ["log"],
onPick: { url in
print("url : \(url)")
},
onDismiss: {
print("dismiss")
}
)
UIApplication.shared.windows.first?.rootViewController?.present(picker, animated: true)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
class DocumentPickerViewController: UIDocumentPickerViewController {
private let onDismiss: () -> Void
private let onPick: (URL) -> ()
init(supportedTypes: [String], onPick: #escaping (URL) -> Void, onDismiss: #escaping () -> Void) {
self.onDismiss = onDismiss
self.onPick = onPick
super.init(documentTypes: supportedTypes, in: .open)
allowsMultipleSelection = false
delegate = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension DocumentPickerViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
onPick(urls.first!)
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
onDismiss()
}
}
How can I solve this issue?
In my macCatalyst app, I use a UIViewControllerRepresentable to show a UIDocumentPickerViewController
#Binding to your main view so you can reference that data from the selected file
struct DocumentImportViewController: UIViewControllerRepresentable {
#Binding var imgData:Data! //This doesn't need to be a "Data"
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<DocumentImportViewController>) -> UIDocumentPickerViewController {
let vc = UIDocumentPickerViewController(forOpeningContentTypes: [.image], asCopy: true)
vc.delegate = context.coordinator
return vc
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<DocumentImportViewController>) {
}
}
Coordinator for delegates
extension DocumentImportViewController{
class Coordinator: NSObject, UIDocumentPickerDelegate {
var parent: DocumentImportViewController
init(_ parent: DocumentImportViewController) {
self.parent = parent
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
parent.imgData = //get image from url
}
}}