且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用Gmail API发送电子邮件-目标C

更新时间:2022-11-17 07:51:18

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

首先,您需要在开发人员控制台中创建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];