No shared download link from boxItem in BoxFolderPickerViewController - ios

How can I get the download_url or shared link from the item returned below? The file id and name, size and date modified are available, but my requirement is to return a download_url or shared link.
- (void)folderPickerController:(BoxFolderPickerViewController *)controller didSelectBoxItem:(BoxItem *)item
{
[self dismissViewControllerAnimated:YES completion:^{
// NSLog("%#",item);
// NSLog(#"%#", item.rawResponseJSON);
if ([BoxSDK sharedSDK].OAuth2Session.isAuthorized){
NSLog(#"authorized");
BoxAPIJSONFailureBlock failure = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSDictionary *JSONDictionary){
[self boxError:error];
};
NSDictionary *const queryParametersDictionary = #{#"fields" : #"item_collection"};
BoxFilesRequestBuilder *builder = [[BoxFilesRequestBuilder alloc] initWithQueryStringParameters:queryParametersDictionary];
BoxAPIJSONOperation *operation= [[BoxSDK sharedSDK].filesManager fileInfoWithID:item.modelID
requestBuilder:builder
success:nil
failure:failure];
}else{NSLog(#"fail");}
}];

Not sure why you'd need download_url. Do you want to expose it to some other app or user?
If you don't see shared_link in your item rawResponseJSON (debugger: po [file rawResponseJSON][#"shared_link"]), then it is highly likely that file is not shared. You need to share it first.
Here is how you do it, given you already have BoxItem
BoxFileBlock success = ^(BoxFile *file) {
dispatch_async(dispatch_get_main_queue(), ^{
self.item = file;
NSDictionary *sharedLinkInfo = [self.item sharedLink];
// here is your urlString of the sharedLink
NSString *urlString = [sharedLinkInfo objectForKey:#"url"];
});
};
BoxFilesRequestBuilder *builder = [[BoxFilesRequestBuilder alloc] init];
builder.sharedLink = [[BoxSharedObjectBuilder alloc] init];
builder.sharedLink.access = BoxAPISharedObjectAccessOpen;
builder.sharedLink.canDownload = BoxAPISharedObjectPermissionStateEnabled;
builder.sharedLink.canPreview = BoxAPISharedObjectPermissionStateEnabled;
[self.sdk.filesManager editFileWithID:self.item.modelID
requestBuilder:builder
success:success
failure:failure];
Ideally you also want to provide failure block (omitted here)
Also make sure that you're using latest SDK. It had a small bug fix for sharedObjectFileBuilder that went in yesterday.

Related

ios - Program with block executed out of order?

I'm trying to get an array of urls from my backend.
I use AFNetworking and I have a HTTPUtil class implemented as singleton to handle my requests.
HTTPUtil.m
#import "HTTPUtil.h"
#implementation HTTPUtil
+(instancetype)sharedInstance{
NSLog(#"sharedInstance"); //to check the order
static HTTPUtil* manager;
static dispatch_once_t once;
dispatch_once(&once, ^{
manager = [[HTTPUtil alloc] init];
});
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
return manager;
}
-(void)getImageArrayFromURL:(NSString *)url success:(void(^)(NSArray* array))success failure:(void(^)(NSError* error))failure{
NSLog(#"getting..."); //to check the order
[self GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask* task, id response){
NSLog(#"Response: %#", response);
NSString* imgStr = [[response objectForKey:kResponseDataKey] objectForKey:#"img"];
//convert nsstring to nsarray
NSArray* array = [StringUtil arrayFromString:imgStr];
//construct urls
NSMutableArray* ret = [[NSMutableArray alloc] init];
NSMutableString* url;
for (NSString* rawStr in array) {
url = [NSMutableString stringWithString:kUrlBase];
[url appendString:[rawStr stringByReplacingOccurrencesOfString:#"/" withString:#"+"]];
[ret addObject:url];
}
success(ret);
}failure:^(NSURLSessionDataTask* task, NSError* error){
NSLog(#"Error: %#", error);
failure(error);
}];
}
In my view controller, I call the method to fetch the array.
_vCycleScrollView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 0, 0, 0) delegate:self placeholderImage:[UIImage imageNamed:#"checked"]];
NSMutableString* url = [NSMutableString stringWithString:kUrlBase];
[url appendString:#"activityImgArray"];
//
__block NSArray* imgarr;
[[HTTPUtil sharedInstance] getImageArrayFromURL:url success:^(NSArray* array){
imgarr = [NSArray arrayWithArray:array];
}failure:^(NSError* error){
NSLog(#"%#", error);
}];
NSLog(#"adding...");
_vCycleScrollView.imageURLStringsGroup = imgarr;
[self.view addSubview:_vCycleScrollView];
[_vCycleScrollView mas_makeConstraints:^(MASConstraintMaker* make){
make.top.equalTo(self.view);
make.left.equalTo(self.view);
make.right.equalTo(self.view);
make.height.mas_equalTo(180);
make.width.equalTo(self.view.mas_width);
}];
In the console, I got
2016-05-20 14:41:19.411 SCUxCHG[10470:4909076] sharedInstance
2016-05-20 14:41:19.415 SCUxCHG[10470:4909076] getting...
2016-05-20 14:41:19.417 SCUxCHG[10470:4909076] adding...
2016-05-20 14:41:19.591 SCUxCHG[10470:4909076]
Response: {
data = {
img = "[activity/test1, acti/1]";
};
message = success;
result = 0;
}
I thought imgArr should be assigned in the success block and it shouldn't be nil when I assign it to _vCycleScrollView.imageURLStringsGroup.
However, I can tell from the output in the console that the HTTP request is sent after NSLog(#"adding..."); and that leads to the fact that imgArr is still nil when _vCycleScrollView.imageURLStringsGroup = imgarr; is executed.
Why is that?
Yes below code is in block so this will continue in background
[[HTTPUtil sharedInstance] getImageArrayFromURL:url success:^(NSArray* array){
imgarr = [NSArray arrayWithArray:array];
}failure:^(NSError* error){
NSLog(#"%#", error);
}];
solution - You should add _vCycleScrollView.imageURLStringsGroup = imgarr; inside of success block because you d0 not know when it will completed Or there is another way you should not call in block or should not create block.
Try bellow:
__block NSArray* imgarr;
[[HTTPUtil sharedInstance] getImageArrayFromURL:url success:^(NSArray* array){
imgarr = [NSArray arrayWithArray:array];
NSLog(#"adding...");
_vCycleScrollView.imageURLStringsGroup = imgarr;
}failure:^(NSError* error){
NSLog(#"%#", error);
}];
The completion block is executed once data is fetched.
In your case code continues to execute after the completion block is set but data hasn't been fetched yet, that's why imgarr is nil.
That's the whole idea: That blocks are executed out of order. The trick is that you don't wait for a block to finish. Instead, the block finishes and then it does what is needed. The code in your viewcontroller isn't going to work, can't work, and we don't want it to work. Instead, the callback block deposits the image somewhere, and then tells the tableview to reload the row.

SPTTrack trackWithURI issue

I had created the new project which fetches the track of songs, so I tried to pass an array of SPTTracks to the player, please find it below.
self.player = [[SPTAudioStreamingController alloc] initWithClientId:auth.clientID];
self.player.diskCache = [[SPTDiskCache alloc] initWithCapacity:1024 * 1024 * 64];
NSString *trackURI = #"spotify:track:1zHlj4dQ8ZAtrayhuDDmkY";
[SPTTrack trackWithURI:[NSURL URLWithString:trackURI] accessToken:auth.session.accessToken market:#"ES" callback:^(NSError *error, id object) {
if (!error) {
SPTTrack *trackInfo = object;
NSArray *tracks = #[trackInfo];
[self.player playURIs:tracks fromIndex:0 callback:^(NSError *error) {
if (!error) {
} else {
NSLog(#"*** Failed to play track : %#", error);
}
}];
} else {
NSLog(#"Error %#", error);
}
}];
But I get crashes, whenever I run it. Please find error below while it is getting crash :
Simple Track Playback[254:24669] -[__NSCFConstantString absoluteString]: unrecognized selector sent to instance 0x1000c0508
I had also looked it on spotify api spotify_ios_sdk but I had found that one developer had already posted the same issue link.
If anyone has solved these type of issue then please provide your guidance.
Thanks in advanced.
Unfortunately, this method is not "equivalent", because the SPTTrack object returned inside the callback of [SPTTrack trackWithURI.... has many less informations.
I've tried some workaround, and I found that for me the solution is
1) Create a request for an SPTTrack object.
2) Pass that request to SPRequest performRequest callback.
3) Wait for the response, and eventually create a track from data (please find the below complete code).
self.player = [[SPTAudioStreamingController alloc] initWithClientId:auth.clientID];
self.player.diskCache = [[SPTDiskCache alloc] initWithCapacity:1024 * 1024 * 64];
NSString *market = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
NSURLRequest *request = [SPTTrack createRequestForTrack:[NSURL URLWithString:#"spotify:track:1zHlj4dQ8ZAtrayhuDDmkY"]
withAccessToken:auth.session.accessToken
market:market
error:nil];
[[SPTRequest sharedHandler] performRequest:request
callback:^(NSError *error, NSURLResponse *response, NSData *data) {
if (!error) {
NSError *parsingError = nil;
SPTTrack *track = [SPTTrack trackFromData:data
withResponse:response
error:&parsingError];
self.arrURIs = #[track.playableUri];
[self.player playURIs:self.arrURIs fromIndex:0 callback:nil];
}
}];
Thanks "Kry256" for detail explaination.

How to modify a non-local (global) variable from inside of a block?

I am quite new to Objective-C & have to dynamically change the value of #property (strong, nonatomic) NSMutableArray *allCategories from inside of AFHTTPRequestOperationManager in success block.
[self.allCategories addObject:tempObject]; doesn't change the value of allCategories while iterating in a loop.
The variable has been initialized as self.allCategories = [[NSMutableArray alloc]init]; in viewDidLoad.
I have also tried creating a temporary variable as __block NSMutableArray *tempCategories = [[NSMutableArray alloc]init]; before initiating AFHTTPRequestOperationManager object. tempCategories doesn't even retain its value.Can't figure out what's happening.EditSorry for inconvenienceviewDidLoad has the following code self.allCategories = [[NSMutableArray alloc]init];[self loadData];Here's the code
-(NSMutableArray *)loadData
{
__block NSMutableArray *tempCategories = [[NSMutableArray alloc]init];
manager = [AFHTTPRequestOperationManager manager];
[manager GET:kAPICategoryList
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// downcast id to NSMutableDictionary
NSMutableDictionary *json = (NSMutableDictionary *)responseObject;
// check if dictionary is non nil has at least 1 element
if (json != nil && [json count] >= 1) {
// NSLog(#"json:\t%#", json);
// check json is non nil & has success message
if ([json objectForKey:kAPIKeyCategoryRoot] != nil) {
NSArray *arrCategoriesRoot = [json objectForKey:kAPIKeyCategoryRoot];
// check categories has some data
if (arrCategoriesRoot.count >= 1) {
for (int i = 0; i < arrCategoriesRoot.count; i++) {
SomeModel *pCategory;
NSDictionary *dctCategorySingle = [arrCategoriesRoot objectAtIndex:i];
// check category has sub category
if ([dctCategorySingle objectForKey:kAPIKeyCategorySubCategory] != nil) {
// create category with sub category
pCategory = [[SomeModel alloc]initWithSubCategorisedCategoryID:[dctCategorySingle objectForKey:kAPIKeyCategoryID]
name:[dctCategorySingle objectForKey:kAPIKeyCategoryName]
image:kIMGCategoryDefault
subCategory:[dctCategorySingle objectForKey:kAPIKeyCategorySubCategory]];
} else{
// create just a category
pCategory = [[SomeModel alloc]initWithCategoryID:[dctCategorySingle objectForKey:kAPIKeyCategoryID]
name:[dctCategorySingle objectForKey:kAPIKeyCategoryName]
image:kIMGCategoryDefault];
} // else just
[tempCategories addObject:pCategory];
[_allCategories addObject:pCategory];
} // for
NSLog(#"categories count %lu", [self.allCategories count]);
} // if count >= 1
}
else if ([json objectForKey:kAPIRespMsgCategoryFetchErrKey] != nil) {
[Utility showAlertWithTitle:kAPIRespMsgCategoryFetchErrKey
message:[json objectForKey:kAPIRespMsgCategoryFetchErrVal]
button:kMsgButtonOkayTtl];
}
} else {
// error in login => enable login
NSLog(#"%#", kMsgNetworkEmptyJSON);
}
}
// network error
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error %#", [error localizedDescription]);
}];
NSLog(#"tempCategories count %lu", [tempCategories count]);
return tempCategories;
}
Here's the output form NSLog:2015-03-19 18:27:17.845 MyProject[4011:121268] viewDidLoad
2015-03-19 18:27:18.133 MyProject[4011:121268] tempCategories count 0
2015-03-19 18:27:18.136 MyProject[4011:121268] numberOfRowsInSection count 0
2015-03-19 18:27:18.137 MyProject[4011:121268] numberOfRowsInSection count 0
2015-03-19 18:27:19.019 MyProject[4011:121268] categories count 20when loadData finishes allCategories has not data in it (nil).
As far as I know it should work that way.. are you sure your success block is being called before you check the content of allCategories?
A success block work asynchronously, which means it will be executed only when the RequestOperationis completed (which can take a long time if you're downloading something big)
If you are trying to get the value of allCategories before the success block is executed you won't get what you're expecting. I would recommend using breakpoints or NSLog on your success block to see if it's been executed when you think it's doing it.
e.g
...
successBlock:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Success");
[self.allCategories addObject:tempObject]
}]; //End of request
[operation start]; //Begin executing the AFHTTPOperation
NSLog("%#",self.allCategories.description); //probably nil or empty
//since the success block hasn't been called yet
EDIT:
As I though, you are returning a value before is been set by the async operation, to return a value from an async operation I would suggest take a look to this answer and this one . Also you should read a bit of how async task work.
Basically what you want to do with async operations/tasks is make sure the value will be available when you want to use it. The main issue with that is that you don't know when the value will be set, but you can make sure what you want to do whenever it's set.
To do that you can create a simple method with a custom completion block
- (void)myCustomMethodWithCompletionBlock: (void (^)(NSArray *))completion {
//Do your request
//...
successBlock:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Success");
completionBlock(allCategories);
}]; //End of request
}
Meanwhile in your main method you call
[self myCustomMethodWithCompletionBlock:^(NSArray *allCategories) {
self.allCategories = allCategories;
//Do other stuff you need to with that variable since now you are
//sure the value will be set unless the operation failed
}];
I had the same problem a few days ago. My problem was my array seems nil, array allocations in viewdidload method may be your request run before viewDidLoad. Check it with debug if you see the array is nill then alloc array different place.
P.S: I m not expert but may be it's the same problem with me.
Try this:
dispatch_async(dispatch_get_main_queue(), ^{
[self.allCategories addObject:tempObject];
});
Define NSMutableArray with following line.
#property (nonatomic, strong) NSMutableArray * arrData;
initializein viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
self.arrData = [NSMutableArray array];
}
call following method with any UIButton action for see output OR working behavior
- (void) TestMethod {
dispatch_queue_t queue = dispatch_queue_create("myQueue", 0);
dispatch_async(queue, ^{
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL urlWithEncoding:#"https://www.google.co.in/?gws_rd=ssl"]];
[httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[httpClient setDefaultHeader:#"Accept" value:#"application/json"];
[httpClient setParameterEncoding:AFJSONParameterEncoding];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:#"" parameters:nil];
[request setTimeoutInterval:180];
[AFJSONRequestOperation addAcceptableContentTypes:[NSSet setWithObject:#"text/html"]];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
{
[self.arrData addObject:[NSDictionary dictionaryWithObjectsAndKeys:#"test",#"t3da",#"adsf",#"afds", nil]];
dispatch_semaphore_signal(sema);
} failure:^ (NSURLRequest *request, NSURLResponse *response, NSError *error, id json){
[self.arrData addObject:[NSDictionary dictionaryWithObjectsAndKeys:#"test",#"t3da",#"adsf",#"afds", nil]];
dispatch_semaphore_signal(sema);
}];
[operation start];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
DLog(#"arrData = %#",self.arrData);
});
}

How to Fetch data out from block in AFNetworking ios?

Creating first app with webservices, I am using AFNetworking for webservices. Everything is working fine but i have no idea , that how to fetch data out from block which i am getting in response. This is what i have done so far
+(WebServices *)sharedManager{
static WebServices *managerServices = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
managerServices = [[self alloc] init];
});
return managerServices;
}
-(NSArray *)firstPostService{
//1
NSURL *url = [NSURL URLWithString:BaseURLString];
//2
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSDictionary *param = #{#"request" : #"get_pull_down_menu" , #"data" : #"0,0,3,1"};
[manager POST:#"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
[self methodUsingJsonFromSuccessBlock:responseObject];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[av show];
}];
if (list.count == 0) {
NSLog(#"Nothing in array yet!!");
}
else{
NSLog(#"Object 1 is : %#", [list objectAtIndex:1]);
}
return list;
}
- (void)methodUsingJsonFromSuccessBlock:(id)json {
// use the json
NSString *string = [NSString stringWithUTF8String:[json bytes]];
NSLog(#"This is data : %#", string);
list = [string componentsSeparatedByString:#"\n"];
NSLog(#"After sepration first object: %#", [list objectAtIndex:1]);
//NSLog(#"json from the block : %#", json);
}
What i understand reading from different blogs and tuts, that block is a separate thread and what every i do finishes with it. I read some where that this is normally use for it
dispatch_async(dispatch_get_main_queue(), ^{
data = [string componentsSeparatedByString:#"\n"];
//WHERE DATA IS __block NSArray * data = [[NSArray alloc] init];
});
and i was returning it in the of the function(firstPostService) but nothing happen. i still get an empty array outside the block. Kindly help me , suggest me some good reading stuff. Thanking you all in advance.
You say:
I need this data to my view controller i am trying to return in dispatch part but it is not allowing. Is it possible to get data into my viewcontroller class ?
Yes, it's possible. But, no, firstPostService should not return the results. It can't because it returns immediately, but the POST completion blocks won't be called until much later. There's nothing to return by the time firstPostService returns.
At the end of your original question, you said:
What i understand reading from different blogs and tuts, that block is a separate thread and what every i do finishes with it. I read some where that this is normally use for it
dispatch_async(dispatch_get_main_queue(), ^{
data = [string componentsSeparatedByString:#"\n"];
//WHERE DATA IS __block NSArray * data = [[NSArray alloc] init];
});
This is not the appropriate pattern of __block local variable. You generally use that __block pattern when dealing with some block that runs synchronously (for example the block of an enumeration method). But while you can use __block variable with asynchronous block, you almost never do (and it doesn't quite make sense to even try to do it). When you use appropriate completion block patterns, there's no need for any __block variable.
So, let's go back to your original code sample: So, you should take a page from AFNetworking and employ completion blocks yourself. When the AFNetworking POST method wanted to return data to your code asynchonously, it used a completion block pattern, instead. Thus, if your own firstPostService wants to pass back data asynchronously, it should do the same.
For example:
#interface WebServices ()
#property (nonatomic, strong) AFHTTPSessionManager *manager;
#end
#implementation WebServices
// note, use `instancetype` rather than actually referring to WebServices
// in the `sharedManager` method
+ (instancetype)sharedManager
{
static id sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
// I'd also suggest that you init the `AFHTTPSessionManager` only once when this
// object is first instantiated, rather than doing it when `firstPostService` is
// called
- (instancetype)init
{
self = [super init];
if (self) {
NSURL *url = [NSURL URLWithString:BaseURLString];
self.manager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
self.manager.responseSerializer = [AFHTTPResponseSerializer serializer];
}
return self;
}
// Notice:
//
// 1. This now has a return type of `void`, because when it instantly returns,
// there is no data to return.
//
// 2. In order to pass the data back, we use the "completion handler" pattern.
- (void)firstPostServiceWithCompletionHandler:(void (^)(NSArray *list, NSError *error))completionHandler {
NSDictionary *param = #{#"request" : #"get_pull_down_menu" , #"data" : #"0,0,3,1"};
[self.manager POST:#"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray *list = [self methodUsingJsonFromSuccessBlock:responseObject];
if (completionHandler) {
completionHandler(list, nil);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[[[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] show];
if (completionHandler) {
completionHandler(nil, error);
}
}];
// // none of this code belongs here!!! You are dealing with asynchronous methods.
// // the `list` has not been returned by the time you get here!!! You shouldn't even
// // be using instance variable anyway!
//
// if (list.count == 0) {
//
// NSLog(#"Nothing in array yet!!");
// }
// else{
// NSLog(#"Object 1 is : %#", [list objectAtIndex:1]);
//
// }
// return list;
}
- (NSArray *)methodUsingJsonFromSuccessBlock:(NSData *)data {
// note, do not use `stringWithUTF8String` with the `bytes` of the `NSData`
// this is the right way to convert `NSData` to `NSString`:
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"This is string representation of the data : %#", string);
// Note, retire the `list` instance variable, and instead use a local variable
NSArray *list = [string componentsSeparatedByString:#"\n"];
NSLog(#"After sepration first object: %#", [list objectAtIndex:1]);
return list;
}
#end
Then, you could invoke that like so:
[[WebServices sharedManager] firstPostServiceWithCompletionHandler:^(NSArray *list, NSError *error) {
if (error) {
// handle the error here
} else {
// use the `list` results here
}
}];
// NOTE, DO NOT USE `list` HERE. By the time you get here, `list` has not been
// returned. Only use it in the above block.
//
// In fact, you can see that if you put a `NSLog` or breakpoint here, and again, above
// where it says "use the `list` results` here", you'll see that it's running the code
// inside that block _after_ this code down here!
I'd suggest you tackle the above first, to first make sure you completely understand the proper asynchronous technique of the completion block pattern. We don't want to complicate things quite yet. Make sure you're getting the sort of data you wanted before you proceed to what I will describe below.
But, once you've grokked the above, it's time to look at your JSON parsing. You make several reference to JSON, but if that's what it really is, then using componentsSeparatedByString is not the right way to parse it. You should use NSJSONSerialization. Or even better, you can let AFNetworking do that for you (right now, you're making it more complicated than it needs to be and your results will not be formatted correctly).
Above, I kept your methodUsingJsonFromSuccessBlock in the process, but if you're really dealing with JSON, you should eliminate that method entirely. Let AFNetworking do this for you.
You should eliminate the line that says:
responseSerializer = [AFHTTPResponseSerializer serializer];
The default serializer is AFJSONResponseSerializer which is what you want to use if handling JSON requests.
The methodUsingJsonFromSuccessBlock is then no longer needed because AFNetworking will do the JSON conversion for you. So firstPostServiceWithCompletionHandler should look like:
- (void)firstPostServiceWithCompletionHandler:(void (^)(NSArray *list, NSError *error))completionHandler {
NSDictionary *param = #{#"request" : #"get_pull_down_menu" , #"data" : #"0,0,3,1"};
[self.manager POST:#"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[[[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] show];
if (completionHandler) {
completionHandler(nil, error);
}
}];
}

iOS BoxSDK returning nil for sharedLink

We need to create a shared link for a file and then retrieve that link so
that we can display it inside our application.
We are able to create a shared link for a specific file (we can see it
inside Box Account on the Web) but we are not able to retrive sharedLink
via the API. It is always nil, although isShared method returns YES.
From the header file of BoxObject.h we find that these two methods provide
required information about shared state of the item.
#protocol BoxObject
// ...
// Information about the shared state of the item
#property (readonly, getter = isShared) BOOL shared;
#property (readonly) NSString *sharedLink;
//...
#end
This is how we create shared link.
Find BoxFile that we would like to share, lets call that object photo
Prior calling method shareWithPassword:message:emails:callbacks:, [photo
isShared] returns NO.
we call [photo shareWithPassword:#"" message:#"" emails:[NSArray
arrayWithObject:#""] callbacks:^(id<BoxOperationCallbacks>
on1){...}];
inside on1.after we check if response == BoxCallbackResponseSuccessful
and then we call [photo updateWithCallbacks:^(id
on2){..}]
inside on2.after we check if response == BoxCallbackResponseSuccessful
on successful response [photo isShared] returns YES but [photo
sharedLink] returns nil
And if we check on the Web, we can see that file is actually shared but we
cannot retrive sharedLink from the Box SDK.
Anyone has the same problem?
This is working for me, based off of the code already posted and the information found on github here
- (void) getShareableLinkForFileId:(NSString *)fileId
{
BoxFileBlock fileSuccess = ^(BoxFile *file) {
NSDictionary *fileInfo = file.rawResponseJSON;
if (![fileInfo[#"shared_link"] isEqual:[NSNull null]]) {
NSDictionary *linkData = fileInfo[#"shared_link"];
//Do something with the link
} else {
// failure
}
};
BoxAPIJSONFailureBlock failure = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSDictionary *JSONDictionary) {
//Handle the failure
};
BoxFilesRequestBuilder *builder = [[BoxFilesRequestBuilder alloc] init];
BoxSharedObjectBuilder *sharedBuilder = [[BoxSharedObjectBuilder alloc] init];
sharedBuilder.access = BoxAPISharedObjectAccessOpen;
builder.sharedLink = sharedBuilder;
[[BoxSDK sharedSDK].filesManager editFileWithID:fileId requestBuilder:builder success:fileSuccess failure:failure];
}
I was able to get the share link by refreshing the folder itself. This is the code I came up with:
[boxFile shareWithPassword:#"" message:#"" emails:#[ #"" ] callbacks:^(id<BoxOperationCallbacks> on) {
on.after(^(BoxCallbackResponse response) {
if (response == BoxCallbackResponseSuccessful) {
[self.rootFolder updateWithCallbacks:^(id<BoxOperationCallbacks> on) {
on.after(^(BoxCallbackResponse response) {
BoxFile *updatedBoxFile = (BoxFile*)[self.rootFolder.children objectAtIndex:self.selectedIndexPath.row];
NSString *fileName = updatedBoxFile.name;
NSString *shareLink = updatedBoxFile.sharedLink;
NSLog(#"%# [%#]: %#", fileName, updatedBoxFile.isShared ? #"YES" : #"NO", shareLink);
});
}];
} else {
[BoxErrorHandler presentErrorAlertViewForResponse:response];
}
});
}];
This is with the old v1 API. Not sure if it has changed with the newer v2.
You can create a shared link by edit its info with Box V2:
Box2FolderBlock folderSuccess = ^(Box2Folder *folder) {
if (![[folder sharedLink] isEqual:[NSNull null]]) {
NSString *sharedUrl = [[folder sharedLink] objectForKey:Box2APIObjectKeyURL];
} else {
// failure
}
};
Box2FileBlock fileSuccess = ^(Box2File *file) {
if (![[file sharedLink] isEqual:[NSNull null]]) {
NSString *sharedUrl = [[file sharedLink] objectForKey:Box2APIObjectKeyURL];
} else {
// failure
}
};
Box2APIJSONFailureBlock failure = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSDictionary *JSONDictionary) {
};
BoxSharedObjectBuilder *sharedLinkObject = [[BoxSharedObjectBuilder alloc] init];
sharedLinkObject.access = BoxAPISharedObjectAccessOpen;
BoxAPIJSONOperation *operation;
if (isFile == NO) {
sharedLinkObject.canPreview = BoxAPISharedObjectPermissionStateEnabled;
BoxFoldersRequestBuilder *requestBuilder = [[BoxFoldersRequestBuilder alloc] init];
requestBuilder.sharedLink = sharedLinkObject;
operation = [boxSDK.foldersManager editFolderWithID:fileOrFolderId requestBuilder:requestBuilder success:folderSuccess failure:failure];
} else {
sharedLinkObject.canDownload = BoxAPISharedObjectPermissionStateEnabled;
BoxFilesRequestBuilder *requestBuilder = [[BoxFilesRequestBuilder alloc] init];
requestBuilder.sharedLink = sharedLinkObject;
operation = [boxSDK.filesManager editFileWithID:fileOrFolderId requestBuilder:requestBuilder success:fileSuccess failure:failure];
}

Resources