Sending Email from SwiftUI Problems - swiftui

I have been using UIViewControllerRepresentable with MFmailComposer to pre-populate an email message from a SwiftUI view. I am getting the following error in the console when sending the message:
2020-10-02 09:27:58.559227-0400 CC0 Notes2[1288:471808] [PPT] Error creating the CFMessagePort needed to communicate with PPT.
In spite of the error, the message sends successfully. On IPadOS I get a Swoosh to show that the mail was sent, but on iOS I get a successful mail send but no Swoosh sound to confirm the send. Is this error a bug? I am using XCode 12.0.1, iPadOS 14.0.1 and iPOS 14.0.1
If i can not get a Swoosh out of iOS, how can I tap into the send result to confirm to the user that the mail has been sent?
Using this code:
struct MailView: UIViewControllerRepresentable {
#EnvironmentObject var theBody: GlobalData
var imageData: NoteImageData
#State private var ishowingSuccess = false
#Binding var isShowing: Bool
#Binding var result: Result<MFMailComposeResult, Error>?
class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
#Binding var isShowing: Bool
#Binding var result: Result<MFMailComposeResult, Error>?
init(isShowing: Binding<Bool>,
result: Binding<Result<MFMailComposeResult, Error>?>) {
_isShowing = isShowing
_result = result
}
func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult,
error: Error?) {
defer {
isShowing = false
}
guard error == nil else {
self.result = .failure(error!)
return
}
self.result = .success(result)
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(isShowing: $isShowing,
result: $result)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<MailView>) -> MFMailComposeViewController {
let vc = MFMailComposeViewController()
vc.mailComposeDelegate = context.coordinator
vc.setSubject("")
//vc.setMessageBody(imageData.description , isHTML: true)
vc.setMessageBody( theBody.mailBody, isHTML: true)
return vc
}
func updateUIViewController(_ uiViewController: MFMailComposeViewController,
context: UIViewControllerRepresentableContext<MailView>) {
}
}
and calling the email:
.sheet(isPresented: $isShowingMailView) {
MailView(imageData: imageData, isShowing: self.$isShowingMailView, result: self.$result )
}

Related

SwiftUI UIIMagePickerController Find/Search Field Strange Behaviour

I'm using a UIImagePickerController to select an image from the user's photo library. All works fine except when the user taps on the field with the magnifying glass icon to search the library. Sometimes it dismisses ImagePicker, other times it does nothing and occasionally it works as expected... any ideas?
Code to display the sheet:
Button(action: {}) {
ZStack {
Image(systemName: "photo")
.foregroundColor(darkMode ? .white : .black)
}
}
.frame(width: 44, height: 44)
.onTapGesture {
showPhotoLibrary.toggle()
}
.sheet(isPresented: $showPhotoLibrary) {
ImagePicker(sourceType: .photoLibrary, selectedImage: self.$image)
}
The ImagePicker:
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
var sourceType: UIImagePickerController.SourceType = .photoLibrary
#Binding var selectedImage: UIImage
#Environment(\.presentationMode) private var presentationMode
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = sourceType
imagePicker.delegate = context.coordinator
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
internal func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
parent.selectedImage = image
}
parent.presentationMode.wrappedValue.dismiss()
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
I've seen many struggle with this, it's all over the Apple dev forums so I thought I would have a go at solving it. From my own testing I believe the reason for the problems is UIImagePickerController is designed to be presented using the present method of a parent View Controller, not used as the root view controller of a SwiftUI presented sheet, as Apple (in their sample code) and everyone else seem to be doing. Below is my incomplete test code, which shows a working image picker where the search works and it can also be dragged down to dismiss, so I think this might be the correct solution.
The idea is we use UIViewControllerRepresentable to present our own custom view controller that presents or dismisses the image picker view controller when the present boolean changes.
struct ImagePickerTest: View {
#State var show = false
#State var image1: UIImage?
var body: some View {
VStack {
Button("Hello, World!") {
show.toggle()
}
Text("image: \(image1?.description ?? "" )")
}
.imagePicking(isPresented: $show, selectedImage: $image1)
}
}
extension View {
// this could be refactored into a `ViewModifier`
func imagePicking(isPresented: Binding<Bool>, selectedImage: Binding<UIImage?>) -> some View {
ZStack {
ImagePicking(isPresented: isPresented, selectedImage: selectedImage)
self
}
}
}
class MyViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate{
var dismissPicker: (() -> Void)?
var selectImage: ((UIImage) -> Void)?
func showPickerIfNecessary() {
if self.presentedViewController != nil {
return
}
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
present(imagePickerController, animated: true)
}
func hidePickerIfNecessary() {
if let vc = self.presentedViewController {
vc.dismiss(animated: true)
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
print("imagePickerControllerDidCancel")
dismissPicker?()
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
print("didFinishPickingMediaWithInfo")
if let image = (info[.editedImage] ?? info[.originalImage]) as? UIImage {
selectImage?(image)
dismissPicker?()
}
}
}
struct ImagePicking: UIViewControllerRepresentable {
#Binding var isPresented: Bool
#Binding var selectedImage: UIImage?
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
uiViewController.dismissPicker = {
isPresented = false
}
uiViewController.selectImage = { image in
selectedImage = image
}
if isPresented {
uiViewController.showPickerIfNecessary()
} else {
uiViewController.hidePickerIfNecessary()
}
}
func makeUIViewController(context: Context) -> some MyViewController {
MyViewController()
}
}
Before I started, I verified the OP's code was failing when typing a search, it crashed with this error:
2022-08-31 22:39:17.808899+0100 Test[66967:5425868] [UI] -[PUPhotoPickerHostViewController viewServiceDidTerminateWithError:] Error Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted}

Swift UI: send text message

I modified this code for MFMailComposeViewController to work with MFMessageComposeViewController. For the most part, it works. However, I am having a problem closing the messaging interface. The problem I am having is that when I tap cancel on the messaging interface nothing happens.
import SwiftUI
import UIKit
import MessageUI
struct MessengerView: UIViewControllerRepresentable{
#Binding var isShowing: Bool
#Binding var result: Result<MessageComposeResult, Error>?
class Coordinator: NSObject, MFMessageComposeViewControllerDelegate {
#Binding var isShowing: Bool
#Binding var result: Result<MessageComposeResult, Error>?
init(isShowing: Binding<Bool>,
result: Binding<Result<MessageComposeResult, Error>?>) {
_isShowing = isShowing
_result = result
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
defer {
isShowing = false
}
self.result = .success(result)
controller.dismiss(animated: true, completion: nil)
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(isShowing: $isShowing, result: $result)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<MessengerView>) -> MFMessageComposeViewController {
let vc = MFMessageComposeViewController()
vc.recipients = ["4085551212"]
vc.body = "Hello from California!"
return vc
}
func updateUIViewController(_ uiViewController: MFMessageComposeViewController,
context: UIViewControllerRepresentableContext<MessengerView>) {
}
}
In Usage:
var body: some View {
NavigationView{
if viewModel.signedIn {
VStack {
if MFMessageComposeViewController.canSendText() {
Button("Show messenger view") {
self.isShowingMailView.toggle()
}
} else {
Text("Can't send messages from this device")
}
if result != nil {
Text("Result: \(String(describing: result))")
.lineLimit(nil)
}
}
.sheet(isPresented: $isShowingMailView) {
MessengerView(isShowing: self.$isShowingMailView, result: self.$result)
}
code I modified
Is there something wrong with my messageComposeViewController

SwiftUI MessageUI messageComposeViewController ever called

Looking to send SMS from a swiftui enviro .Passing recipients from a swiftui contentview, i can send sms . However, messageComposeViewController is never called hence modal is never dismissed and i dont get any callBack (same issue when trying to cancel)
import UIKit
import MessageUI
import SwiftUI
struct sms: View {
#State private var isShowingMessage = false
#State private var recipients = ["007"]
#State private var message = "this is my message "
#State private var resultValue = ""
var body: some View {
Button{
self.isShowingMessage.toggle()
} label : {
Text("Show messages")
}
.sheet(isPresented: $isShowingMessage) {
SMSMessageUIView(recipients:recipients,body: message,completion:handleCompletion(_:))
}
}
func handleCompletion (_ result:MessageComposeResult){
switch result {
case.cancelled :
resultValue = "cancelled"
break
case .sent :
resultValue = "sent"
break
case.failed :
resultValue = "failed"
break
#unknown default :
break
}
}
}
class SMSMessageViewController : UIViewController, MFMessageComposeViewControllerDelegate {
var delegate : MessagesViewDelegate?
var recipients : [String]?
var body :String?
override func viewDidLoad() {
super.viewDidLoad()
}
func displayMessageInterface(){
let composeVC = MFMessageComposeViewController()
composeVC.body = body ?? ""
composeVC.recipients = recipients
if MFMessageComposeViewController.canSendText(){
self.present(composeVC,animated:true,completion: nil)
}else{
self.delegate?.messageCompletion(result: MessageComposeResult.failed)
}
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
print("NEVR CALLED")
self.delegate?.messageCompletion(result: result)
controller.dismiss(animated: true, completion: nil)
}
}
public protocol MessagesViewDelegate {
func messageCompletion(result: MessageComposeResult)
}
struct SMSMessageUIView : UIViewControllerRepresentable {
#Environment(\.presentationMode) var presentationMode
let recipients: [String]?
let body : String
var completion : ((_ result: MessageComposeResult)-> Void)
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> SMSMessageViewController {
let controller = SMSMessageViewController()
controller.delegate = context.coordinator
controller.recipients = recipients
controller.body = body
return controller
}
func updateUIViewController(_ uiViewController: SMSMessageViewController, context: Context) {
uiViewController.recipients = recipients
uiViewController.displayMessageInterface()
}
class Coordinator :NSObject,UINavigationControllerDelegate,MessagesViewDelegate{
var parent:SMSMessageUIView
init(_ parent:SMSMessageUIView) {
self.parent = parent
}
func messageCompletion(result: MessageComposeResult) {
self.parent.presentationMode.wrappedValue.dismiss()
self.parent.completion(result)
}
}
}

How do I fix a value member not found error in iOS foundation with swift while working with notifications?

I'm trying to experiment with iOS notifications, so I tried making a swiftUI view that would send a notification, basing it off a tutorial Found Here. I have wrote this so far:
import SwiftUI
import Foundation
class LocalNotificationManager: ObservableObject {
var notifications = [Notification]()
init() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound])
{ granted, error in
if granted == true && error == nil {
print("yay")
} else {
print("boo")
}
}
func sendNotification(title: String, subtitle: String?, body: String, LaunchIn: Double) {
let content = UNMutableNotificationContent()
content.title = title
if let subtitle = subtitle {
content.subtitle = subtitle
}
content.body = body
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: LaunchIn, repeats: false)
let request = UNNotificationRequest(identifier: "demoNotification", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
}
}
struct Screen: View {
#ObservedObject var NotificationManager = LocalNotificationManager()
var body: some View {
Button(action: {
self.notificationManager.sendNotification(title: "It worked", subtitle: ":D", body: "If you see this text, launching the local notification worked!", launchIn: 5)
}) {
Text("Test Notification")
}
}
}
struct Screen_Previews: PreviewProvider {
static var previews: some View {
Screen()
}
}
Its this line where I get problems:
self.notificationManager.sendNotification(title: "It worked", subtitle: ":D", body: "If you see this text, launching the local notification worked!", launchIn: 5)
I get an error that says:
Value of type 'Screen' has no member 'notificationManager'
Change
#ObservedObject var NotificationManager = LocalNotificationManager()
To
#StateObject var notificationManager = LocalNotificationManager()
Variables are case-sensitive and should start with a lowercase
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app
https://swift.org/documentation/api-design-guidelines/

MFMessageComposeViewController + SwiftUI Buggy Behavior

I'm using ViewControllerRepresentable to present a MFMessageComposeViewController so users can send texts from my app.
However, every time the view is presented, it's very buggy - elements randomly disappear, scrolling is off, and the screen flickers. Tested on iOS 14.2 and 14.3.
Here's the code:
import SwiftUI
import MessageUI
struct MessageView: UIViewControllerRepresentable {
var recipient: String
class Coordinator: NSObject, MFMessageComposeViewControllerDelegate {
var completion: () -> Void
init(completion: #escaping ()->Void) {
self.completion = completion
}
// delegate method
func messageComposeViewController(_ controller: MFMessageComposeViewController,
didFinishWith result: MessageComposeResult) {
controller.dismiss(animated: true, completion: nil)
completion()
}
}
func makeCoordinator() -> Coordinator {
return Coordinator() {} // not using completion handler
}
func makeUIViewController(context: Context) -> MFMessageComposeViewController {
let vc = MFMessageComposeViewController()
vc.recipients = [recipient]
vc.messageComposeDelegate = context.coordinator
return vc
}
func updateUIViewController(_ uiViewController: MFMessageComposeViewController, context: Context) {}
typealias UIViewControllerType = MFMessageComposeViewController
}
and my view
struct ContentView: View {
#State private var isShowingMessages = false
#State var result: Result<MFMailComposeResult, Error>? = nil
var body: some View {
VStack {
Button("Show Messages") {
self.isShowingMessages = true
}
.sheet(isPresented: self.$isShowingMessages) {
MessageView(recipient: "+15555555555")
}
.edgesIgnoringSafeArea(.bottom)
}
}
}
Is there something wrong with the way I'm presenting this view? Has anyone else experienced this behavior? Similar behavior happens with MFMailComposeViewController, but it's not as buggy.
5 minutes later, I realized I needed to add this when presenting the sheet:
MessageView(recipient: "+15555555555")
.ignoresSafeArea()
The view looked buggy because it was trying to account for the keyboard safe area and had a hard time doing it.