CSVファイルを作成し、電子メールで送信しようとしました。メールを送信するためのウィンドウを表示しますが、メールの本文と添付ファイルはありません。この画面でアプリケーションがハングします。
「キャンセル」ボタンが機能しません。コンソールに数秒後に表示されます:
viewServiceDidTerminateWithError: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "The operation couldn’t be completed. (_UIViewServiceInterfaceErrorDomain error 3.)" UserInfo=0x7f8409f29b50 {Message=Service Connection Interrupted}
<MFMailComposeRemoteViewController: 0x7f8409c89470> timed out waiting for fence barrier from com.Apple.MailCompositionService
私のコードがあります:
func actionSheet(actionSheet: UIActionSheet!, clickedButtonAtIndex buttonIndex: Int) {
if buttonIndex == 0 {
println("Export!")
var csvString = NSMutableString()
csvString.appendString("Date;Time;Systolic;Diastolic;Pulse")
for tempValue in results { //result define outside this function
var tempDateTime = NSDate()
tempDateTime = tempValue.datePress
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
var tempDate = dateFormatter.stringFromDate(tempDateTime)
dateFormatter.dateFormat = "HH:mm:ss"
var tempTime = dateFormatter.stringFromDate(tempDateTime)
csvString.appendString("\n\(tempDate);\(tempTime);\(tempValue.sisPress);\(tempValue.diaPress);\(tempValue.hbPress)")
}
let fileManager = (NSFileManager.defaultManager())
let directorys : [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory,NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if ((directorys) != nil) {
let directories:[String] = directorys!;
let dictionary = directories[0];
let plistfile = "bpmonitor.csv"
let plistpath = dictionary.stringByAppendingPathComponent(plistfile);
println("\(plistpath)")
csvString.writeToFile(plistpath, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
var testData: NSData = NSData(contentsOfFile: plistpath)
var myMail: MFMailComposeViewController = MFMailComposeViewController()
if(MFMailComposeViewController.canSendMail()){
myMail = MFMailComposeViewController()
myMail.mailComposeDelegate = self
// set the subject
myMail.setSubject("My report")
//Add some text to the message body
var sentfrom = "Mail sent from BPMonitor"
myMail.setMessageBody(sentfrom, isHTML: true)
myMail.addAttachmentData(testData, mimeType: "text/csv", fileName: "bpmonitor.csv")
//Display the view controller
self.presentViewController(myMail, animated: true, completion: nil)
}
else {
var alert = UIAlertController(title: "Alert", message: "Your device cannot send emails", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
else {
println("File system error!")
}
}
}
代わりにUIActivityViewController
を使用してメールを送信しようとしています:
let fileURL: NSURL = NSURL(fileURLWithPath: plistpath)
let actViewController = UIActivityViewController(activityItems: [fileURL], applicationActivities: nil)
self.presentViewController(actViewController, animated: true, completion: nil)
電子メールを送信するには、ほぼ同じ画面を参照してください。しばらくすると、前の画面に戻ります。コンソールで、今別のエラー:
viewServiceDidTerminateWithError: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "The operation couldn’t be completed. (_UIViewServiceInterfaceErrorDomain error 3.)" UserInfo=0x7faab3296ad0 {Message=Service Connection Interrupted}
Errors encountered while discovering extensions: Error Domain=PlugInKit Code=13 "query cancelled" UserInfo=0x7faab3005890 {NSLocalizedDescription=query cancelled}
<MFMailComposeRemoteViewController: 0x7faab3147dc0> timed out waiting for fence barrier from com.Apple.MailCompositionService
PlugInKit
について何かがありました。
代わりにUIActivityViewController
を使用してUIDocumentInteractionController
を試してください:
let docController = UIDocumentInteractionController(URL: fileURL)
docController.delegate = self
docController.presentPreviewAnimated(true)
...
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController!) -> UIViewController! {
return self
}
この画面には、CSVファイルの内容が表示されます。
右上のボタンエクスポートを押すと、次の画面が表示されます。
mAILを選択すると、数秒間表示されます。
その後、ファイルの内容の表示に戻ります!コンソールでUIActivityViewController
を使用するときと同じメッセージ。
2016年でさえ、シミュレーターはアプリからのメール送信をサポートしていません。
実際、シミュレーターにはメールクライアントがありません。
アンリは完全な答えを与えました。絶対です
-早い段階でMFMailComposeViewControllerを割り当てて開始する
-1つの静的変数に保持する、その後、
-必要に応じて、静的なMFMailComposeViewControllerインスタンスを取得して使用します。
そして、ほぼ確実に、使用するたびにグローバルMFMailComposeViewControllerを循環させる必要があります。同じものを再利用することはnot信頼できません。
シングルトンMFMailComposeViewController
を解放してから再初期化するグローバルルーチンを用意します。メール作成者の作業が終了したら、毎回そのグローバルルーチンを呼び出します。
任意のシングルトンで実行します。もちろん、アプリのデリゲートがシングルトンであることを忘れないでください。
@property (nonatomic, strong) MFMailComposeViewController *globalMailComposer;
-(BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
........
// part 3, our own setup
[self cycleTheGlobalMailComposer];
// needed due to the worst programming in the history of Apple
.........
}
そして...
-(void)cycleTheGlobalMailComposer
{
// cycling GlobalMailComposer due to idiotic iOS issue
self.globalMailComposer = nil;
self.globalMailComposer = [[MFMailComposeViewController alloc] init];
}
次に、メールを使用するには、このようなもの...
-(void)helpEmail
{
// APP.globalMailComposer IS READY TO USE from app launch.
// recycle it AFTER OUR USE.
if ( [MFMailComposeViewController canSendMail] )
{
[APP.globalMailComposer setToRecipients:
[NSArray arrayWithObjects: emailAddressNSString, nil] ];
[APP.globalMailComposer setSubject:subject];
[APP.globalMailComposer setMessageBody:msg isHTML:NO];
APP.globalMailComposer.mailComposeDelegate = self;
[self presentViewController:APP.globalMailComposer
animated:YES completion:nil];
}
else
{
[UIAlertView ok:@"Unable to mail. No email on this device?"];
[APP cycleTheGlobalMailComposer];
}
}
-(void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError *)error
{
[controller dismissViewControllerAnimated:YES completion:^
{ [APP cycleTheGlobalMailComposer]; }
];
}
{nb、以下のMichael Salamoneによる誤字を修正。}
便宜上、プレフィックスファイルに次のマクロを含めます
#define APP ((AppDelegate *)[[UIApplication sharedApplication] delegate])
また、次のような「マイナーな」問題が発生し、数日かかる場合があります。 https://stackoverflow.com/a/17120065/294884
2016 FTRの場合、基本的なSwift APPでメールを送信するコード、
class YourClass:UIViewController, MFMailComposeViewControllerDelegate
{
func clickedMetrieArrow()
{
print("click arrow! v1")
let e = MFMailComposeViewController()
e.mailComposeDelegate = self
e.setToRecipients( ["[email protected]"] )
e.setSubject("Blah subject")
e.setMessageBody("Blah text", isHTML: false)
presentViewController(e, animated: true, completion: nil)
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?)
{
dismissViewControllerAnimated(true, completion: nil)
}
Plistに追加...
<key>LSApplicationQueriesSchemes</key>
<array>
<string>instagram</string>
</array>
そして、次のようなコード
func pointlessMarketingEmailForClient()
{
let subject = "Some subject"
let body = "Plenty of <i>email</i> body."
let coded = "mailto:[email protected]?subject=\(subject)&body=\(body)".stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())
if let emailURL:NSURL = NSURL(string: coded!)
{
if UIApplication.sharedApplication().canOpenURL(emailURL)
{
UIApplication.sharedApplication().openURL(emailURL)
}
else
{
print("fail A")
}
}
else
{
print("fail B")
}
}
最近では、アプリの「内部」からメールを送信するよりもはるかに優れています。
IOSシミュレーターにはメールクライアントがありません(アプリ内でcomposerを使用してメールを送信することもできません)。デバイスでテストする必要があります。
Swiftとは関係ありません。それはメールの問題ですcomposerそれは永遠に存在しているようです。そのことは、タイムアウトで失敗することからキャンセルされてもデリゲートメッセージを送信することまで非常に厄介です。
誰もが使用する回避策は、グローバルメールcomposer(シングルトンなど)を作成し、必要に応じて毎回再初期化することです。これにより、メールcomposerは、OSが必要とするときに常に存在しますが、再利用したいときにもがくもありません。
したがって、メールcomposer=を保持する強力な(可能な限りグローバルな)変数を作成し、使用するたびにリセットします。
メールcomposerのプロパティを作成し、ビュー内でインスタンス化して、メール作成者が必要になったときに呼び出すよりもロードしました。
@property (strong, nonatomic) MFMailComposeViewController *mailController;
self.mailController = [[MFMailComposeViewController alloc] init];
[self presentViewController:self.mailController animated:YES completion:^{}];
上記のソリューションで提案されているリサイクルが必要かどうかはわかりません。ただし、適切なパラメーターを使用する必要があります。
デリゲートはMFMailComposeViewController* parameter
を受け取ります。また、コントローラーを閉じるときにself
の代わりにそれを使用する必要があります。つまり.
デリゲートは(MFMailComposeViewController *) controller
を受け取ります。 MFMailComposeViewController controller
を閉じるときは、self
の代わりにそれを使用する必要があります。それは結局あなたが却下したいことです。
-(void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError *)error
{
[controller dismissViewControllerAnimated:YES completion:^
{ [APP cycleTheGlobalMailComposer]; }
];
}
Swiftでメールを処理するための単純なヘルパークラス。 Joe Blowの回答に基づきます。
import UIKit
import MessageUI
public class EmailManager : NSObject, MFMailComposeViewControllerDelegate
{
var mailComposeViewController: MFMailComposeViewController?
public override init()
{
mailComposeViewController = MFMailComposeViewController()
}
private func cycleMailComposer()
{
mailComposeViewController = nil
mailComposeViewController = MFMailComposeViewController()
}
public func sendMailTo(emailList:[String], subject:String, body:String, fromViewController:UIViewController)
{
if MFMailComposeViewController.canSendMail() {
mailComposeViewController!.setSubject(subject)
mailComposeViewController!.setMessageBody(body, isHTML: false)
mailComposeViewController!.setToRecipients(emailList)
mailComposeViewController?.mailComposeDelegate = self
fromViewController.presentViewController(mailComposeViewController!, animated: true, completion: nil)
}
else {
print("Could not open email app")
}
}
public func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?)
{
controller.dismissViewControllerAnimated(true) { () -> Void in
self.cycleMailComposer()
}
}
}
インスタンス変数としてAppDelegate-classに配置し、必要なときに呼び出します。
ちょっとこれは2日前にリリースされたiOS 8.3で解決されます。