we are working on sharing with the linkedIn objective C SDK, latest version.
Using this code:
NSString *url = #"https://api.linkedin.com/v1/people/~/shares";
NSString *payload = #"{\"visibility\":[{\"code\":\"anyone\"}],\"comment\":\"Check out developer.linkedin.com! http://linkd.in/1FC2PyG\"}";
if ([LISDKSessionManager hasValidSession]) {
[[LISDKAPIHelper sharedInstance] postRequest:url stringBody:payload success:^(LISDKAPIResponse *response) {
// do something with response
NSLog(#"Success: %#", response.description);
dispatch_async(dispatch_get_main_queue(), ^{
_responseLabel.text = response.description;
});
} error:^(LISDKAPIError *apiError) {
// do something with error
NSLog(#"Error: %#", apiError.description);
dispatch_async(dispatch_get_main_queue(), ^{
_responseLabel.text = apiError.description;
});
}];
}
lifted pretty much from their sample page. (Had to update a bit, the URL on the site was declared with initWithString which is no longer around).
We have requested and receive a valid session and requested the w_share permission as required in the updated spec.
Here is the actual error:
Error Domain=LISDKErrorAPIDomain Code=400 "(null)" UserInfo={LISDKAuthErrorAPIResponse=<LISDKAPIResponse: 0x1288cc100>}
Any hints would be appreciated!
According to Linked-in's docs and API Console, XML is the default and you need to specify that you want JSON, like this:
https://api.linkedin.com/v1/people/~/shares?format=json
And possibly with a header (not sure if the LISDKAPIHelper) knows how to do that part.
The documentation is really unclear. I suggest you capture the packets with something like CharlesProxy and see if what is getting sent is what you expect. Alternatively, use your same code, but send XML instead of JSON.
Related
I'm making a swift app in Xcode that makes use of a cocapod called HNKWordLookup (originally written in objective c). This pod uses the WordNik API to return a random word. My only issue is that a lot of the words that are returned are quite obscure.
I figured that I could go to the http://developer.wordnik.com/docs page and set parameters there, and then be given a Request URL that caters to these parameters . I assume I need to put this into my code somewhere in place of another URL that is present within the pre written pod, but I have no clue where to put the request URL. At first I put it in place in the following line of code which was located in the pod's .m file ("HNKLookup.m:):
static NSString *const kHNKLookupBaseUrl = #"http://api.wordnik.com:80/v4";
changing it to
static NSString *const kHNKLookupBaseUrl = #"http://api.wordnik.com:80/v4/words.json/randomWord?hasDictionaryDef=true&excludePartOfSpeech=definite-article&minCorpusCount=1&maxCorpusCount=-1&minDictionaryCount=30&maxDictionaryCount=-1&minLength=1&maxLength=-1&api_key=a2a73e7b926c924fad7001ca3111acd55af2ffabf50eb4ae5";
but this broke my code. Is there a certain phrase or area that I should be looking out for within the pod where I can put my new request URL in and thus run my program with my desired parameters? As you can tell I'm pretty new to programming.
You should not change kHNKLookupBaseUrl in pod. kHNKLookupBaseUrl is used to connect to the service.
Use this to get a random word:
[[HNKLookup sharedInstance] randomWordWithCompletion:^(NSString *randomWord, NSError *error) {
if (error) {
NSLog(#"ERROR: %#", error);
} else {
NSLog(#"%#", randomWord);
}
}];
You have the parameters initialised in HNKHttpSessionManager.m
+ (NSUInteger)randomWordWithCompletion:(void (^)(NSURLSessionDataTask *, id,
NSError *))completion
{
return
[self startRequestWithPath:kHNKPathRandomWord
parameters:#{
#"hasDictionaryDef" :
#(kHNKRandomWordShouldHaveDictionaryDefinition),
#"minCorpusCount" : #(kHNKRandomWordMinimumCorpusCount),
#"maxCorpusCount" : #(kHNKRandomWordMaximumCorpusCount),
#"minDictionaryCount" :
#(kHNKRandomWordMinimumDictionaryCount),
#"maxDictionaryCount" :
#(kHNKRandomWordMaximumDictionaryCount),
#"minLength" : #(kHNKRandomWordMinimumLength),
#"maxLength" : #(kHNKRandomWordMaximumLength)
}
completion:completion];
}
You can tweak this to get desired result.
I have an IOS app using azure storage blobs for jpg photos. My issue is when retrieving the blobs for display.
Most of the time they are retrieved fine. But just occasionally an odd one will not be returned. Instead 749 bytes are returned but with error still = nil.
Now that would be fine, no problem really. However EVERY time after that when I try to retrieve that blob again then the same issue occurs.
All the surrounding blobs are returned fine. The blob in question is fine and can be retrieved using another device.
I have spent lots of time clearing all variables involved and recalling the blob in question and no matter what only ever 749 bytes are returned. Deleting the app from the device and reinstalling it is the only workaround!
So I presume Azure storage or mobile services think the returned data was ok (since it had no error) and keeps sending the same - how can I prevent that and demand a true retry?
The actual retrieving code below was courtesy of github: thank you Ajayi13 it is almost awesome
[request fetchDataWithBlock:^(NSData* data, NSError* error)
{
if(error)
{
if(block)
{
block(nil, error);
}
else if([(NSObject*)_delegate respondsToSelector:#selector(storageClient:didFailRequest:withError:)])
{
[_delegate storageClient:self didFailRequest:request withError:error];
}
return;
}
if(block)
{
block(data, nil);
}
else if([(NSObject*)_delegate respondsToSelector:#selector(storageClient:didGetBlobData:blob:)])
{
[_delegate storageClient:self didGetBlobData:data blob:blob];
}
}];
I have now added the following code based on AdamSorrin's response and a blog post BY DANIEL PASCO: https://blackpixel.com/writing/2012/05/caching-and-nsurlconnection.html
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)[cachedResponse response];
// Look up the cache policy used in our request
if([connection currentRequest].cachePolicy == NSURLRequestUseProtocolCachePolicy) {
NSDictionary *headers = [httpResponse allHeaderFields];
NSString *cacheControl = [headers valueForKey:#"Cache-Control"];
NSString *expires = [headers valueForKey:#"Expires"];
if((cacheControl == nil) && (expires == nil)) {
NSLog(#"server does not provide expiration information and we are using NSURLRequestUseProtocolCachePolicy");
return nil; // don't cache this
}
}
return nil;
}
BUT this has not fixed my issue :o(
I'm not sure exactly what networking library you're using (your request object), so I'm going to assume that it's based off of NSURLSession and NSURLRequest. If not, the details here will be wrong, but the underlying reason still might be correct.
I would guess that your problem is two-fold.
For NSURLSession downloadTaskWithRequest:completionHandler:, the NSError parameter passed into the completion handler block is set only for client-side errors. Retryable service-side errors (throtting, server busy, etc.) aren't detected as such on the client - you need to look at the HTTP response code/message and handle appropriately.
Look at the NSURLSessionConfiguration documentation, specifically requestCachePolicy. My guess is that you're getting stale data from the cache when you try and re-fetch the contents of the blob. You can use this parameter to force the request to re-fetch the data from the service, if this is indeed causing the problem.
I am trying to fetch youtube channel id using the google-api-objectivec-client. The problem I am having is basically that for some reason I am receiving exception when trying to access the channelId. The code I am using:
GTLServiceYouTube *service = [[GTLServiceYouTube alloc] init];
service.APIKey = _MY_API_KEY_;
GTLQueryYouTube *query = [GTLQueryYouTube queryForSearchListWithPart:#"id"];
query.q = #"google";
query.type = #"channel";
query.maxResults = 1;
GTLServiceTicket *ticket = [service executeQuery:query completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (error == nil) {
GTLYouTubeSearchListResponse *products = object;
for (id item in products.items) {
GTLYouTubeSearchResult *result = item;
NSLog(#"Identifier:%#",result.identifier);
GTLYouTubeResourceId* resourceId = result.identifier;
NSLog(#"kind:%#",resourceId.kind);
NSLog(#"channel:%#",resourceId.channelId);
}
}else{
NSLog(#"Error: %#", error.description);
}
}];
The output I get when i am running this code is:
2013-04-05 11:37:12.615 YouTest[21704:11303] Identifier:GTLYouTubeChannel 0x7233b00: {kind:"youtube#channel" channelId?:"UCK8sQmJBp8GCxrOtXWBpyEA"}
2013-04-05 11:37:12.617 YouTest[21704:11303] kind:youtube#channel
2013-04-05 11:37:12.617 YouTest[21704:11303] -[GTLYouTubeChannel channelId]: unrecognized selector sent to instance 0x7233b00
2013-04-05 11:37:12.618 YouTest[21704:11303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[GTLYouTubeChannel channelId]: unrecognized selector sent to instance 0x7233b00'
So my implementation crashes on the point where I am trying to access the channelId of the resourceId. From the documentation I understood that the channelId should be there as the type of the resourceId is youtube#channel. The channelId can be off course parsed from the result.identifier string that I am also printing, but since there is a property for the channelId I would prefer using that.
Any ideas about what is wrong with my code?
There is indeed a bug in the Google libraries. However I solved this problem by accessing the JSON string directly and parsing it with the help of the NSString+SBJSON.h class, as in this example.
#import "NSString+SBJSON.h"
...
GTLYouTubeResourceId *resource = channel.snippet.resourceId;
NSDictionary *jsonObject = [resource.JSONString JSONValue];
NSString *channelid = [jsonObject valueForKey:#"channelId"];
I'm not very familiar with Objective-C, but yeah, that looks like there's something wrong with the generated client library's YouTube Data API v3 bindings. Are you using the latest version from the project page? You might want to file a bug against the client library if you can reproduce it with the latest version. While troubleshooting this further, I'd check to see if you have the same problem when query.type = #"video"; and you try to access the videoId of the response item.
Here's an alternative you could try, though. The channel's id is also returned in the snippet.channelId property. If you request the snippet part via GTLQueryYouTube *query = [GTLQueryYouTube queryForSearchListWithPart:#"snippet"]; see if you can read that value instead.
I had the same issue. Solved it with the following...
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:[resourceId.JSONString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
NSString *channelId = [jsonObject valueForKey:#"channelId"];
NSLog(#"channelId is %#", channelId);
Workaround Code:
channel.snippet.resourceId.JSON[#"channelId"];
No need to parse the JSON yourself as the underlying JSON is exposed.
It looks like the automatic binding is not working for GTLYouTubeResourceId because the "kind" element of "youtube#channel" is throwing off the runtime object creation and creating a GTLYouTubeChannel instead.
Thorough Workaround Code:
ticket.surrogates = #{ (id)[GTLYouTubeChannel class] : [GTLYouTubeResourceId class] };
If you really want to force that binding to work you can workaround a little further upstream on the ticket when you execute the query.
Global Workaround Patch:
https://github.com/google/google-api-objectivec-client/pull/109
There's open tickets for the issue:
https://github.com/google/google-api-objectivec-client/issues/63
https://github.com/google/google-api-objectivec-client/issues/92
It seems they want to change the API to not call the resourceId.kind 'kind' to avoid this problem. But while we wait for the API to change, any of these three workarounds should serve your purposes.
Using iOS 5's new TWRequest API, I've ran into a brick wall related with block usage.
What I need to do is upon receiving a successful response to a first request, immediately fire another one. On the completion block of the second request, I then notify success or failure of the multi-step operation.
Here's roughly what I'm doing:
- (void)doRequests
{
TWRequest* firstRequest = [self createFirstRequest];
[firstRequest performRequestWithHandler:^(NSData* responseData,
NSHTTPURLResponse* response,
NSError* error) {
// Error handling hidden for the sake of brevity...
TWRequest* secondRequest = [self createSecondRequest];
[secondRequest performRequestWithHandler:^(NSData* a,
NSHTTPURLResponse* b,
NSError* c) {
// Notify of success or failure - never reaches this far
}];
}];
}
I am not retaining either of the requests or keeping a reference to them anywhere; it's just fire-and-forget.
However, when I run the app, it crashes with EXC_BAD_ACCESS on:
[secondRequest performRequestWithHandler:...];
It executes the first request just fine, but when I try to launch a second one with a handler, it crashes. What's wrong with that code?
The methods to create the requests are as simple as:
- (TWRequest*)createFirstRequest
{
NSString* target = #"https://api.twitter.com/1/statuses/home_timeline.json";
NSURL* url = [NSURL URLWithString:target];
TWRequest* request = [[TWRequest alloc]
initWithURL:url parameters:params
requestMethod:TWRequestMethodGET];
// _twitterAccount is the backing ivar for property 'twitterAccount',
// a strong & nonatomic property of type ACAccount*
request.account = _twitterAccount;
return request;
}
Make sure you're keeping a reference/retaining the ACAccountStore that owns the ACAccount you are using to sign the TWRequests.
If you don't, the ACAccount will become invalid and then you'll get EXC_BAD_ACCESS when trying to fire a TWRequest signed with it.
I'm not familiar with TW*, so consider this a wild guess ... try sending a heap-allocated block:
[firstRequest performRequestWithHandler:[^ (NSData *responseData, ...) {
...
} copy]];
To clarify, I think the block you're sending is heap-allocated, so while TW* might be retaining it, it won't make any difference if it has already gone out of scope.
Like it's written in the title, How can I download data from a server in my Cocoa Application?
So far I looked for, I found this.
If you're not downloading a lot of things in parallel and you're doing a simple GET request, the easiest way to do it is to dispatch a synchronous request to one of the global queues:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com/"]];
NSURLResponse* response = nil;
NSError* error = nil;
NSData* result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// There will be response data in response now, like the http status code
// etc. You should check this information to make sure you didn't get a 404
// or some other http status error
if( result ) {
// you have a good result, do something with it like create a new object or
// pass it to a method on this object, etc.
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomethingWithResponseData:result];
});
} else {
// You got an error making the connection, so handle it
NSLog(#"Error making connection: %#", error);
}
});
**note: this sample code uses GCD and therefore will only run on Snow Leopard (10.6) or better. If you need to target Leopard or Tiger, you can do the same thing using dispatched thread selectors, but not as in-line.
ASIHTTPRequest works for iPhone and Mac