⬆️ ⬇️

Work with Vkontakte.ru API in iOS application

In this short article I would like to share ways to integrate Vkontakte API into an IOS project.

There is little information on this topic on the Internet right now and maybe my advice will help someone. I will show how you can not only get user data, but also place a photo, text and link on the wall, as well as a case with captcha input.



The project with an example is on github.com - https://github.com/maiorov/VKAPI



Documentation on the methods of the VKontakte API can be read here.



Articles from other sources on this topic:

1. appleinsider.ru

2. imaladec.net

')

So, some stages in work with VK API:

1. User authorization through UIWebView and getting accessToken'a and user ID.

2. Using VK API, requests and data retrieval:

2.1 Sending text to the user's wall

2.1.1 Captcha

2.2 Sending an Image to a Wall

2.2.1 Getting the VK server url for downloading images

2.2.2 Form a POST request

2.2.3 Create a request to save photos on the server VKontakte

2.2.4 Place the photo on the wall

3. User logout



Part 0. Application registration

As it turned out, not the most trivial part of the operation, due to the fact that VKontakte are several forms of creating mobile applications! For proper registration, you need to use the link http://vkontakte.ru/editapp?act=create&site=1 , select the application type Standalone-application. Parameters in the settings can be omitted. You just need to copy the application ID. In my example, this parameter is appID.



Part 1. Authorization

To work on the Oauth 2.0 protocol with the VK API, we need accessToken and user_id. We can receive it by sending a request to the VK server via the UIWebView web browser, in response to the request the VKontakte server will issue a user authorization form, after passing authorization we will receive accessToken, user_id (id of the user who entered his data), expires_in . To do this in the new project, create a view-controller, let's call it for example vkLoginViewController. This will be the controller responsible for authorizing the user, upon successful authorization, it saves the received parameters to NSUserDefaults and calls the authComplete delegate method.



You can see the detailed code in the example . Here I will analyze only the main points.



Adding to the controller UIWebView * vkWebView, we make a request:

NSString *authLink = [NSString stringWithFormat:@"http://api.vk.com/oauth/authorize?client_id=%@&scope=wall,photos&redirect_uri=http://api.vk.com/blank.html&display=touch&response_type=token", appID]; NSURL *url = [NSURL URLWithString:authLink]; [vkWebView loadRequest:[NSURLRequest requestWithURL:url]]; 


Here client_id is the id of your application, scope = wall, photos are the access rights that the application requests from the user (wall placement on the wall, photos are needed to post images on the wall)



In the function webViewDidFinishLoad, we get a response from the VK server:

 -(void)webViewDidFinishLoad:(UIWebView *)webView { //      if ([vkWebView.request.URL.absoluteString rangeOfString:@"access_token"].location != NSNotFound) { NSString *accessToken = [self stringBetweenString:@"access_token=" andString:@"&" innerString:[[[webView request] URL] absoluteString]]; //  id ,    NSArray *userAr = [[[[webView request] URL] absoluteString] componentsSeparatedByString:@"&user_id="]; NSString *user_id = [userAr lastObject]; NSLog(@"User id: %@", user_id); if(user_id){ [[NSUserDefaults standardUserDefaults] setObject:user_id forKey:@"VKAccessUserId"]; } if(accessToken){ [[NSUserDefaults standardUserDefaults] setObject:accessToken forKey:@"VKAccessToken"]; //    .  expires_in=86400   ,     . //   ,   ,          [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"VKAccessTokenDate"]; [[NSUserDefaults standardUserDefaults] synchronize]; } NSLog(@"vkWebView response: %@",[[[webView request] URL] absoluteString]); [(ViewController *)delegate authComplete]; [self dismissModalViewControllerAnimated:YES]; } else if ([vkWebView.request.URL.absoluteString rangeOfString:@"error"].location != NSNotFound) { NSLog(@"Error: %@", vkWebView.request.URL.absoluteString); [self dismissModalViewControllerAnimated:YES]; } } 




After that, we got everything needed: accessToken, user_id. And we pass on to the delegate that the authorization has been successfully passed, we no longer need this controller and we remove it:

 [(ViewController *)delegate authComplete]; [self dismissModalViewControllerAnimated:YES]; 




Part 2. API Requests

Now the fun begins! In the example in the ViewController controller, I prepared several functions by which you can understand how to work with the API.



2.1 Sending text to the user's wall

Take, for example, the function of sending text to the wall:

 - (void) sendText { NSString *user_id = [[NSUserDefaults standardUserDefaults] objectForKey:@"VKAccessUserId"]; NSString *accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:@"VKAccessToken"]; NSString *text = @"   API !"; //        NSString *sendTextMessage = [NSString stringWithFormat:@"https://api.vk.com/method/wall.post?owner_id=%@&access_token=%@&message=%@", user_id, accessToken, [self URLEncodedString:text]]; NSLog(@"sendTextMessage: %@", sendTextMessage); //            NSDictionary *result = [self sendRequest:sendTextMessage withCaptcha:NO]; //       NSString *errorMsg = [[result objectForKey:@"error"] objectForKey:@"error_msg"]; if(errorMsg) { [self sendFailedWithError:errorMsg]; } else { [self sendSuccessWithMessage:@"   !"]; } } 


For simplicity of example, I store the data in NSUserDefaults. Authorization passed, so we have everything you need to make a request. Using the wall.post method, we pass the user id (owner_id) and the token (access_token), we first run the message text for the message parameter through the URLEncodedString function:

 //         - (NSString *)URLEncodedString:(NSString *)str { NSString *result = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)str, NULL, CFSTR("!*'();:@&=+$,/?%#[]"), kCFStringEncodingUTF8); [result autorelease]; return result; } 




After we have prepared a request, you need to send it to the server using the function:
 [self sendRequest:sendTextAndLinkMessage withCaptcha:NO]; 
As you can see, the function accepts the withCaptcha: (BOOL) parameter, which we will need if the server requests a user to enter a captcha to confirm actions. Since no captcha is required for the time being, we transmit NO.



Actually, the function of sending a request:

 - (NSDictionary *) sendRequest:(NSString *)reqURl withCaptcha:(BOOL)captcha { //      ,      captcha_sid  captcha_key if(captcha == YES){ NSString *captcha_sid = [[NSUserDefaults standardUserDefaults] objectForKey:@"captcha_sid"]; NSString *captcha_user = [[NSUserDefaults standardUserDefaults] objectForKey:@"captcha_user"]; //      .  ,      . reqURl = [reqURl stringByAppendingFormat:@"&captcha_sid=%@&captcha_key=%@", captcha_sid, [self URLEncodedString: captcha_user]]; } NSLog(@"Sending request: %@", reqURl); NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:reqURl] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0]; //      NSURLConnection,     NSData NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; //    ,        JSONKit  NSDictionary if(responseData){ NSDictionary *dict = [[JSONDecoder decoder] parseJSONData:responseData]; //       NSString *errorMsg = [[dict objectForKey:@"error"] objectForKey:@"error_msg"]; NSLog(@"Server response: %@ \nError: %@", dict, errorMsg); //    .      Captcha needed,     . if([errorMsg isEqualToString:@"Captcha needed"]){ isCaptcha = YES; //     NSString *captcha_sid = [[dict objectForKey:@"error"] objectForKey:@"captcha_sid"]; NSString *captcha_img = [[dict objectForKey:@"error"] objectForKey:@"captcha_img"]; [[NSUserDefaults standardUserDefaults] setObject:captcha_img forKey:@"captcha_img"]; [[NSUserDefaults standardUserDefaults] setObject:captcha_sid forKey:@"captcha_sid"]; //  url         [[NSUserDefaults standardUserDefaults] setObject:reqURl forKey:@"request"]; [[NSUserDefaults standardUserDefaults] synchronize]; [self getCaptcha]; } return dict; } return nil; } 


The function returns the NSDictionary response of the server, JSONKit is used to form it , since by default, VKontakte responds in JSON format.



Captcha

If suddenly the server decided that the user needs to enter a captcha, then in response to the request, he will return an error with the text: Captcha needed and parameters - captcha_sid (these are id captcha), captcha_img (link to the captcha image). Armed with these parameters and at the same time saving the request for which the server answered us like that, we call the captcha input function:
 [self getCaptcha]; 


Consider it a little more:

 - (void) getCaptcha { NSString *captcha_img = [[NSUserDefaults standardUserDefaults] objectForKey:@"captcha_img"]; UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:@" :\n\n\n\n\n" message:@"\n" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; UIImageView *imageView = [[[UIImageView alloc] initWithFrame:CGRectMake(12.0, 45.0, 130.0, 50.0)] autorelease]; imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:captcha_img]]]; [myAlertView addSubview:imageView]; UITextField *myTextField = [[[UITextField alloc] initWithFrame:CGRectMake(12.0, 110.0, 260.0, 25.0)] autorelease]; [myTextField setBackgroundColor:[UIColor whiteColor]]; //   myTextField.autocorrectionType = UITextAutocorrectionTypeNo; //   myTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; myTextField.tag = 33; [myAlertView addSubview:myTextField]; [myAlertView show]; [myAlertView release]; } - (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if(isCaptcha && buttonIndex == 1){ isCaptcha = NO; UITextField *myTextField = (UITextField *)[actionSheet viewWithTag:33]; [[NSUserDefaults standardUserDefaults] setObject:myTextField.text forKey:@"captcha_user"]; NSLog(@"Captcha entered: %@",myTextField.text); //           NSString *request = [[NSUserDefaults standardUserDefaults] objectForKey:@"request"]; NSDictionary *newRequestDict =[self sendRequest:request withCaptcha:YES]; NSString *errorMsg = [[newRequestDict objectForKey:@"error"] objectForKey:@"error_msg"]; if(errorMsg) { [self sendFailedWithError:errorMsg]; } else { [self sendSuccessWithMessage:@"     !"]; } } } 


Using the standard UIAlertView, with the addition of a UIImageView and a UITextField, and the captcha_img parameter, we enable the user to enter a captcha. After the captcha is entered, in the UIAlertView delegate function, we save what the user entered into NSUserDefaults with the key captcha_user and using the previously saved request url, we re-enter it, this time specifying it in the withCaptcha: YES parameter. As a result, before sending the request, the captcha_sid and captcha_key parameters are added to it (what the user entered):

 - (NSDictionary *) sendRequest:(NSString *)reqURl withCaptcha:(BOOL)captcha { //      ,      captcha_sid  captcha_key if(captcha == YES){ NSString *captcha_sid = [[NSUserDefaults standardUserDefaults] objectForKey:@"captcha_sid"]; NSString *captcha_user = [[NSUserDefaults standardUserDefaults] objectForKey:@"captcha_user"]; //      .  ,      . reqURl = [reqURl stringByAppendingFormat:@"&captcha_sid=%@&captcha_key=%@", captcha_sid, [self URLEncodedString: captcha_user]]; } ... 


Captcha entered, request sent, text must be successfully posted on the user's wall!



2.2 Sending an Image to a Wall

Often there is a need to place a photo from the application on the user's wall. To solve this problem you need to go through the following steps:

  1. Request url server VKontakte to download our image ( photos.getWallUploadServer )
  2. According to the received link in the server's response we send the image using the POST method.
  3. Having received hash, photo, server, we will send a command to save the photo on the wall ( photos.saveWallPhoto )
  4. Having received photo id in the answer, we make a request to place a picture on the wall using wall.post , where we specify photo id as an attachment




2.2.1 Getting the url of the VK server to download the image

 - (IBAction)sendImageAction:(id)sender { if(!isAuth) return; UIImage *image = [UIImage imageNamed:@"test.jpg"]; NSString *user_id = [[NSUserDefaults standardUserDefaults] objectForKey:@"VKAccessUserId"]; NSString *accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:@"VKAccessToken"]; //  1 NSString *getWallUploadServer = [NSString stringWithFormat:@"https://api.vk.com/method/photos.getWallUploadServer?owner_id=%@&access_token=%@", user_id, accessToken]; NSDictionary *uploadServer = [self sendRequest:getWallUploadServer withCaptcha:NO]; //      NSString *upload_url = [[uploadServer objectForKey:@"response"] objectForKey:@"upload_url"]; ... 


Now, having received a link to download the image, we need to make a POST request with an attached image. To do this, convert the image to NSData and create a query using the function
 [self sendPOSTRequest:upload_url withImageData:imageData]; 




2.2.2 Form a POST request

 ... //  2 //    NSData NSData *imageData = UIImageJPEGRepresentation(image, 1.0f); NSDictionary *postDictionary = [self sendPOSTRequest:upload_url withImageData:imageData]; //     hash, photo, server NSString *hash = [postDictionary objectForKey:@"hash"]; NSString *photo = [postDictionary objectForKey:@"photo"]; NSString *server = [postDictionary objectForKey:@"server"]; ... 


You can see the function that forms the POST request in the example .



2.2.3 Create a request to save photos on the server VKontakte

 ... //  3 //        ,    id  NSString *saveWallPhoto = [NSString stringWithFormat:@"https://api.vk.com/method/photos.saveWallPhoto?owner_id=%@&access_token=%@&server=%@&photo=%@&hash=%@", user_id, accessToken,server,photo,hash]; NSDictionary *saveWallPhotoDict = [self sendRequest:saveWallPhoto withCaptcha:NO]; NSDictionary *photoDict = [[saveWallPhotoDict objectForKey:@"response"] lastObject]; NSString *photoId = [photoDict objectForKey:@"id"]; ... 


In response, we get the photo id of the loaded photo, now it remains to make the usual request wall.post, where as the attachment parameter specify photo id. If you still need to add a link, then it is enough to indicate it after the photo id, separated by a comma.



2.2.4 Place the photo on the wall

 ... //  4 //      NSString *postToWallLink = [NSString stringWithFormat:@"https://api.vk.com/method/wall.post?owner_id=%@&access_token=%@&message=%@&attachment=%@", user_id, accessToken, [self URLEncodedString:@"      "], photoId]; NSDictionary *postToWallDict = [self sendRequest:postToWallLink withCaptcha:NO]; NSString *errorMsg = [[postToWallDict objectForKey:@"error"] objectForKey:@"error_msg"]; if(errorMsg) { [self sendFailedWithError:errorMsg]; } else { [self sendSuccessWithMessage:@"   !"]; } // !     ! 




Part 3. User logout

To logout, just send a request to the VK server:

 NSString *logout = @"http://api.vk.com/oauth/logout"; 


An example of the logout function:

 - (IBAction)logout:(id)sender { //   logout NSString *logout = @"http://api.vk.com/oauth/logout"; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:logout] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0]; NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; if(responseData){ NSDictionary *dict = [[JSONDecoder decoder] parseJSONData:responseData]; NSLog(@"Logout: %@", dict); //    ,    isAuth = NO; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"VKAccessUserId"]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"VKAccessToken"]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"VKAccessTokenDate"]; [[NSUserDefaults standardUserDefaults] synchronize]; [self sendSuccessWithMessage:@"  !"]; } } 




Despite the fact that VKontakte responds with an error, the user's exit still happens :)

 Logout: { error = "invalid_client"; "error_description" = "client_id is incorrect"; } 




Conclusion

I hope that this article will help you in mastering the API Vkontakte. I would be happy for comments, additions.

Source: https://habr.com/ru/post/133504/



All Articles