使用Gmail API发送电子邮件-目标C [英] Send email in Gmail API - Objective C

查看:120
本文介绍了使用Gmail API发送电子邮件-目标C的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在一个iOS项目中,该项目涉及通过Gmail API发送电子邮件,并且在查找有关如何实际执行操作的文档方面遇到了麻烦.

首先,我们还没有完全弄清楚身份验证.我们正在使用 AppAuth 来处理此问题,到目前为止效果很好,但我们还没有非常确定如何在我们的代码中将其链接到Gmail API.

第二,我们如何发送消息本身?我们已经格式化了内容和所有内容,只是无法弄清楚如何实际发送消息.我们要做的就是从用户自己的电子邮件帐户向指定的电子邮件地址发送一条简单消息;没有附件或类似的东西.我们已经看到了几个快速的示例,但是我们更愿意使用目标C.关于如何实现此目标的任何想法吗?

更新:

在玩了很多之后,我们找到了另一种连接Gmail的方法.与其使用Google API Objective C Client for REST中的类,我们只是尝试使用HTTP POST方法发送电子邮件.这似乎比处理我们之前遇到的所有错误要容易得多.现在唯一的问题是我们仍然不能完全发送消息.我们几乎尝试了所有事情,API只是创建一个空消息并将其放入我们的已发送"邮箱中.而已.这是我们现在拥有的:

- (void)sendEmail{
    NSURL *userinfoEndpoint = [NSURL URLWithString:@"https://www.googleapis.com/upload/gmail/v1/users/TEST_USERNAME/messages/send?uploadType=media"];
    NSString *currentAccessToken = _authState.lastTokenResponse.accessToken;

    [self logMessage:@"Trying to authenticate...."];

    // Handle refreshing tokens

    NSString *message = [NSString stringWithFormat:@"{\"raw\": \"%@\"}",[self generateMessage]];
    NSLog(@"%@", message);

    // creates request to the userinfo endpoint, with access token in the Authorization header
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
    NSString *authorizationHeaderValue = [NSString stringWithFormat:@"Bearer %@", accessToken];
    [request addValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"message/rfc822" forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)[message length]] forHTTPHeaderField:@"Content-Length"];
    [request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding];

    NSURLSessionConfiguration *configuration =
    [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
                                                          delegate:nil
                                                     delegateQueue:nil];
    // performs HTTP request
    NSURLSessionDataTask *postDataTask =
    [session dataTaskWithRequest:request
               completionHandler:^(NSData *_Nullable data,
                                   NSURLResponse *_Nullable response,
                                   NSError *_Nullable error) {
                  // Handle response
               }];

    [postDataTask resume];
}];

}
- (NSString *)generateMessage{
    NSString *message = [NSString stringWithFormat:@"From: <TEST_USER@domain.com>\nTo: <TEST_USER@domain.com>\nSubject: Test\n\nThis is a test"];
    NSString *rawMessage = [message stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"];

    NSData *encodedMessage = [rawMessage dataUsingEncoding:NSUTF8StringEncoding];
    NSString *encoded = [encodedMessage base64EncodedStringWithOptions:0];
    NSLog(@"%@", encoded);

    return encoded;
}

我们已经测试了编码部分,它正在制作正确的base64字符串,但是在那之后,显然某些东西没有正确格式化或其他东西.我们收到确认消息已成功创建的确认,但是API所做的只是创建一个没有收件人,主题或正文的空电子邮件.关于如何使它起作用的任何想法?

解决方案

经过无数次试验,以下代码似乎终于对我有用,我在上面的示例中完成了工作.

首先,您需要在开发人员控制台中创建google项目,获取其客户端ID和Api-Key(这可能不是必需的),并在-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(( NSDictionary *)launchOptions方法:

[GIDSignIn sharedInstance].clientID = @"your proj client id here";
[GIDSignIn sharedInstance].delegate = self;
[GIDSignIn sharedInstance].scopes=[NSArray arrayWithObjects:@"https://www.googleapis.com/auth/gmail.send",@"https://www.googleapis.com/auth/gmail.readonly",@"https://www.googleapis.com/auth/gmail.modify", nil];

现在发送电子邮件:

// refresh token
appDelegate.delAuthAccessToken=@"";
[[GIDSignIn sharedInstance] signInSilently];
NSDate *timeStart = [NSDate date];
NSTimeInterval timeSinceStart=0;
while([appDelegate.delAuthAccessToken isEqualToString:@""] && timeSinceStart<10){//wait for new token but no longer than 10s should be enough
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                             beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0f]];//1sec increment actually ~0.02s
    timeSinceStart = [[NSDate date] timeIntervalSinceDate:timeStart];
}
if (timeSinceStart>=10) {//timed out
    return;
}

//compose rfc2822 message AND DO NOT base64 ENCODE IT and DO NOT ADD {raw etc} TOO, put 'To:' 1st, add \r\n between the lines and double that before the actual text message
NSString *message = [NSString stringWithFormat:@"To: %@\r\nFrom: %@\r\nSubject: EzPic2Txt\r\n\r\n%@", appDelegate.delToEmails, appDelegate.delAuthUserEmail, appDelegate.delMessage];

NSURL *userinfoEndpoint = [NSURL URLWithString:@"https://www.googleapis.com/upload/gmail/v1/users/me/messages/send?uploadType=media"];

NSLog(@"%@", message);

//create request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding]];//message is plain UTF8 string

//add all headers into session config, maybe ok adding to request too
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = @{
 @"api-key"       : @"api-key here, may not need it though",
 @"Authorization" : [NSString stringWithFormat:@"Bearer %@", appDelegate.delAuthAccessToken],
 @"Content-type"  : @"message/rfc822",
 @"Accept"        : @"application/json",
 @"Content-Length": [NSString stringWithFormat:@"%lu", (unsigned long)[message length]]
 };
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];

 // performs HTTP request
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
                                                completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
                                                    // Handle response
                                                }];
 [postDataTask resume];

希望它对某人有帮助

在我的应用中,我曾经能够使用MailCore2,但是由于Google Mail仅具有FULL权限,因此它被Google阻止(当我切换为允许的发送,只读和修改作用域时,访问被拒绝). Google允许仅使用发送,只读和修改范围.尽管没有指导方针,如何在iOS的Gmail中使用其伟大的Restful api",所以似乎HTTP POST是最后的手段,直到他们也将其关闭.

我不能让我的应用被Google视为不安全.如果可以的话,您仍然可以使用MailCore2,没问题.

使用HTTP GET接收电子邮件:

第一个可获取20个未读邮件ID:

//get IDs of no more than 20 unread messages
//in query you can add extra filters, say messages only from specific emails
NSString *query=@"from:aaa@gmail.com|from:bbb@yahoo.com";
NSString *tmpStr=[NSString stringWithFormat:@"https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=20&q=\"is:unread\" \"%@\"",query];
NSString *tmpStrURL=[tmpStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *userinfoEndpoint = [NSURL URLWithString:tmpStrURL];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];

[request setHTTPMethod:@"GET"];

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = @{@"api-key"       : @"your api key here",
                                        @"Authorization" : [NSString stringWithFormat:@"Bearer %@", yourTokenHere],
                                        @"Accept"        : @"application/json"
                                        };
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
// performs HTTP request
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
                                                completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
                                                    // Handle response
                                                    if (!error){
                                                        NSMutableDictionary *jsondata = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
                                                        long jsonMsgsCnt = [[jsondata valueForKey:@"resultSizeEstimate"] longValue];
                                                        if(jsonMsgsCnt>0){
                                                            NSMutableArray *jsonMsgs = [jsondata objectForKey:@"messages"];
                                                            for (NSMutableDictionary *tmp in jsonMsgs){
                                                                [delMsgsReceived  addObject:[tmp objectForKey:@"id"]];
                                                            }
                                                        }
                                                      NSLog(@"retrieve Email Id postDataTask n msg:%li",delMsgsReceived.count);
                                                    }else{
                                                     NSLog(@"retrieve Email Id postDataTask error:%@",error.description);
                                                    }
                                                }];
[postDataTask resume];

现在delMsgsReceived包含messagesIds.处理它们,以获取一封真实的电子邮件:

NSString *tmpStr=[NSString stringWithFormat:@"https://www.googleapis.com/gmail/v1/users/me/messages/%@?format=full", msgId];//supply message id here 
NSString *tmpStrURL=[tmpStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *userinfoEndpoint = [NSURL URLWithString:tmpStrURL];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];

[request setHTTPMethod:@"GET"];

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = @{
                                        @"api-key"       : @"your api key",
                                        @"Authorization" : [NSString stringWithFormat:@"Bearer %@", your auth token],
                                        @"Accept"        : @"application/json"
                                        };
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];

// performs HTTP request
NSURLSessionDataTask *postDataTask =
[session dataTaskWithRequest:request
           completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
               // Handle response
               if (!error){
                   NSMutableDictionary *jsondata = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
                   NSString *body=[jsondata objectForKey:@"snippet"];//not full msg! 
//for full message get the whole payload and extract what you need from there NSMutableArray *jsonPayload = [[jsondata objectForKey:@"payload"] objectForKey:@"headers"];
               }else{
                   //deal with error
                   NSLog(@"retrieving message error:%@",error.description);
               }
           }];
[postDataTask resume];

We are working on an iOS project that involves sending emails through the Gmail API and we are having trouble finding documentation on how to actually do this.

First, we haven't completely figured out authentication. We are using AppAuth to handle that, and it's worked pretty well so far, but we are not quite sure how to link that up to the Gmail API in our code.

Second, how do we send the message itself? We have the content and everything formatted, we just can't figure out how to actually send the message. All we are looking to do is send a simple message to a specified email address from the user's own email account; no attachments or anything like that. We have seen a couple swift examples, however we would prefer to use Objective C. Any ideas on how we could do this?

Update:

After playing around with things a bit more, we found another way to connect to Gmail. Instead of using the classes from the Google API Objective C Client for REST, we are simply trying to send the email using an HTTP POST method. This appears to be way easier than dealing with all of the errors we were getting before. The only problem we have now is that we still can't quite send messages. With nearly everything we've tried, the API just creates an empty message and puts it in our Sent mailbox; that's it. Here's what we have right now:

- (void)sendEmail{
    NSURL *userinfoEndpoint = [NSURL URLWithString:@"https://www.googleapis.com/upload/gmail/v1/users/TEST_USERNAME/messages/send?uploadType=media"];
    NSString *currentAccessToken = _authState.lastTokenResponse.accessToken;

    [self logMessage:@"Trying to authenticate...."];

    // Handle refreshing tokens

    NSString *message = [NSString stringWithFormat:@"{\"raw\": \"%@\"}",[self generateMessage]];
    NSLog(@"%@", message);

    // creates request to the userinfo endpoint, with access token in the Authorization header
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
    NSString *authorizationHeaderValue = [NSString stringWithFormat:@"Bearer %@", accessToken];
    [request addValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"message/rfc822" forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)[message length]] forHTTPHeaderField:@"Content-Length"];
    [request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding];

    NSURLSessionConfiguration *configuration =
    [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
                                                          delegate:nil
                                                     delegateQueue:nil];
    // performs HTTP request
    NSURLSessionDataTask *postDataTask =
    [session dataTaskWithRequest:request
               completionHandler:^(NSData *_Nullable data,
                                   NSURLResponse *_Nullable response,
                                   NSError *_Nullable error) {
                  // Handle response
               }];

    [postDataTask resume];
}];

}
- (NSString *)generateMessage{
    NSString *message = [NSString stringWithFormat:@"From: <TEST_USER@domain.com>\nTo: <TEST_USER@domain.com>\nSubject: Test\n\nThis is a test"];
    NSString *rawMessage = [message stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"];

    NSData *encodedMessage = [rawMessage dataUsingEncoding:NSUTF8StringEncoding];
    NSString *encoded = [encodedMessage base64EncodedStringWithOptions:0];
    NSLog(@"%@", encoded);

    return encoded;
}

We have tested the encoding part and it is making a proper base64 string, however after that point, something clearly is not formatted right or something. We get a confirmation that the message was successfully created, however all the API does is create an empty email with no recipient, subject, or body. Any ideas on what we could do to get this to work?

解决方案

After numerous experimentations, here is the code that seems to finally work for me, i worked it off your example above.

1st you need to create google project in dev console, get its Client ID and Api-Key(this may not be necessary) and implement Google SignIn in AppDelegete in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method:

[GIDSignIn sharedInstance].clientID = @"your proj client id here";
[GIDSignIn sharedInstance].delegate = self;
[GIDSignIn sharedInstance].scopes=[NSArray arrayWithObjects:@"https://www.googleapis.com/auth/gmail.send",@"https://www.googleapis.com/auth/gmail.readonly",@"https://www.googleapis.com/auth/gmail.modify", nil];

Now sending emails:

// refresh token
appDelegate.delAuthAccessToken=@"";
[[GIDSignIn sharedInstance] signInSilently];
NSDate *timeStart = [NSDate date];
NSTimeInterval timeSinceStart=0;
while([appDelegate.delAuthAccessToken isEqualToString:@""] && timeSinceStart<10){//wait for new token but no longer than 10s should be enough
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                             beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0f]];//1sec increment actually ~0.02s
    timeSinceStart = [[NSDate date] timeIntervalSinceDate:timeStart];
}
if (timeSinceStart>=10) {//timed out
    return;
}

//compose rfc2822 message AND DO NOT base64 ENCODE IT and DO NOT ADD {raw etc} TOO, put 'To:' 1st, add \r\n between the lines and double that before the actual text message
NSString *message = [NSString stringWithFormat:@"To: %@\r\nFrom: %@\r\nSubject: EzPic2Txt\r\n\r\n%@", appDelegate.delToEmails, appDelegate.delAuthUserEmail, appDelegate.delMessage];

NSURL *userinfoEndpoint = [NSURL URLWithString:@"https://www.googleapis.com/upload/gmail/v1/users/me/messages/send?uploadType=media"];

NSLog(@"%@", message);

//create request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding]];//message is plain UTF8 string

//add all headers into session config, maybe ok adding to request too
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = @{
 @"api-key"       : @"api-key here, may not need it though",
 @"Authorization" : [NSString stringWithFormat:@"Bearer %@", appDelegate.delAuthAccessToken],
 @"Content-type"  : @"message/rfc822",
 @"Accept"        : @"application/json",
 @"Content-Length": [NSString stringWithFormat:@"%lu", (unsigned long)[message length]]
 };
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];

 // performs HTTP request
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
                                                completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
                                                    // Handle response
                                                }];
 [postDataTask resume];

Hope it helps somebody

In my app I used to be able to use MailCore2 but it got blocked by Google (I got access denied when I switched to permitted send, readonly and modify scopes) since MailCore2 works only with FULL permissions. Google allowed to use ONLY send, readonly and modify scopes. There is no guide lines how to use their "great restful api" with Gmail in iOS though, so it seems like HTTP POST is the last resort until they shut it down too.

I cannot have my app to be deemed by Google as insecure. If you are OK with that you can still use MailCore2, no problem.

Receiving email with HTTP GET:

1st get up to 20 unread messages ids:

//get IDs of no more than 20 unread messages
//in query you can add extra filters, say messages only from specific emails
NSString *query=@"from:aaa@gmail.com|from:bbb@yahoo.com";
NSString *tmpStr=[NSString stringWithFormat:@"https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=20&q=\"is:unread\" \"%@\"",query];
NSString *tmpStrURL=[tmpStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *userinfoEndpoint = [NSURL URLWithString:tmpStrURL];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];

[request setHTTPMethod:@"GET"];

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = @{@"api-key"       : @"your api key here",
                                        @"Authorization" : [NSString stringWithFormat:@"Bearer %@", yourTokenHere],
                                        @"Accept"        : @"application/json"
                                        };
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
// performs HTTP request
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
                                                completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
                                                    // Handle response
                                                    if (!error){
                                                        NSMutableDictionary *jsondata = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
                                                        long jsonMsgsCnt = [[jsondata valueForKey:@"resultSizeEstimate"] longValue];
                                                        if(jsonMsgsCnt>0){
                                                            NSMutableArray *jsonMsgs = [jsondata objectForKey:@"messages"];
                                                            for (NSMutableDictionary *tmp in jsonMsgs){
                                                                [delMsgsReceived  addObject:[tmp objectForKey:@"id"]];
                                                            }
                                                        }
                                                      NSLog(@"retrieve Email Id postDataTask n msg:%li",delMsgsReceived.count);
                                                    }else{
                                                     NSLog(@"retrieve Email Id postDataTask error:%@",error.description);
                                                    }
                                                }];
[postDataTask resume];

Now delMsgsReceived contains messagesIds. Process them to get actual emails one by one:

NSString *tmpStr=[NSString stringWithFormat:@"https://www.googleapis.com/gmail/v1/users/me/messages/%@?format=full", msgId];//supply message id here 
NSString *tmpStrURL=[tmpStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *userinfoEndpoint = [NSURL URLWithString:tmpStrURL];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];

[request setHTTPMethod:@"GET"];

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = @{
                                        @"api-key"       : @"your api key",
                                        @"Authorization" : [NSString stringWithFormat:@"Bearer %@", your auth token],
                                        @"Accept"        : @"application/json"
                                        };
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];

// performs HTTP request
NSURLSessionDataTask *postDataTask =
[session dataTaskWithRequest:request
           completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
               // Handle response
               if (!error){
                   NSMutableDictionary *jsondata = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
                   NSString *body=[jsondata objectForKey:@"snippet"];//not full msg! 
//for full message get the whole payload and extract what you need from there NSMutableArray *jsonPayload = [[jsondata objectForKey:@"payload"] objectForKey:@"headers"];
               }else{
                   //deal with error
                   NSLog(@"retrieving message error:%@",error.description);
               }
           }];
[postDataTask resume];

这篇关于使用Gmail API发送电子邮件-目标C的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆