UNIRest objective c Currency Converter Api - ios

I am trying to use the currency converter api from mashape located at https://www.mashape.com/ultimate/currency-convert#!
I am new to objective-c. I am trying to call the api through this code -
NSDictionary* headers = #{#"X-Mashape-Authorization": #"key"};
NSDictionary* parameters = #{#"amt": #"2", #"from": #"USD", #"to": #"INR", #"accuracy": #"2"};
UNIHTTPJsonResponse* response = [[UNIRest post:^(UNISimpleRequest* request) {
[request setUrl:#"https://exchange.p.mashape.com/exchange/?amt=120&from=usd&to=gbp&accuracy=3&format=json"];
[request setHeaders:headers];
[request setParameters:parameters];
}] asJson];
Can someone tell me how I can access the information returned and also how to send the parameter 2 as a number instead of a string.
Thanks for all the help.

It seems like mashape's APIs are not all standardized to the point of taking parameters from the parameter array - you need to pass them in the setUrl call of your UNIHTTPJsonResponse object.
Also, using async calls when getting data from a remote API like this is a Good Idea.
NSDictionary* headers = #{#"X-Mashape-Authorization": #"key"};
[[UNIRest post:^(UNISimpleRequest* request) {
[request setUrl:#"https://exchange.p.mashape.com/exchange/?amt=120&from=usd&to=gbp&accuracy=3&format=json"]; // this is where you want to set your currencies, amounts, etc.
[request setHeaders:headers];
[request setParameters:#{}]; // is this needed? I dunno
}] asJsonAsync:^(UNIHTTPJsonResponse* response, NSError *error) {
if (error) {
NSLog(#"%#",[error localizedDescription]);
} else {
// here you do all the stuff you want to do with the data you got.
// like launch any code that actually deals with the data :)
NSDictionary *currencyResult = [NSJSONSerialization JSONObjectWithData:[response rawBody] options: 0 error: &error];
NSLog(#"%#", currencyResult);
}
}];

Related

iOS client: I can have user sign into Active Directory, but how to access MS Graph API?

I'm developing an iOS app that needs to read User data from MS Azure Active Directory.
I have successfully followed some examples on iOS app from the MS Azure documentation and successfully brought up their authentication page and have the user signed in. What I get back is some user data in the form of a ADUserInformation object.
Here's is the code I have:
NSString *authority = #"https://login.microsoftonline.com/a5960f61-0bf9-4bf6-96cd-98c61d30XXXX/federationmetadata/2007-06/federationmetadata.xml";
NSString *resourceId = #"74cd2559-0389-4871-9904-bc767d71XXXX"; // (server)
NSString *clientId = #"c8a956a7-84b7-4050-875c-896aab6bXXXX"; //ios-client (us)
NSURL *redirectUri = [[NSURL alloc]initWithString:#"https://XXXXevents.azurewebsites.net/.auth/login/done"];
ADAuthenticationError *error;
ADAuthenticationContext * authContext = [ADAuthenticationContext authenticationContextWithAuthority:authority error:&error];
//authContext.parentController = parent;
[ADAuthenticationSettings sharedInstance].enableFullScreen = YES;
[authContext acquireTokenWithResource:resourceId
clientId:clientId
redirectUri:redirectUri
completionBlock:^(ADAuthenticationResult *result) {
if (result.status != AD_SUCCEEDED) {
NSLog(#"%#", result);
return;
}
else {
//save all of this information into core data
NSDictionary * payload = #{#"access_token" : result.tokenCacheItem.accessToken};
NSLog(#"%#", payload);
//#"aad"
//#"windowsazureactivedirectory"
[[QSActivityService defaultService].client loginWithProvider: #"aad"
token: payload
completion: ^(MSUser * _Nullable user, NSError * _Nullable error) {
NSLog(#"loginWithProvider-------");
if(!error) {
NSLog(#"YAY! %s - user: %# ", __FUNCTION__, user.userId);
ADUserInformation * temp = result.tokenCacheItem.userInformation;
[[CoreDataStack defaultStack] updateUserDetailFamilyName:temp.allClaims[#"family_name"]
version:temp.allClaims[#"ver"]
email:temp.allClaims[#"email"]
nbf:temp.allClaims[#"nbf"]
exp:temp.allClaims[#"exp"]
givenName:temp.allClaims[#"given_name"]
idp:temp.allClaims[#"idp"]
ipaddr:temp.allClaims[#"ipaddr"]
iss:temp.allClaims[#"iss"]
oid:temp.allClaims[#"oid"]
typ:temp.allClaims[#"typ"]
sub:temp.allClaims[#"sub"]
amr:temp.allClaims[#"amr"]
aud:temp.allClaims[#"aud"]
alg:temp.allClaims[#"alg"]
iat:temp.allClaims[#"iat"]
tid:temp.allClaims[#"tid"]
name:temp.allClaims[#"name"]
uniqueName:temp.allClaims[#"unique_name"]];
//other code, no problems here
MS Graph API
However, I would like access profile images, and all the other data. I have read that MS Graph API provides it, but I'm not sure how and where I would put the token.
Do I use the token from result.tokenCacheItem.accessToken? If so, in the header? or body?
Or do I simply hit up graph.windows.com twice. First time to get the Authentication Token, and second time for the data?
I have read a lot of documentation and none of them works as I keep getting the Token Missing or Malformed error message.
My Graph API code looks like this:
-(void)getUsersUsingAccessToken:(NSDictionary*)token completion:(void (^) (void))completion {
NSString * tenant = #"a5960f61-0bf9-4bf6-96cd-98c61d306f12";
NSString * accessToken = token[#"access_token"];
NSString * urlString = [NSString stringWithFormat: #"https://graph.windows.net/%#/tenantDetails?api-version=1.6", tenant];
NSString * httpVerb = #"POST";
//build an info object and convert to json
NSDictionary * bodyFormDict
= [NSDictionary dictionaryWithObjectsAndKeys:
#"client_credentials", #"grant_type",
#"https://graph.windows.net", #"resource",
#"c8a956a7-84b7-4050-875c-896aab6xxxx", #"client_id",
#"XLlZl69aUKiQTo4dpeiprItm+LYbDtpt6e9dn0bxxxx", #"client_secret",
nil];
NSError *error = nil;
//1st step
NSData * jsonInputData = [NSJSONSerialization dataWithJSONObject:bodyFormDict
options:NSJSONWritingPrettyPrinted
error:&error];
//2nd step
NSString * httpBodyString = [[NSString alloc]
initWithData:jsonInputData
encoding:NSUTF8StringEncoding];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.allowsCellularAccess = YES;
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
request.HTTPMethod = httpVerb;
[request setValue: #"application/json; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setValue: accessToken forHTTPHeaderField:#"Authorization: Bearer"];
[request setHTTPBody:[httpBodyString dataUsingEncoding:NSUTF8StringEncoding]];
//asynchronous
NSURLSessionDataTask * getDataTask = [self.session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
//other code
}
If someone can provide working code sample using objective c to successfully retrieve data from the MS Graph API, it would be a great help.
Thanks for your time!
I believe the problem you are having is that the http header field isn't set correctly. Try this -
NSString *authValue = [NSString stringWithFormat:#"Bearer %#", accessToken];
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
I found my answers in MS MSDN's starter projects and code samples
https://msdn.microsoft.com/en-us/office/office365/howto/starter-projects-and-code-samples
The specific project that helped me is this:
https://github.com/OfficeDev/O365-iOS-Microsoft-Graph-Profile
As you are going through that sample keep in mind:
Replace INSERT-AUTHORITY-HERE - the name of the tenant in which you provisioned your application. The format should be https://login.windows.net/(YourAzureUserName).onmicrosoft.com
I have found that
https://login.microsoftonline.com/YourAzureAccountID/federationmetadata/2007-06/federationmetadata.xml
also works
Replace INSERT-RESOURCE-ID-HERE - the ID for your mobile app backend. This is the Web API service app ID. NOT the native client iOS app.
Replace INSERT-CLIENT-ID-HERE - the client ID you copied from your iOS NATIVE client application. NOT the Web API service app.
Replace INSERT-REDIRECT-URI-HERE - your site’s /.auth/login/done endpoint, using the HTTPS scheme. This value should be similar to
#"https://XXXXXXXXXX.azurewebsites.net/.auth/login/done"
IF you have trouble importing the ADAL framework...
http://shanghaiseagull.com/index.php/2016/05/11/import-another-project-framework-into-your-project/
library can be found here: https://github.com/AzureAD/azure-activedirectory-library-for-objc
Hope it helps someone starting out...and please let me know if I can be of further help.

How to display json file data to UICollectionView

Here I tried to get data from my URL used base64,shaa256 for authorize Header and I just tried to display in my console. It's worked well. But I need to convert to NSDictionary and to display to my UICollectionView. And my data having 3 sections with different data. Each section having 12 - 20 files.
So my question are:
How to convert my url to json to NSDictionary ?
Hoe to display with 3 section & each section should have 12 to 20 data files?
I used only this code in my viewdidload to get data.does i need to add 1.Connection did receive response 2.Connection did receive data 3.connection didFailWithError 4.connectionDidFinishLoading to my code or don't want these 4 method to add in to my code
Here is my code:
I use to insert my below code to my above code after
NSString *str = [[NSString alloc] initWithData:returnData
encoding:NSUTF8StringEncoding];
NSError * error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:str
options: NSJSONReadingMutableContainers
error: &error];
NSLog(#"%#",json);
But it din't work.if any useful answer for my 3 question is very useful for me.i am new to ios.help me out.Thanks in advance !
The data will most probably be JSON and not plain text? So convert the response straight to NSDictionary:
// show loading indicators when you are fetching data
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// hide loading indicators when you received a response
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (connectionError) {
// Handle error
} else {
NSDictionary *jsonResults = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSLog(#"jsonResults: %#", jsonResults);
// reload your views
// [self.myCollectionView reloadData];
}
}];
To make the collectionView with 3 sections you would need to have an NSArray containing the 3 sections, each of these 3 arrays would contain the 12-20 dictionaries with keys and values that will populate each cell.
NSArray *sectionOne = #[#{#"Section":#"1", #"Object":#"1"}, #{#"Section":#"1", #"Object":#"2"}];
NSArray *sectionTwo = #[#{#"Section":#"2", #"Object":#"1"}, #{#"Section":#"2", #"Object":#"2"}];
NSArray *sectionThree = #[#{#"Section":#"3", #"Object":#"1"}, #{#"Section":#"3", #"Object":#"2"}];
NSArray *collectionData = #[sectionOne, sectionTwo, sectionThree];
NSLog(#"collectionData: %#", collectionData);

Freesound OAuth2 authentication fails

I'm trying to create an iOS app that uses OAuth2 authentication using the native iOS NSURLSession URL loading classes. I gain an access token fine using the directions here:
http://www.freesound.org/docs/api/authentication.html
I subsequently launch the application and run a search query
https://www.freesound.org/apiv2/search/text/?query=snare
The request header fields looks like this (note my access token is not expired and I have confirmed it is the same as I received from performing the steps above)
{
"Authorization: Bearer" = MY_ACCESS_TOKEN;
}
This fails with:
{"detail": "Authentication credentials were not provided."}
The response headers look like this:
{
Allow = "GET, HEAD, OPTIONS";
Connection = "keep-alive";
"Content-Type" = "application/json";
Date = "Sat, 31 Jan 2015 13:56:32 GMT";
Server = "nginx/1.2.1";
"Transfer-Encoding" = Identity;
Vary = "Accept, Cookie";
"Www-Authenticate" = "Bearer realm=\"api\"";
}
The funny thing is that this does not always happen. If I repeat this entire process a number of times, deleting the app in between, it will eventually work. Once it works, it will continue to work while I'm developing. Sometimes then when I come back to it, say the next day, it stops working and I need to repeat this deleting and re-installing routine to get it back working again!
There's an authentication challenge delegate method on NSURLSession that will get called if implemented. It's a 'server trust' challenge. Could this be something to do with it? Would you even expect an authentication challenge of this nature? There's nothing mentioned about it in the docs alluded to above.
Any help would be much appreciated.
EDIT
This is how the search text ("snare") GET call is made.
I basically pass in an NSMutableURLRequest with the URL set to the above (https://www.freesound.org/apiv2/search/text/?query=snare). useAccessToken is set to YES.
- (void)makeRequest:(NSMutableURLRequest *)request useAccessToken:(BOOL)useAccessToken completion:(CompletionBlock)completion {
NSAssert(completion, #"No completion block.");
if (useAccessToken) {
NSString *accessToken = [[ODMFreesoundTokenCache sharedCache] accessToken];
NSAssert(accessToken.length, #"No access token.");
[request addValue:accessToken forHTTPHeaderField:#"Authorization: Bearer"];
}
NSLog(#"Making request: %# \n\nWith access token: %#", request, [[ODMFreesoundTokenCache sharedCache] accessToken]);
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSInteger code = [(NSHTTPURLResponse *)response statusCode];
if (code == 200) {
if (!error) {
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"json: %#", json);
completion(json, error);
}
else {
completion(nil, error);
}
}
else {
NSString *reason = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSError *error = [NSError errorWithDomain:#"Request Error" code:code userInfo: reason ? #{NSLocalizedDescriptionKey : reason} : nil];
NSLog(#"error: %#", error);
completion(nil, error);
}
}];
[task resume];
}
The 2 flows for authentication described in the doc are not "safe" for a device. Using API keys would require the secret to be stored in the device.
The OAuth2 flow they support (authorization_code) requires a server to server call to exchange a code for the actual token (This step: http://www.freesound.org/docs/api/authentication.html#step-3). This call requires another credential (the client_secret that you probably should not store in the device either.
You need a server in between that negotiates this for you. Or a server that translates the code flow into token one. (Illustrated here: https://auth0.com/docs/protocols#5).

iOS: How would a put request work in this scenario?

I am using AFNetworking and trying to figure out how to use PUT requests properly. This is what the API document has given me (just an easy example).
curl -X PUT -d '{"problem":[{"problem":"text"}]}'
"https://api.website.com/form?apiKey={apiKey}"
Params is
'{"problem":[{"problem":"text"}]}' while urlStr is https://api.website.com/form?apiKey={apiKey} If you need any more information please let me know.
Here is my put request being executed.
- (void) executePutRequest : (NSString *) url params : (NSString *) params
{
NSString *urlStr = [NSString stringWithFormat:#"%#?apikey=%#", url,apiKey];
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:
NSASCIIStringEncoding];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager PUT:urlStr parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
[operation setUserInfo:userinfo];
SBJsonParser *jsonparser = [SBJsonParser new];
id result = [jsonparser objectWithString:[operation responseString]];
if ( self.delegate != nil && [self.delegate respondsToSelector:finishSelector] ) {
[self.delegate performSelector:finishSelector withObject:result];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[operation setUserInfo:userinfo];
if ( self.delegate != nil && [self.delegate respondsToSelector:failSelector] ) {
[self.delegate performSelector:failSelector withObject:[operation error]];
}
}];
}
What am I doing wrong? I am getting a 400 error (bad request). This is what my log looks like.
Error Domain=com.alamofire.error.serialization.response Code=-1011 "Request failed: bad request (400)" UserInfo=0x7aee9a40 {com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x7aeea400>
AFNetworking is meant to do some of the redundant work for you, including serializing and deserializing output to server and input from server respectively. Your code implies that you are trying to do it manually, too, which is incorrect.
You have params set to be an NSString *. Just pass it the NSDictionary * it expects. e.g. NSDictionary *params = #{ #"problem": #[ #{ #"problem": #"text" }]};, from your example.
If you enter the success block, then responseObject should be deserialized into the format you have configured. IIRC, the default response format is JSON -- but look it up. You don't need to further process the response -- it's been done for you. For example, if the JSON body is something like { "cat": { "says": "meow" } }, then simply cast the object to an NSDictionary; e.g. NSDictionary *catDict = (NSDictionary *)responseObject;
Did you or your team write the web service? Do you know what the expected request and response format is?
If you have to further configure the request and response serializers, you can do so in your manager's constructor (so subclass the manager, and make your own). For e.g,
[self setRequestSerializer:[AFJSONRequestSerializer serializer]];
[self setResponseSerializer:[AFJSONResponseSerializer serializer]];
... would configure the client to send JSON to the server, and expect JSON back from the server.

objective c ios 7 get data from rest api

i want to get all articles from the shopware api(http://wiki.shopware.de/Shopware-API_cat_919.html)
but the i dont get the data into an NSDictionary
url i call: http://myshop.com/api/articles
here is the source i got
NSURL *url = [NSURL URLWithString:weburl];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError) {
if (data.length > 0 && connectionError == nil) {
NSDictionary *rest_data = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
_newsDataForTable = [NSMutableArray array];
NSDictionary *news;
for (id key in rest_data[#"postalcodes"]) {
news = [rest_data[#"postalcodes"] objectForKey:key];
}
int iterator = 0;
for (id key in news) {
[_newsDataForTable insertObject:key[#"title"] atIndex:iterator];
iterator++;
}
[_newsTable reloadData];
[_newsTable numberOfRowsInSection:[_newsDataForTable count]];
[_newsTable reloadRowsAtIndexPaths:0 withRowAnimation:UITableViewRowAnimationLeft];
}
}];
}
There are a couple of things in your approach that could use improvement.
First, this is performing networking on the main queue. That is a no-no, wether the networking is synchronous or not. Creating a new NSOperationQueue for your connections and passing that instead of [NSOperationQueue mainQueue] is a huge improvement.
Second, the error handling is incorrect. In general the correct error handling pattern for Objective-C is to check wether a call resulted in the expected result before using the error. In this case, it's the NSURLResponse that should be checked, not the data. NSURLConnection may be able to connect to the remove service just fine but get no data back - and for many HTTP requests this is expected, correct behavior. If there is a problem connecting, the NSURLResponse will be nil. Check wether the response is nil, if it is then handle the error.
You're also not checking the HTTP response status code or MIME type. The server could respond with a 500, indicating a server error, or could mistakenly send you HTML (which would give the JSON parser fits).
A verbose example that does the above correctly is here. :
[NSURLConnection sendAsynchronousRequest:request queue:[self connectionQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (response != nil){
if ([[self acceptableStatusCodes] containsIndex:[(NSHTTPURLResponse *)response statusCode] ]){
// The server responded with an HTTP status code that indicates success
if ([[self acceptableMIMETypes] containsObject:[[response MIMEType] lowerCaseString] ]){
// The server responded with a MIME type we can understand.
if ([data length] > 0){
NSError *jsonError = nil;
id jsonObject = nil;
// The server provided data in the response, which means we can attempt to parse it
// Note that we are not specifying NSJSONReadingMutableContainers or NSJSONReadingMutableLeaves, as this would result in
// an object that is not safe to use across threads.
jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError];
if (jsonObject != nil){
// The JSON parser successfully parsed the data, and returned an object. There is nothing to tell us what kind of object was returned.
// We need to make sure it responds to the selectors we will be using - ideally, we'd pass this object to a method that takes an
// id parameter, not NSDictionary, and inside that method it would check wether the id object responds to the specific selectors
// it is going to use on it.
if ([jsonObject respondsToSelector:#selector(dictionaryWithDictionary:)]){
[self doStuffWithDictionary:jsonObject];
}
} else {
// The JSON parser was unable to understand the data we provided, and the error should indicate why.
[self presentError:jsonError];
}
} else {
// The server responded with data that was zero length. How you deal with this is up to your application's needs.
// You may create your own instance of NSError that describes the problem and pass it to your error handling, etc.
}
} else {
// The server response was a MIME type we could not understand. How you handle this is up to you.
}
} else {
// The server response indicates something went wrong: a 401 Not Found, etc.
// It's up to your application to decide what to do about HTTP statuses that indicate failure.
// You may create your own instance of NSError that describes the problem and pass it to your error handling, etc.
}
} else {
// Only inspect the error parameter if the response is nil.
// The error indicates why the URL loading system could not connect to the server.
// It is only valid to use this error if the server could not connect - which is indicated by a nil response
[self presentError:connectionError];
}
}];
// Returns the HTTP status codes we find acceptable.
- (NSIndexSet *) acceptableStatusCodes {
return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 99)];
}
// Returns the mime types we can accept and understand.
- (NSSet *) acceptableMimeTypes {
NSSet *result = nil;
result = [NSSet setWithObjects:#"application/json", #"application/json; charset=utf-8", nil];
return result;
}
// Generic error handling method.
- (void) presentError:(NSError *)error {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
}];
}
Yup, that's a lot of code, and it should be broken into smaller methods - but it illustrates the logic that should be implemented.
The NSError you are getting now
In your comments you indicate that you are getting an NSError with the domain NSURLErrorDomain and code -1002. If you look at NSURLErrors.h, you will see that NSURL errors map to CFURL errors. If you look at CFNetworkErrors.h, you can see that error code -1002 is kCFURLErrorUnsupportedURL. The URL loading system thinks the URL you are using is not a supported type. This is most likely because the scheme of your URL is incorrect, or how you are attempting to pass credentials as part of the URL is incorrect. Elsewhere in your comments you indicate you are passing credentials as follows:
username:apikey:someurl.com/foo/
Which should be more like:
https://username:apikey#someurl.com/foo/
But only if the service you are accessing is using a supported HTTP authentication type (i.e. Basic authentication). Either way, correctly composing the URL will fix the error you are currently seeing.

Resources