SoundCloud API and 401 Error for registered application - objective-c

I have a problem with SoundCloud API in my application.
I have created an app, what get link to the track (e.g. something like this http://api.soundcloud.com/tracks/48760960) and get stream_url, what later can be played using SCAudioStream. There is no auth for users, I use only client id and client secret of my app. App is registered.
I create soundCloudManager this way:
- (id)init
{
if(self = [super init])
{
conf = [SCSoundCloudAPIConfiguration configurationForProductionWithClientID: kSCClientID
clientSecret: kSCClientSecret
redirectURL: kSCRedirectURL];
conf.accessTokenURL = kSCAccessTokenURL;
api = [[SCSoundCloudAPI alloc] initWithDelegate: self
authenticationDelegate: self
apiConfiguration: conf];
//[api checkAuthentication];
}
return self;
}
If uncomment [api checkAuthentication], then logs will says next:
"auth ready with URL:https://soundcloud.com/connect?client_id=&redirect_uri=&response_type=code"
Then I call this method to get data from track:
- (void)runSearchingStreamWithTrackID: (NSString *)trackID
{
[api performMethod: #"GET"
onResource: #"tracks"
withParameters: [NSDictionary dictionaryWithObject:trackID forKey:#"ids"]
context: #"trackid"
userInfo: nil];
}
And then this delegate's method calls:
- (void)soundCloudAPI:(SCSoundCloudAPI *)soundCloudAPI
didFailWithError:(NSError *)error
context:(id)context
userInfo:(id)userInfo
Text of error:
"HTTP Error: 401".
It means unauthorized. But why? Should I be authorized as soundCloud user to getting info about tracks?
I'm using this for SoundCloudAPI:
https://github.com/soundcloud/cocoa-api-wrapper
Please, help! :(
Today I fixed it. I just add this method to init :
[api authenticateWithUsername:kLogin password:kPwd];
After it this method was called:
- (void)soundCloudAPIDidAuthenticate
So, this test account was authorized.
Then I call this method:
- (void)runSearchingStreamWithTrackID: (NSString *)trackID
{
[api performMethod: #"GET"
onResource: #"tracks"
withParameters: [NSDictionary dictionaryWithObject:trackID forKey:#"ids"]
context: #"trackid"
userInfo: nil];
}
And no one of these methods will be called:
- (void)soundCloudAPI:(SCSoundCloudAPI *)soundCloudAPI
didFailWithError:(NSError *)error
context:(id)context
userInfo:(id)userInfo;
- (void)soundCloudAPI:(SCSoundCloudAPI *)soundCloudAPI
didFinishWithData:(NSData *)data
context:(id)context
userInfo:(id)userInfo;
- (void)soundCloudAPIDidAuthenticate;
- (void)soundCloudAPIDidResetAuthentication;
- (void)soundCloudAPIDidFailToGetAccessTokenWithError:(NSError *)error;
- (void)soundCloudAPIPreparedAuthorizationURL:(NSURL *)authorizationURL;
But there is log:
-[NXOAuth2PostBodyStream open] Stream has been reopened after close
And this method was called:
[NXOAuth2Client oauthConnection:connection didFailWithError:error];
error: HTTP 401
What do I wrong?

I understood the problem. Just download the SC API from the official site and install the latest SC API. Then do something like this:
- (void)searchStreamURLByTrackID: (NSString *)trackID
{
NSString *string = [NSString stringWithFormat:#"https://api.soundcloud.com/tracks/%#.json?client_id=%#", trackID, kSCClientID];
[SCRequest performMethod: SCRequestMethodGET
onResource: [NSURL URLWithString:string]
usingParameters: nil
withAccount: nil
sendingProgressHandler: nil
responseHandler:^(NSURLResponse *response, NSData *data, NSError *error){
NSString *resultString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"FOUND:\n%#", resultString);
}
];
}
After that obtain the stream url from the JSON in the resultString.
Finally, you have to write this code:
NSString *streamURL = [(NSDictionary *)jsonData objectForKey:#"stream_url"];
NSString *realURL = [streamURL stringByAppendingFormat:#".json?client_id=%#", kSCClientID];
AVPlayer *player = [[AVPlayer alloc] initWithURL:[NSURL URLWithString:realURL]];
Enjoy!

Did you check https://github.com/soundcloud/CocoaSoundCloudAPI ?
This is the more current version of the API wrapper (if you don't need iOS 3.0 support).
It also comes with a tutorial video: http://vimeo.com/28715664
If you want to stay with this library you should try to figure out where the log message ("auth ready with ...") appears. Did you implement any of the AuthorizationDelegate methods?
See https://github.com/soundcloud/cocoa-api-wrapper/blob/master/Usage.md#the-authentication-delegate-the-easy-way

Related

IOS App Action extension is not closing

I am facing app extension close issues , please tell me if anyone know what wrong am doing.I am using action extension after preform some action inside extension i need to return response back.
Sample Code
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
}
// With Error Case
- (void) completeActionWithError: (NSError *) error {
[self.extensionContext cancelRequestWithError: error];
}
With Success Case working fine but some time is not closing,
With Error Case not working above code.
Please let me know what went wrong.Thanks
When you create an action extension, this is the default method which will close the Action Extension View Controller:
- (IBAction)done {
// Return any edited content to the host app.
// This template doesn't do anything, so we just echo the passed in items.
[self.extensionContext completeRequestReturningItems:self.extensionContext.inputItems completionHandler:nil];
}
Since this method is already provided, you should just try calling it from your success method.
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
// Call to "done" method
[self done];
}

ConnectionKit and SFTP: How to authenticate CK2FileManager

For my Mac OS X Cocoa app, I am trying to
connect to a SFTP server that only accepts username/password credentials
get the contents of a remote directory
upload files
and find it surprisingly complicated.
After trying ConnectionKit (nearly no documentation), NMSSH (crashed once too often with simultaneous uploads), rsync (not supported by the server), sftp (needs key authentication if scripted, doesn't work with username/password), I am now back to ConnectionKit: https://github.com/karelia/ConnectionKit
However, I am struggling with the authentication challenge, as I don’t know what to do with my credential in the delegate method.
I downloaded and compiled ConnectionKit (apparently version 2).
I am trying to use CK2FileManager as the Readme indicates (is this the right approach at all? Or should I use the libssh2_sftp-Cocoa-wrapper instead?… however I had troubles with libssh2 blocking methods in NMSSH before)
I am successfully setting up my connection URL and
my delegates' -didReceiveAuthenticationChallenge is called
But this is where I struggle: I know how to create a NSURLCredential, however, I can’t figure out what to do with it =>
- (void)fileManager:(CK2FileManager *)manager
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSURLCredential *credentials = [NSURLCredential
credentialWithUser:self.username
password:[self getPassword]
persistence:NSURLCredentialPersistenceForSession];
// what to do now?
// [manager useCredential:] doesn’t exist, nor is there a manager.connection?
// ...
}
I already read the header, I searched the archives of this list, but all answers seem to be outdated.
I also searched Google, Bing and StackOverflow and found one promising example from 2011 using CKFTPConnection, which doesn’t seem to be included in the current framework anymore.
Thanks so much for any pointer to the right direction.
tl;dr
I don't know how to respond to ConnectionKit's CK2FileManager authenticationChallenge:
see the comment in the code example
For CK2:
- (void)listDirectoryAtPath:(NSString *)path
{
// path is here #"download"
NSURL *ftpServer = [NSURL URLWithString:#"sftp://companyname.topLevelDomain"];
NSURL *directory = [CK2FileManager URLWithPath:path isDirectory:YES hostURL:ftpServer];
CK2FileManager *fileManager = [[CK2FileManager alloc] init];
fileManager.delegate = self;
[fileManager contentsOfDirectoryAtURL:directory
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsHiddenFiles
completionHandler:^(NSArray *contents, NSError *error) {
if (!error) {
NSLog(#"%#", contents);
} else {
NSLog(#"ERROR: %#", error.localizedDescription);
}
}];
}
and than you have to implement the following protocol
- (void)fileManager:(CK2FileManager *)manager operation:(CK2FileOperation *)operation
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(CK2AuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
if (![challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodDefault]) {
completionHandler(CK2AuthChallengePerformDefaultHandling, nil);
return;
}
NSString * username = #"<username>";
NSString * pathToPrivateSSHKey = #"</Users/myNameOnLocalMaschine/.ssh/id_rsa>"
NSURLCredential *cred = [NSURLCredential ck2_credentialWithUser:username
publicKeyURL:nil
privateKeyURL:[NSURL fileURLWithPath:pathToPrivateSSHKey]
password:nil
persistence:NSURLCredentialPersistenceForSession];
completionHandler(CK2AuthChallengeUseCredential, cred);
}
That's it.
Call -listDirectoryAtPath: and then you will get in the Completion Handler Block in contents array all the files located on the given path :)
Okay, that was easy and I could have found out that on my own; just for the reference: [[challenge sender] useCredential:credentials forAuthenticationChallenge:challenge];
Sorry to reward myself for my own question, but maybe this code snippet helps filling the missing docs, this is how I connect to my SFTP server with ConnectionKit:
- (void)connectWithCompletionBlock:(void (^)(void))completionBlock {
if(!self.cFileManager) {
self.cFileManager = [[CK2FileManager alloc] init];
self.cFileManager.delegate = self;
}
NSURL *sftpServer = [NSURL URLWithString:[#"sftp://" stringByAppendingString:self.server]];
self.remoteFolder = [CK2FileManager URLWithPath:self.remotePath relativeToURL:sftpServer];
// try to get the contents of the current directory
[self.cFileManager contentsOfDirectoryAtURL:self.remoteFolder
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsHiddenFiles
completionHandler:^(NSArray *contents, NSError *error)
{
NSLog(#"remote folder contents: \n%#", contents);
// invoke completion block
completionBlock();
}];
}
- (void)fileManager:(CK2FileManager *)manager
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSURLCredential *credentials = [NSURLCredential
credentialWithUser:self.username
password:[self getPassword]
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:credentials forAuthenticationChallenge:challenge]
}

Show error messages in UIAlertView by UIAlerView+AFNetworking

There is a categroy UIAlertView+AFNetworking.h ship with AFNetworking 2.0. And I use it to show error messages in my App like this:
[UIAlertView showAlertViewForTaskWithErrorOnCompletion:task delegate:nil]
But I want to add some other messages in the message of UIAlertView.
i.e.:
StatusCode: 422
JSON Data: {"errors":{"detail":"Wrong password"}}
How to get "Wrong password" and show in a UIAlertView by the functions in UIAlertView+AFNetworking?
UIAlertView+AFNetworking category uses error messages from NSError – localizedDescription, localizedRecoverySuggestion and localizedFailureReason. AFNetworking generates NSError on failure, however, you must write your own responseSerializer to set your custom error messages there.
This topic has been thoroughly discussed on GitHub and I use solution based #camdez's code snippet.
My error response messages from API looks like this:
{ error: { message: "This username already exists" } }
Now, I've subclassed AFJSONResponseSerializer and when an error occurs, I save the error message to the NSError instance.
CustomResponseSerializer.h
#interface CustomResponseSerializer : AFJSONResponseSerializer
#end
CustomResponseSerializer.m
#implementation CustomResponseSerializer
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
id JSONObject = [super responseObjectForResponse:response data:data error:error]; // may mutate `error`
if (*error && [JSONObject objectForKey:#"error"]) {
NSMutableDictionary *mutableUserInfo = [(*error).userInfo mutableCopy];
mutableUserInfo[NSLocalizedFailureReasonErrorKey] = [[JSONObject objectForKey:#"error"] objectForKey:#"message"];
NSError *newError = [NSError errorWithDomain:(*error).domain code:(*error).code userInfo:[mutableUserInfo copy]];
(*error) = newError;
}
return JSONObject;
}
#end
Finally, you need to set appropriate responseSerializer on AFHTTPSessionManager (or AFHTTPRequestOperationManager if you use NSURLConnection API) instance. If you subclass this class as I do, you can simply do it in your init method by following:
self.responseSerializer = [CustomResponseSerializer serializer];

How to implement the string SCAudioStream? - Sound Cloud

I'm working with the sound cloud sdk, I'm trying to use their libraries to play Audio Stream.
I have not been able to find any reference on how to use that class.
I would appreciate any help you could provide.
If you are talking about this libraries I can help you.
First of all you should run this method to get some info about track by its ID:
[api performMethod: #"GET"
onResource: #"tracks"
withParameters: [NSDictionary dictionaryWithObject:trackID forKey:#"ids"]
context: #"trackid"
userInfo: nil];
If you don't know trackID and have only link to track like this : http://soundcloud.com/go-yoshimura/mikasasukasa, then run this method:
[api performMethod: #"GET"
onResource: #"resolve"
withParameters: [NSDictionary dictionaryWithObject:songLink forKey:#"url"]
context: #"songname"
userInfo: nil];
Then you should implement delegate's methods:
- (void)soundCloudAPI:(SCSoundCloudAPI *)soundCloudAPI
didFinishWithData:(NSData *)data
context:(id)context
userInfo:(id)userInfo;
- (void)soundCloudAPI:(SCSoundCloudAPI *)soundCloudAPI
didFailWithError:(NSError *)error
context:(id)context
userInfo:(id)userInfo;
In first method you should parse data using JSON Parser (JSONKit or SBJSON). You should get NSDictionary.
streamURL = [(NSDictionary *)jsonData objectForKey:#"stream_url"];
streamable = [[(NSDictionary *)jsonData objectForKey:#"streamable"] boolValue];
If this track is streamable, you can get SCAudioStream this way:
stream = [[SCAudioStream alloc] initWithURL:[NSURL URLWithString:streamURL] authentication:auth];
Auth you can create earlier in your init method this way:
conf = [SCSoundCloudAPIConfiguration configurationForProductionWithClientID: kSCClientID
clientSecret: kSCClientSecret
redirectURL: kSCRedirectURL];
auth = [[SCSoundCloudAPIAuthentication alloc] initWithAuthenticationDelegate:self apiConfiguration:conf];
And now you have SCAudioStream.
//Playing
[stream play];
//Pause
[srteam pause];
//Seeking
[stream seekToMillisecond:ms startPlaying:YES];
That's all :)

Twitter SDK for IPad, weired error on twitter login

when I try to share on twitter I do the following steps
if (!twitterEngine)
{
twitterEngine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate: self];
twitterEngine.consumerKey = kOAuthConsumerKey;
twitterEngine.consumerSecret = kOAuthConsumerSecret;
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine: twitterEngine delegate: self];
if (controller)
[currentController presentModalViewController: controller animated: YES];
else
[self postTwitterStatus];
}
else {
[self postTwitterStatus];
}
after I got the twitter login view and after I put my pass and user and click add app
I get crash on asembly code, which I don't have an error message, but I saw something weiered also that in the delegates
- (void) OAuthTwitterController: (SA_OAuthTwitterController *) controller authenticatedWithUsername: (NSString *) username {
NSLog(#"Authenicated for %#", username);
[self postTwitterStatus];
}
I got that authintication succeeded but I get that the username == null
how can I solve this issue
Thanks
I did NOT get THIS error, but a similar one using Twitter+OAuth by Ben Gottlieb. If you are using this thing, it is required to use https and not http to connect to twitter.
go to: SA_OAuthTwitterEngine.m and change to:
- (SA_OAuthTwitterEngine *) initOAuthWithDelegate: (NSObject *) delegate {
if (self = (id) [super initWithDelegate: delegate]) {
//CHANGED FROM 'HTTP' TO 'HTTPS':
self.requestTokenURL = [NSURL URLWithString: #"https://twitter.com/oauth/request_token"];
self.accessTokenURL = [NSURL URLWithString: #"https://twitter.com/oauth/access_token"];
self.authorizeURL = [NSURL URLWithString: #"https://twitter.com/oauth/authorize"];
}
return self;
}
give it a try, i'm not sure it's the same as your problem..
Every thing looks fine to me. check with this example and see weather you are getting username in this.