I have an application which implements remote notifications via firebase messaging api. In this app, I have implemented a notification service extension, which among others, implement UNNotificationActions.
In one of these actions, I've implemented an input field where you can write something, which then should be posted to firestore.
I've tried implementing this, but without success. So my question is how can I write to firestore from a rich notification running in the background - is this even possible?
My implementation looks like this:
let likeAction = UNNotificationAction(identifier: "likeAction", title: "Like", options: [])
let commentAction = UNTextInputNotificationAction(identifier: "commentAction", title: "Comment", options: [UNNotificationActionOptions.authenticationRequired], textInputButtonTitle: "Send", textInputPlaceholder: "Type your message")
let category = UNNotificationCategory(identifier: "posts", actions: [likeAction, commentAction], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
Then in AppDelegate, I implement the function to run whenever this is triggered like this:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
switch response.actionIdentifier {
case "commentAction":
guard let data = response.notification.request.content.userInfo["data"] as? [String: Any] else { return }
guard
let channelName = data["channelName"],
let postId = data["postId"]
else { return }
if let message = response as? UNTextInputNotificationResponse {
let documentPath = "\(channelName)/\(postId))"
let post = Post()
post.documentPath = documentPath
post.addComment(text: message.userText, postDocumentPath: documentPath)
}
I've debugged the code, and the method post.addComment() does actually get fired, and every field has a value. When I check the database, nothing gets inserted into it. The console prints out this, which I don't know if is related to the problem, I haven't been able to find anything online about these lines:
dnssd_clientstub deliver_request ERROR: write_all(21, 65 bytes) failed
nssd_clientstub read_all(26) DEFUNCT
When running the post method, no error from firebase comes up.
This was the initial information I could think of. I can provide more code or info if need be.
Update
I've discovered if I press the button while on lock screen, but with the iPhone in an unlocked state, nothing happens - as soon as I swipe up, and the app shows, the request gets send. This does indeed seem to be a background issue.
Kind regards Chris
So I finally found a solution / workaround.
It turns out that firebase will not post in the background, instead it stores the data locally, until the app comes into foreground, then it will post it.
The solution was to add another firebase function that listens for HTTP requests. In this function, I added a method to post to the database with the data from the action.
I then do a regular but modified http post request with Alamofire from the action to the url like this:
let headers: HTTPHeaders = [
"Authorization": "Bearer \(token)",
"Content-Type": "application/json"
]
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 15.0
configuration.timeoutIntervalForResource = 15.0
configuration.waitsForConnectivity = true
self.alamofireManager = Alamofire.SessionManager(configuration: configuration)
guard let manager = self.alamofireManager else {
return
}
manager.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseString(completionHandler: { (response) in
let _ = manager
print(response)
closure(true)
})
The important part of this, for me, was to set configuration.waitsForConnectivity = true otherwise, it would come back saying no connection to the internet.
This enables the function to comment on a notification from the lock screen.
Hope this information helps others looking for the same.
For anyone using a standard NSURLSession to complete the firestore HTTP REST request (and not Alamofire like in #ChrisEenberg's excellent answer above)
[NB: A previous version of this answer used background tasks, which are not necessary. This edit uses an ordinary NSURLSessionDataTask upload task and appears to work when app is backgrounded or closed, as expected.]
OBJECTIVE-C
Inside didReceiveNotificationResponse:
Prepare request
// Grab authenticated firestore user
FIRUser *db_user = [FIRAuth auth].currentUser;
// Set up the response
NSDictionary *reqFields;
// Fields for firestore object (REST API)
NSDictionary *my_uid_val = [[NSDictionary alloc] initWithObjectsAndKeys: (db_user.uid ?: [NSNull null]), (db_user.uid ? #"stringValue" : #"nullValue"), nil];
NSDictionary *action_val = [[NSDictionary alloc] initWithObjectsAndKeys: response.actionIdentifier, (response.actionIdentifier ? #"stringValue" : #"nullValue"), nil];
// Create object
reqFields = #{
#"my_uid": my_uid_val,
#"action": action_val
};
// Place fields into expected reqBody format (i.e. under 'fields' property)
NSDictionary *reqBody = [[NSDictionary alloc] initWithObjectsAndKeys:
reqFields, #"fields",
nil];
// Confirm...?
NSLog(#"%#", reqBody);
Compose the request
// Grab current user's token (for authenticated firestore REST API call)
[db_user getIDTokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
if (!error && token) {
NSLog(#"Successfully obtained getIDTokenWithCompletion: %#", token);
// Compose stringified response
// + (per https://stackoverflow.com/a/44923210/1183749 )
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:reqBody options:kNilOptions error:&error];
if(!postData){
NSLog(#"Error creating JSON: %#", [error localizedDescription]);
}else{
NSLog(#"Successfully created JSON.");
}
// Create the request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://firestore.googleapis.com/v1beta1/projects/%#/databases/%#/documents/%#", #"project_id", #"(default)", #"collection_id"]]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"Bearer %#", token] forHTTPHeaderField:#"Authorization"]; // 'token' is returned in [[FIRAuth auth].currentUser getIDTokenWithCompletion]. (All of this code resides inside the getIDTokenWithCompletion block so we can pass it along with the request and let firebase security rules take care of authenticating the request.)
[request setHTTPBody:postData];
// Set up the session configuration
NSURLSessionConfiguration *sessionConfig;
sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
// Set up the session
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
Start the upload task
// Start the upload task
NSURLSessionDataTask *uploadTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *uploadTaskResp, NSError *error) {
NSLog(#"dataTask Request reply: %#", uploadTaskResp);
if(error){
NSLog(#"dataTask Request error: %#", error);
}
// Call completion handler
completionHandler();
}
}
}
Related
I'm trying to read remote txt file, which is located at remote hosting. I have a link, like http://www.link.com/file.txt.
I am using this code:
let myURLString = "http://google.com"
guard let myURL = NSURL(string: myURLString) else {
print("Error: \(myURLString) doesn't seem to be a valid URL")
return
}
do {
let myHTMLString = try String(contentsOfURL: myURL)
print("HTML : \(myHTMLString)")
} catch let error as NSError {
print("Error: \(error)")
}
But always getting empty string. File is not empty for sure.
I'm running my app on iOS simulator in Xcode.
What am I doing wrong?
Sorry for my bad English.
Don't use stringWithContentsOfURL and friends to retrieve data from a remote server. That's a synchronous API that was designed solely for use with local files. It isn't even guaranteed to work right for network requests on background threads, much less in your main thread.
The right way to retrieve data from remote URLs is with NSURLSession. I'm not a Swift programmer, so I'm not going to attempt a Swift snippet, but the Objective-C equivalent is:
NSURL *url = ...
NSURLSessionDataTask *task =
[NSURLSession sharedSession dataTaskWithURL:url
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (error) {
// Handle client-side errors here
} else if (((NSHTTPURLResponse *)response).statusCode != 200) {
// Handle server-side errors here
} else {
NSString *stock = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// Do something with data here.
}
}];
[task resume];
Haven't test the code, but this should lead you on the right direction.
var statsRequest: String = "http://www.link.com/file.txt"
var statsRequestURL: NSURL = NSURL(string: statsRequest)!
var error: NSError? = nil
var stockNews: String = try! String.stringWithContentsOfURL(statsRequestURL, encoding: NSUTF8StringEncoding)
If the text file has a different encoding you can just use NSASCIIStringEncoding and then parse the html file.
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.
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).
I just started ios development and I'm trying to exchange data with my api. When I'm doing POST requests everything is going fine but when I'm trying to do a GET request I get the following error:
Error Domain=NSURLErrorDomain Code=-1017 "The operation couldn’t be
completed. (NSURLErrorDomain error -1017.)" UserInfo=0x145a2c00
{NSErrorFailingURLStringKey=http://myAPI.com/,
_kCFStreamErrorCodeKey=-1, NSErrorFailingURLKey=http://myAPI.com,
_kCFStreamErrorDomainKey=4, NSUnderlyingError=0x145b21d0 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error
-1017.)"}
Could someone explain what's going wrong and how I can fix it?
My request:
-(void)hitApiWithURL:(NSString*)url HTTPMethod:(NSString*)HTTPMethod params:(NSDictionary*)params successBlock:(successTypeBlock)success failureBlock:(errorTypeBlock)failure{
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
[sessionConfig setHTTPAdditionalHeaders:#{#"Content-type": #"application/json"}];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:HTTPMethod];
// The body
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
[request setHTTPBody:jsonData];
NSURLSessionDataTask *dataTaks = [session dataTaskWithRequest:request];
[dataTaks resume];
NSLog(#"dataTask started");
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
if (error) {
//Gives my error
}
else {
// do something:
}
}
If you forget to mention HTTPMethod, the error can also take place, I faced the Problem "NSURLErrorDomain Code=-1017", somehow i forget to add line "request.HTTPMethod = "POST".
After adding the line, my code worked perfectly.
You have something wrong with your JSON parameters:
kCFURLErrorCannotParseResponse = -1017
This error occurs when you perform a GET request with the body set (setHTTPBody)
It isn't direct solution to this case but might help someone with -1017 error.
I had this issue after upgrading Alamofire from 4.8.0 to 5.2.2. Problem was with HTTPHeader.
var authorizationHeader: HTTPHeader {
guard let session = user?.session else {
return HTTPHeader(name: "", value: "")
}
return HTTPHeader(name: "Authorization", value: "Bearer \(session)")
}
Apparently sending HTTPHeader with empty key-value i-e HTTPHeader(name: "", value: "") triggers this error. I adjusted my code to instead return nil and it solved the issue.
For any other people who got error code -1017 -- I fixed it by manually setting my http headers in my http request. I think the server was having problems parsing the HTTP headers because instead of the string "Authorization" : "qii2nl32j2l3jel" my headers didn't have the quotes like this: Authorization : "1i2j12j". Good luck.
Something like this:
NSMutableDictionary* newRequestHTTPHeader = [[NSMutableDictionary alloc] init];
[newRequestHTTPHeader setValue:authValue forKey:#"\"Authorization\""];
[newRequestHTTPHeader setValue:contentLengthVal forKey:#"\"Content-Length\""];
[newRequestHTTPHeader setValue:contentMD5Val forKey:#"\"Content-MD5\""];
[newRequestHTTPHeader setValue:contentTypeVal forKey:#"\"Content-Type\""];
[newRequestHTTPHeader setValue:dateVal forKey:#"\"Date\""];
[newRequestHTTPHeader setValue:hostVal forKey:#"\"Host\""];
[newRequestHTTPHeader setValue:publicValue forKey:#"\"public-read-write\""];
//the proper request is built with the new http headers.
NSMutableURLRequest* request2 = [[NSMutableURLRequest alloc] initWithURL:request.URL];
[request2 setAllHTTPHeaderFields:newRequestHTTPHeader];
[request2 setHTTPMethod:request.HTTPMethod];
PLease check this link
Alamofire.request(method, "(URLString)" , parameters: parameters, headers: headers).responseJSON { response in }
in post method you also add
parameters:parameters, encoding: .JSON
(if you added encoding line to GET POST then error will come "cannot parse response")
2nd please check the header as well.
["authentication_token" : "sdas3tgfghret6grfgher4y"]
then Solved this issue.
For me when send one empty header like ["":""]. Triggers this error 1017.
Solution, if you have declare:
let headers = ["":""]
the best one option is set to nil:
let headers = nil
Then you can:
sessionManager.request(
"www.example.com",
method: .post,
parameters: parameters,
encoding: encoding,
headers: headers
).responseJSON { response in....
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);
}
}];