首页 > 解决方案 > MFMailComposeViewController canSendMail 总是返回 false

问题描述

我正在尝试在我的 iOS 应用程序上使用 MFMailComposeViewController 发送电子邮件。

我在 iPhone 默认邮件应用程序中配置了一个 Gmail 帐户。但是每当我尝试发送电子邮件时,我都会变得MFMailComposeViewController canSendMail错误,因此我的代码无法使用给定的收件人、抄送和附件打开邮件应用程序

#import <MessageUI/MessageUI.h>
#import "RNMail.h"
#import <React/RCTConvert.h>
#import <React/RCTLog.h>

@implementation RNMail
{
    NSMutableDictionary *_callbacks;
}

- (instancetype)init
{
    if ((self = [super init])) {
        _callbacks = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();
}

+ (BOOL)requiresMainQueueSetup
{
    return YES;
}

RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(mail:(NSDictionary *)options
                  callback: (RCTResponseSenderBlock)callback)
{
    if ([MFMailComposeViewController canSendMail])
    {
        MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
        mail.mailComposeDelegate = self;
        _callbacks[RCTKeyForInstance(mail)] = callback;

        if (options[@"subject"]){
            NSString *subject = [RCTConvert NSString:options[@"subject"]];
            [mail setSubject:subject];
        }

        bool *isHTML = NO;

        if (options[@"isHTML"]){
            isHTML = [options[@"isHTML"] boolValue];
        }

        if (options[@"body"]){
            NSString *body = [RCTConvert NSString:options[@"body"]];
            [mail setMessageBody:body isHTML:isHTML];
        }

        if (options[@"recipients"]){
            NSArray *recipients = [RCTConvert NSArray:options[@"recipients"]];
            [mail setToRecipients:recipients];
        }

        if (options[@"ccRecipients"]){
            NSArray *ccRecipients = [RCTConvert NSArray:options[@"ccRecipients"]];
            [mail setCcRecipients:ccRecipients];
        }

        if (options[@"bccRecipients"]){
            NSArray *bccRecipients = [RCTConvert NSArray:options[@"bccRecipients"]];
            [mail setBccRecipients:bccRecipients];
        }

        if (options[@"attachment"] && options[@"attachment"][@"path"] && options[@"attachment"][@"type"]){
            NSString *attachmentPath = [RCTConvert NSString:options[@"attachment"][@"path"]];
            NSString *attachmentType = [RCTConvert NSString:options[@"attachment"][@"type"]];
            NSString *attachmentName = [RCTConvert NSString:options[@"attachment"][@"name"]];

            // Set default filename if not specificed
            if (!attachmentName) {
                attachmentName = [[attachmentPath lastPathComponent] stringByDeletingPathExtension];
            }

            // Get the resource path and read the file using NSData
            NSData *fileData = [NSData dataWithContentsOfFile:attachmentPath];

            // Determine the MIME type
            NSString *mimeType;

            /*
             * Add additional mime types and PR if necessary. Find the list
             * of supported formats at http://www.iana.org/assignments/media-types/media-types.xhtml
             */
            if ([attachmentType isEqualToString:@"jpg"]) {
                mimeType = @"image/jpeg";
            } else if ([attachmentType isEqualToString:@"png"]) {
                mimeType = @"image/png";
            } else if ([attachmentType isEqualToString:@"doc"]) {
                mimeType = @"application/msword";
            } else if ([attachmentType isEqualToString:@"ppt"]) {
                mimeType = @"application/vnd.ms-powerpoint";
            } else if ([attachmentType isEqualToString:@"html"]) {
                mimeType = @"text/html";
            } else if ([attachmentType isEqualToString:@"csv"]) {
                mimeType = @"text/csv";
            } else if ([attachmentType isEqualToString:@"pdf"]) {
                mimeType = @"application/pdf";
            } else if ([attachmentType isEqualToString:@"vcard"]) {
                mimeType = @"text/vcard";
            } else if ([attachmentType isEqualToString:@"json"]) {
                mimeType = @"application/json";
            } else if ([attachmentType isEqualToString:@"zip"]) {
                mimeType = @"application/zip";
            } else if ([attachmentType isEqualToString:@"text"]) {
                mimeType = @"text/*";
            } else if ([attachmentType isEqualToString:@"mp3"]) {
                mimeType = @"audio/mpeg";
            } else if ([attachmentType isEqualToString:@"wav"]) {
                mimeType = @"audio/wav";
            } else if ([attachmentType isEqualToString:@"aiff"]) {
                mimeType = @"audio/aiff";
            } else if ([attachmentType isEqualToString:@"flac"]) {
                mimeType = @"audio/flac";
            } else if ([attachmentType isEqualToString:@"ogg"]) {
                mimeType = @"audio/ogg";
            } else if ([attachmentType isEqualToString:@"xls"]) {
                mimeType = @"application/vnd.ms-excel";
            }

            // Add attachment
            [mail addAttachmentData:fileData mimeType:mimeType fileName:attachmentName];
        }

        UIViewController *root = [[[[UIApplication sharedApplication] delegate] window] rootViewController];

        while (root.presentedViewController) {
            root = root.presentedViewController;
        }
        [root presentViewController:mail animated:YES completion:nil];
    } else {
        callback(@[@"not_available"]);
    }
}

#pragma mark MFMailComposeViewControllerDelegate Methods

- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
    NSString *key = RCTKeyForInstance(controller);
    RCTResponseSenderBlock callback = _callbacks[key];
    if (callback) {
        switch (result) {
            case MFMailComposeResultSent:
                callback(@[[NSNull null] , @"sent"]);
                break;
            case MFMailComposeResultSaved:
                callback(@[[NSNull null] , @"saved"]);
                break;
            case MFMailComposeResultCancelled:
                callback(@[[NSNull null] , @"cancelled"]);
                break;
            case MFMailComposeResultFailed:
                callback(@[@"failed"]);
                break;
            default:
                callback(@[@"error"]);
                break;
        }
        [_callbacks removeObjectForKey:key];
    } else {
        RCTLogWarn(@"No callback registered for mail: %@", controller.title);
    }
    UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
    while (ctrl.presentedViewController && ctrl != controller) {
        ctrl = ctrl.presentedViewController;
    }
    [ctrl dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark Private

static NSString *RCTKeyForInstance(id instance)
{
    return [NSString stringWithFormat:@"%p", instance];
}

@end

iOS版本:12.1.2

我想知道最近是否有任何变化MFMailComposeViewController并导致了这种情况?

我已经配置了邮件帐户,并且工作正常。所以它不是已经发布的任何问题的重复

标签: iosobjective-ciphonemfmailcomposeviewcontrollerios12

解决方案


MFMailComposeViewController 直接链接到 Apple 的邮件应用程序。只有在安装和配置 Apple 的邮件应用程序时,它才会返回 true。

如果您想打开 Gmail(或您安装的任何默认应用程序),那么您需要使用 mailto:// 方案

这是我执行 mailto 部分的功能 - 包括从 html 到纯文本的荒谬简单的转换!

请注意,mailto:// 不支持附件或 html 内容。

/// Send email using mailto
/// - Parameters:
///   - to: array of email addresses
///   - subject: subject
///   - body: body
///   - isHtml: isHtml (only <br/> and <br /> are supported)
/// - Returns: true if sent
    func sendByURL(to:[String],subject:String,body:String, isHtml:Bool) -> Bool {
        
        
        var txtBody = body
        if isHtml {
            txtBody = body.replacingOccurrences(of: "<br />", with: "\n")
            txtBody = txtBody.replacingOccurrences(of: "<br/>", with: "\n")
            if txtBody.contains("/>") {
                print("Can't send html email with url interface")
                return false
            }
        }

        let toJoined = to.joined(separator: ",")
        guard var feedbackUrl = URLComponents.init(string: "mailto:\(toJoined)") else {
            return false
        }
                 

        var queryItems: [URLQueryItem] = []
        queryItems.append(URLQueryItem.init(name: "SUBJECT", value: subject))
        queryItems.append(URLQueryItem.init(name: "BODY",
                                            value: txtBody))
        feedbackUrl.queryItems = queryItems
        
        if let url = feedbackUrl.url {
//            This is probably an unnecessary check
//            You do need to add 'mailto' to your LSApplicationQuerySchemes for the check to be allowed
//            <key>LSApplicationQueriesSchemes</key>
//            <array>
//                <string>mailto</string>
//            </array>
            if UIApplication.shared.canOpenURL(url){
                UIApplication.shared.open(url)
                return true
            }
        }
        
        return false
     
    }

推荐阅读