Retrieve All Recordings on Twilio Using Programable Voice SDK - objective-c

I'm attempting to download all Recording objects associated with a Twilio account but do not see any methods in TwilioVoiceClient.h or VoiceClient.h that achieve this. I'm using the ProgramableVoice iOS SDK (beta 5), but the online documentation doesn't show any objective-c or Swift. I'm able to playback individual recordings with no problems if the Recording SID is known like so:
NSString *baseString = #"https://api.twilio.com/2010-04-01/Accounts/";
NSString *recordingSID = #"RExxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
NSURL *recordingUrl = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#/Recordings/%#.mp3", baseString, kTwilAccount, recordingSID]];
avPlayer = [[AVPlayer alloc]initWithURL:recordingUrl];
What I'd like to do though, is download all associated recording objects for an account. For this, I've turned to NSUrlConnection:
NSString *baseStr = [NSString stringWithFormat:#"https://api.twilio.com/2010-04-01/Accounts/%#/Recordings.json", kTwilAccount];
NSURL *url = [NSURL URLWithString:baseStr];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:1000.0];
NSString *authStr = [NSString stringWithFormat:#"%#:%#", kTwilAccount, authToken];
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
NSString *authValue = [NSString stringWithFormat:#"Base %#", [authData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]];
[theRequest setValue:authValue forHTTPHeaderField:#"Authorization"];
[NSURLConnection sendAsynchronousRequest:theRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *responseCode, NSData *responseData, NSError *responseError) {
if ([responseData length] > 0 && responseError == nil){
// do work
} else if ([responseData length] == 0 && responseError == nil){
NSLog(#"data error: %#", responseError);
} else if (responseError != nil && responseError.code == NSURLErrorTimedOut){
NSLog(#"data timeout: %ld", (long)NSURLErrorTimedOut);
} else if (responseError != nil){
NSLog(#"data download error: %#", responseError);
}
}];
This results in a download error with console output:
data download error: Error Domain=NSURLErrorDomain Code=-1012 "(null)" UserInfo={NSErrorFailingURLStringKey=https://api.twilio.com/2010-04-01/Accounts/ACdee27262ef5fd27a593a697d80e7f7b0/Recordings.json, NSUnderlyingError=0x17084aa70 {Error Domain=kCFErrorDomainCFNetwork Code=-1012 "(null)" UserInfo={_kCFURLErrorAuthFailedResponseKey={url = https://api.twilio.com/2010-04-01/Accounts/ACdeedkfdjieireijrekjrkejrk4kj4/Recordings.json}}}, NSErrorFailingURLKey=https://api.twilio.com/2010-04-01/Accounts/ACdeedkfdjieireijrekjrkejrk4kj4/Recordings.json}
Obviously, it either doesn't recognize the endpoint or doesn't like the way I'm authenticating. How should I be requesting this information in objective-c?
Thanks for reading.

Twilio developer evangelist here.
We actually don't recommend that you put your account credentials into the application and make API calls from your application. It would be possible for a malicious attacker to decompile your application, retrieve your credentials and abuse your account.
We recommend instead that you implement a server side solution to make the call to the API and retrieve your Recordings. Then you can communicate with your server from the application.
Here's a blog post that explains more and includes an example for sending an SMS from an iOS application.

Related

Facebook Graph API Fetching User's Profile Photo

I'm using the Facebook Graph API v2.0. The following call
[FBRequestConnection startWithGraphPath:#"/me/picture" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
NSLog(#"%#", result);
} else {
NSLog(#"%#", [error userInfo]);
}
}];
results in this error:
{
NSLocalizedDescription = "Response is a non-text MIME type; endpoints that return images and other binary data should be fetched using NSURLRequest and NSURLConnection";
"com.facebook.sdk:ErrorSessionKey" = "<FBSession: 0x10f036250, state: FBSessionStateOpen, loginHandler: 0x100073a40, appID: 863523550341327, urlSchemeSuffix: , tokenCachingStrategy:<FBSessionTokenCachingStrategy: 0x10f0339c0>, expirationDate: 4001-01-01 00:00:00 +0000, refreshDate: 2014-07-27 17:20:14 +0000, attemptedRefreshDate: 0001-12-30 00:00:00 +0000, permissions:(\n installed,\n \"public_profile\",\n email,\n \"user_birthday\",\n \"user_location\",\n \"user_friends\"\n)>";
"com.facebook.sdk:HTTPStatusCode" = 0;
}
Does anyone know why this is happening? All I really need is a response containing a url to my profile picture. In 2012 and earlier, similar questions were asked but the API has changed since then. Please don't mark this as a duplicate unless you're actually sure that the question you redirect me to has a working solution. I've tried a couple of other alternatives such as starting the connection myself and using a graph path such as "/me?fields=picture" but they all result in the same error.
It seems my original call requests an image. To get a text based response that gives a url to the profile photo, the following worked for me:
[FBRequestConnection startWithGraphPath:#"me?fields=picture.height(500),picture.width(500)"completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (error) {
}
}];
For the new Facebook Graph API I use:
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc]
initWithGraphPath:#"/me?fields=id,name,picture" //As many fields as you need
parameters:nil
HTTPMethod:#"GET"];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection,
id result,
NSError *error) {
if (!error){
NSDictionary *picture = [(NSDictionary*)result objectForKey:#"picture"];
NSDictionary *data = [picture objectForKey:#"data"];
NSString *url = [data objectForKey:#"url"];
NSData *dataImage = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
}
}];
You can more info about user data here
Load profile picture with the help of this URL template
NSString *imgUrl = [NSString stringWithFormat:#"http://graph.facebook.com/%#/picture", user.id];

JSON Payload doesnt seem to be sending

My problem I'm pretty positive is simple, I must just be missing something.. just not sure what.
I can send GET and POST for granular elements (this=that kind of stuff), but a web service call I need to send data too, takes a raw JSON block, with no "key"
Heres the method I wrote:
-(NSData *)execute {
// Smart Chooser ?
if(PostData.count >0 || Payload != nil)
[self setMethod:UPLINK_METHOD_POST];
else
[self setMethod:UPLINK_METHOD_GET];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.connectionUrl
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
if([UPLINK_METHOD_GET isEqualToString:self.connectionMethod])
[request setHTTPMethod:#"GET"];
else
[request setHTTPMethod:#"POST"];
NSString *gData = [self compileGetData];
NSString *pData = [self compilePostData];
// if we have get data, set it into the URL string
if(GetData.count > 0) {
[self setURLWithString:[[self.connectionUrl absoluteString] stringByAppendingString:[#"?" stringByAppendingString:gData]]];
[request setURL:self.connectionUrl];
}
// if we have post data, set it in the body
if(PostData.count > 0) {
const char *bytes = [[NSString stringWithString:pData] UTF8String];
[request setHTTPBody:[NSData dataWithBytes:bytes length:strlen(bytes)]];
}
// Override any post data if a payload is already defined.
if(Payload != nil) {
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]];
}
NSLog(#"URL : %#", request.URL);
NSURLResponse *response;
NSError *err;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if(err != nil)
NSLog(#"here was an error: %#", err);
return responseData;
}
-(NSDictionary *)executeAsJSON
{
NSData *responseData = [self execute];
NSError *e;
return [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&e];
}
Ok SO, the way this thing works, is that it automatically sets whether the request is POST or GET depending on the data provided in the GetData, PostData, and Payload vars.
The request is GET by default, but turns into POST if PostData or Payload have anything in them.
The compileGetData and compilePostData mostly just bring back formatted strings with arrays of information combined, nothing special there.
But thats not where the problem is.
See, "Payload" overrides anything "PostData" had in it. If you had provided PostData elements into the class, it would just be overridden by a provided Payload if that does exist.
I needed to provide this to demonstrate the "workarea" as it exists right now, its not linearly provided information.
This is the area of interest:
// Override any post data if a payload is already defined.
if(Payload != nil) {
//const char *plbytes = [[NSString stringWithString:Payload] UTF8String]; // this didn't work
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]]; // inline, doesn't work either
}
When I say "doesnt work", what I mean is, im getting back an error JSON array from the webservice that basically means "hey, wheres the payload?". If the request is not POST it comes back as a general error, so thats all working, the URL is then obviously correct.
I've used RESTConsole for Chrome to test the webservice to make sure its working properly, and it does.
I've also checked through the debugger the exact payload im sending, i copy+pasted that into RESTConsole, and it works there.
I'm.. honestly at a loss here...
Try using a web proxy like Charles or Wireshark (I personally preferr Charles due to it's ease of use, it's a 30-day trial though) and monitor the request you make from RESTConsole and the one you make from your app and see if they look the same.
Check any headers, line returns and anything else that looks different.
That's the best I can think of to start with

Constantly calling a method

So I have a method that checks for internet connection, but only during the -(id):init method. Can I set it up so that it constantly checks for connection? If it helps, here is the code:
- (id) checkConnected
{
NSError *error = nil;
NSString *URLString = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com"] encoding:NSASCIIStringEncoding error:&error];
if (URLString != NULL)
{
connected = YES;
}
else connected = NO;
if(connected == YES)
NSLog(#"Connected");
else if (connected == NO)
NSLog(#"NotConnected");
return self;
}
While Reachability is a good first-pass check as others have suggested, it only tests the negative case: is it impossible to make a connection? If a firewall is blocking you, or the remote server is down, or any of a thousand other things happens, Reachability might tell you a system is in principle reachable (i.e. you have a network connection and the host if routeable) but the host is not in fact reachable.
So for some applications what you are asking is not unreasonable. The thing you have to be careful about is not to block your main thread with constant tests. Here is some code that will repeatedly run tests in the background:
NSURL *url = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
__block NSHTTPURLResponse *response = nil;
__block NSError *error = nil;
dispatch_queue_t netQueue = dispatch_queue_create("com.mycompany.netQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(netQueue, ^{
while (! [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error]) {
NSLog(#"Connection failed.");
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Connection succeeded");
});
});
dispatch_release(netQueue);
Where "Connection succeeded" is logged you could instead write some main thread code that runs when a connection is successful. Note that I am passing in *response and *error from outside the block so they too will be available on your main thread inside or outside the block (assuming you keep them in scope) for your use.
You may want to throttle (i.e. just not use while()), but this is an implementation detail. Using NSTimer() as Richard suggested would work.
Finally, even with this code you still need to handle a potential failure of a subsequent connection. Just because it worked once doesn't mean the connection is available a millisecond later.

How to tell if an iOS application has been newly installed or updated?

I have an application currently on the app store which I intend to submit an update for soon.
With this update I want to add code which will tell the app when it first runs application:didFinishLaunchingWithOptions whether it is:
A new install from the app store.
Newly updated from a previous version
There is no code in the app currently in the app store to handle this.
The application uses a SQLite database, but for reasons I won't go into here I don't want to use a check for its existence as a solution to this problem.
As a side question, without storing the data manually, is there an SDK I can use to query when an app was installed onto a device? (Preferably iOS 3.0 compatible)
I have seen a similar question, but none of the answers apply to working with existing app store code.
The following code may help to answer your side question about when an app was installed. I am unsure if the app bundle create date is the XCode build date or the download date as this is untested from app store.
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath]; // e.g. /var/mobile/Applications/<GUID>/<AppName>.app
NSFileManager *manager = [NSFileManager defaultManager];
NSDictionary* attrs = [manager attributesOfItemAtPath:bundleRoot error:nil];
NSLog(#"Build or download Date/Time of first version to be installed: %#", [attrs fileCreationDate]);
NSLog(#"Date/Time of last install (unless bundle changed by code): %#", [attrs fileModificationDate]);
NSString *rootPath = [bundleRoot substringToIndex:[bundleRoot rangeOfString:#"/" options:NSBackwardsSearch].location]; // e.g /var/mobile/Applications/<GUID>
attrs = [manager attributesOfItemAtPath:rootPath error:nil];
NSLog(#"Date/Time first installed (or first reinstalled after deletion): %#", [attrs fileCreationDate]);
You could save a version number to NSUserDefaults, and update it accordingly.
If that won't work, you may be able to release an intermediate version which introduces the versioning scheme.
If that's not an option, you may be able to check for traces of previous runs from files you create, or preferences which you set conditionally or lazily.
try this code, i know i am too late for this answer but for knwoldge sharing here i go.
-(void) checkIsAppUpdated
{
NSString *urlString = #"http://itunes.apple.com/lookup?id=995558215";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"GET"];
NSError *error = nil;
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//NSString *stringReply = (NSString *)[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (error)
{
// NSLog(#"Error: %#", stringReply);
//error reviced
}
else
{
//The response is in data
//NSLog(#"Success: %#", stringReply);
NSDictionary *dictResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
float appStoreVersion=[[[[dictResponse objectForKey:#"results"] firstObject] objectForKey:#"version"] floatValue];
NSLog(#"app stroe version=%f",appStoreVersion);
NSString *strLocalVersion=[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleShortVersionString"];
float localAppVersion=[strLocalVersion floatValue];
if (localAppVersion!=appStoreVersion)
{
//open app store url
// NSString *iTunesLink = #"itms://itunes.apple.com/us/app/apple-store/id375380948?mt=8";
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"https://itunes.apple.com/app/washthenfold/id995558215?mt=8"]];
}
}
}
note
id:-replace id with your own app id. you can get app id from itune connect selected app->more info->meta data
since simulator doesn't have app store app, this code won't work on simulator.
GBVersionTracking is good pod to track all version history.
[GBVersionTracking isFirstLaunchEver];
Here is a simple code to know if the current version is different (this code work on simulator too.)
-(BOOL) needsUpdate
{
NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString* appID = infoDictionary[#"CFBundleIdentifier"];
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"http://itunes.apple.com/lookup?bundleId=%#", appID]];
NSData* data = [NSData dataWithContentsOfURL:url];
NSDictionary* lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([lookup[#"resultCount"] integerValue] == 1)
{
NSString* appStoreVersion = lookup[#"results"][0][#"version"];
NSString* currentVersion = infoDictionary[#"CFBundleShortVersionString"];
if (![appStoreVersion isEqualToString:currentVersion])
{
NSLog(#"Need to update [%# != %#]", appStoreVersion, currentVersion);
return YES;
}
}
return NO;
}
Note: Make sure that when you enter the new version in iTunes, this matches the version in the app you are releasing. If not then the above code will always return YES regardless if the user updates.

Xcode: Validate a URL before Loading it

Im having some trouble findig a way to validate a url on my app.
My intention is to load a URL and at the same time see if other webpage exist for example.
Load http://mysite.com/folder1/1.pdf
validate http://mysite.com/folder1/2.pdf
if folder1/2.pdf exists then load it, else validate /folder2/1.pdf
so far im loading the first pdf like this in order to be able to change the pdf number and the folder:
int numpag = 1;
NSString *baseUrl =#"http://www.cronica.com.mx/iphone/pdf_iphone/";
[pdfView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[baseUrl stringByAppendingFormat:#"folder1/%d.pdf", numpag]]]];
Thanks so much in advance!
how about this:
+ (BOOL)isValidURL:(NSURL*)url
{
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSHTTPURLResponse *res = nil;
NSError *err = nil;
[NSURLConnection sendSynchronousRequest:req returningResponse:&res error:&err];
return err!=nil && [res statusCode]!=404;
}
let me know if it works for you!
(keep in mind that this is a synchronous request and should not be executed on the main thread)
I had to change the line:
return err!=nil && [res statusCode]!=404;
to
return err==nil && [res statusCode]!=404;
for the correct Bool return. The error should remain nil.
This approach is NOT correct, You should avoid Synchronous calls as they are blocking.
Apple says: simply try and wait down to wait for response.