I need to download multiple files in queue. Right now my code is working and all files are downloading simultaneously, However i need to download one file at a time, and all other files are in queue, below is code, please let me know what i doing wrong. I just need to download one file at a time and all other file should be in queue.
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:videoURL];
[httpClient.operationQueue setMaxConcurrentOperationCount:1];
for (NSURL *videoString in videoArray) {
NSURLRequest *request = [NSURLRequest requestWithURL:videoString];
AFDownloadRequestOperation *operation = [[AFDownloadRequestOperation alloc] initWithRequest:request targetPath:path shouldResume:YES];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if(operation.response.statusCode == 200) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Successfully Downloaded" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if(operation.response.statusCode!= 200) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Error While Downloaded" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}];
[httpClient enqueueHTTPRequestOperation:operation];
[operation setProgressiveDownloadProgressBlock:^(NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpected, long long totalBytesReadForFile, long long totalBytesExpectedToReadForFile) {
float percentDone = ((float)totalBytesRead) / totalBytesExpectedToReadForFile;
}
}];
To limit the queue to one Operation at a time,
you could try adding dependencies between each operation before you queue them.
Like this
[Operation2 addDependency:Operation1];
Hope that helps!
use dispatch queues(GCD) after the block setCompletionBlockWithSuccess:
like following way:
dispatch_async(dispatch_get_main_queue(), ^{
// Call any method from on the instance that created the operation here.
[self doSomework]; // example
});
Related
Is there any way to send a xml file (CDA) to health app from another application? Because my application is getting a xml file from a source and I want to send it directly to the new health app (iOS 10).
Create an HKCDADocumentSample and save it with an HKHealthStore.
First, check for authorization
(void) checkForAuthorization {
if ([HKHealthStore isHealthDataAvailable]) {
NSSet *setRead = [NSSet setWithObjects [HKObjectTypedocumentTypeForIdentifier:HKDocumentTypeIdentifierCDA], nil];
NSSet *setWrite = [NSSet setWithObjects:[HKObjectType documentTypeForIdentifier:HKDocumentTypeIdentifierCDA], nil];
[_store requestAuthorizationToShareTypes:setWrite readTypes:nil completion:^(BOOL success, NSError * _Nullable error) {
if (error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
} else if (success) {
[self performSelectorOnMainThread:#selector(addDocumentToHealthApp) withObject:nil waitUntilDone:NO];
}
NSLog(#" Success = %#",success? #"YES" : #"NO");
} ];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Health Kit not supported in device." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
}
Second, add Record wmthod this will add a Health record to health app.
(void) addRecordToHealthApp
{
NSURL *cdaPath = [[NSBundle mainBundle] URLForResource:#"sample" withExtension:#"xml"];
NSString*stringPath = [cdaPath absoluteString];
NSData *dataOfCDAFile = [NSData dataWithContentsOfURL:[NSURL URLWithString:stringPath]];
NSDate *now = [NSDate date];
int daysToAdd = 7;
NSDate *newDate1 = [now dateByAddingTimeInterval:60*60*24*daysToAdd];
NSError *err;
HKCDADocumentSample *doc = [HKCDADocumentSample CDADocumentSampleWithData:dataOfCDAFile startDate:[NSDate date] endDate:newDate1 metadata:nil validationError:&err ];
UIAlertView *alert;
if (err) {
alert = [[UIAlertView alloc] initWithTitle:#"Error" message:err.localizedDescription delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
[_store saveObject:doc withCompletion:^(BOOL success, NSError * _Nullable error) {
NSLog("Stored %#",success?#"YES":#"NO");
}];
}
I have a static file path to a download folder, files download fine but when I download a new one the old one gets replaced by new. I suppose because of static pathing. How do I add a timestamp to my code so whenever a new download is made the old one wouldn't get replaced?Or maybe even give download file the "original" filename from the actual array?
here is my download code:
-(void) Savefile {
[self.downloadfile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Wooopss!" message:#"Download failed. Try Again..." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
else if (data) {
[data writeToFile:#"/Users/Danny/Desktop/PDFFile/hello.pdf" atomically:NO ];
NSLog(#"Downloading file...");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Downloading" message:#"File is being downloaded..." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}];
}
NSDateFormatter *dateFormatter = [NSDateFormatter new];
[dateFormatter setDateFormat:#"MMddYYYYmmss"];
NSString *filePath = [NSString stringWithFormat:#"/Users/Danny/Desktop/PDFFile/hello_%#.pdf", [dateFormatter stringFromDate:[NSDate date]]];
Which will give you this:
/Users/Danny/Desktop/PDFFile/hello_032620144401.pdf
Not entirely sure what you mean by:
Or maybe even give download file the "original" filename from the
actual array
I have an issue showing a UIAlertView on the main thread. I'm not sure why but it keeps crashing, despite me running on the main thread. The following block is on the background thread, but I have the alert on the main as below:
void (^removeFromCalendar)(NSString *, NSString *, EKEventStore *) = ^(NSString *error, NSString *eventKey, EKEventStore *eventDB) {
EKEvent *myEvent = [eventDB eventWithIdentifier:eventKey];
NSError *err = noErr;
if(myEvent != NULL && myEvent != (id)[NSNull null]) {
[eventDB removeEvent:myEvent span:EKSpanThisEvent error:&err];
} else {
// Event was not found, nothing to do
return;
}
[eventDB release];
if (!err || err == noErr) {
NSLog(#"Deleted event %#", myEvent.title);
// Show alert on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// Showing the alert for unattending
NSString *resultString = #"This event was removed from your calendar.";
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Info" message:resultString delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] autorelease];
[alert show];
});
return;
}
error = err.description;
};
If I comment out the bottom where it shows the alert, everything is fine. But for the alert, I keep getting a EXC_BAD_ACCESS error. Can somebody explain why? It's on the correct thread, and I cant for the life of me understand where the memory issue could come from!
May be you view is being released when you finish until you finish with the background queue. So, for safety why dont you use it like this;
...........
UIViewController __weak *myController = self;
dispathch_async(backgroundQueue, ^{
UIViewController __strong *myStrongController = myController;
...............
dispatch_async(dispatch_get_main_queue(), ^{
if(myStrongController){
// Showing the alert for unattending
NSString *resultString = #"This event was removed from your calendar.";
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Info" message:resultString delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] autorelease];
[alert show];
}
});
}).
This is how you present an alert view:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"<#(NSString *)#>" message:#"<#(NSString *)#>" delegate:self cancelButtonTitle:#"<#(NSString *)#>" otherButtonTitles:nil];
[alert show];
[alert release];
Instead of using dispatch_async, why not use the objective C call:
[self performSelectorOnMainThread
You might have to package it up in its own method. Alternatively, call it using:
[self performSelector:#selector(myAlertMethod) withObject:nil afterDelay:0.25]
These methods have been tried and true since day 1.
This question already has answers here:
UIAlertView fails to show and results in “EXC_BAD_ACCESS” error
(6 answers)
Closed 8 years ago.
I'm using AFNetworking to make a web request. After the web request completes. I want a UIAlertView to be shown. I'm using ARC and the code seems to work on devices. If I use a simulator I get an error: EXC_BAD_ACCESS
What am I doing wrong?
UIAlertView* postSubmitAlertView = [[UIAlertView alloc] init];
postSubmitAlertView.delegate = self;
[postSubmitAlertView addButtonWithTitle:#"Ok"];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[op setCompletionBlock:^(void) {
if(op.response.statusCode == 200) {
postSubmitAlertView.title = #"Submission Good";
postSubmitAlertView.message = #"GOOD";
} else {
postSubmitAlertView.title = #"Submission Failed.";
postSubmitAlertView.message = #"FAIL";
}
[postSubmitAlertView show];
}];
[op start];
The key problem is that UIKit stuff should be called on the main thread.
Note: For the most part, UIKit classes should be used only from an application’s main thread. This is particularly true for classes derived from UIResponder or that involve manipulating your application’s user interface in any way.
UIKit Framework Reference
Looking at the docs for NSOperation under setCompletionBlock:
Discussion
The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context. Instead, you should shunt that work to your application’s main thread or to the specific thread that is capable of doing it.
The simplest solution to modify your code is to call the UIKit stuff on the main thread
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *URL = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[op setCompletionBlock:^(void) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView* postSubmitAlertView = [[UIAlertView alloc] init];
postSubmitAlertView.delegate = self;
[postSubmitAlertView addButtonWithTitle:#"Ok"];
if(op.response.statusCode == 200) {
postSubmitAlertView.title = #"Submission Good";
postSubmitAlertView.message = #"GOOD";
} else {
postSubmitAlertView.title = #"Submission Failed.";
postSubmitAlertView.message = #"FAIL";
}
[postSubmitAlertView show];
});
}];
[op start];
}
The EXC_BAD_ACCESS is caused by accessing a released object. To avoid this make your call to UIAlertView kind of modal:
//Function body:
-(void)checkSaving
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Do you want to add these results to your database?"
message:#"\n\n"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Save", nil];
alert.alertViewStyle = UIAlertViewStyleDefault;
[alert show];
//this prevent the ARC to clean up :
NSRunLoop *rl = [NSRunLoop currentRunLoop];
NSDate *d;
d = (NSDate*)[d init];
while ([alert isVisible]) {
[rl runUntilDate:d];
}
}
//Your choice result:
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// the user clicked one of the OK/Cancel buttons
if (buttonIndex == 1)//Save
{
//do something
}
if (buttonIndex == 0)//NO
{
//do something
}
}
//Register the functions in the interface declaration:
#interface yourViewController ()
-(void)checkSaving
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
//...
#end
//To call:
[self checkSaving];
I wish this will help you.
UIAlertView* postSubmitAlertView = [[UIAlertView alloc] init];
postSubmitAlertView.delegate = self;
[postSubmitAlertView addButtonWithTitle:#"Ok"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success: %#", operation.responseString);
postSubmitAlertView.title = #"Submission Good";
postSubmitAlertView.message = #"GOOD";
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", operation.responseString);
postSubmitAlertView.title = #"Submission Failed.";
postSubmitAlertView.message = #"FAIL";
}
];
[postSubmitAlertView show];
Hope this will solve your problem.
I am using a private MBProgressHUD
Now I am using the indicator view on my add button in which I am calling my addrecord service .
UIWindow *window = [UIApplication sharedApplication].keyWindow;
HUD = [[MBProgressHUD alloc] initWithWindow:window];
// Add HUD to screen
[window addSubview:HUD];
// Regisete for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(addingToFavorites) onTarget:self withObject:nil animated:YES];
the adding to favorites method :
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
[request setHTTPMethod:#"GET"];
//[request setTimeoutInterval:10];
//NSURLResponse *response = nil;
// NSError *error = nil;
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSData *data1= [NSURLConnection sendSynchronousRequest:request
returningResponse:nil error:nil];
if(data1 == nil)
{
doneFlag = NO;
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert"
message:#"The network is not available.\n Please check the Internet connection."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
else
{
doneFlag = YES;
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Confirmation"
message:#"Added To favorites"
delegate:nil
cancelButtonTitle:#"OKAY"
otherButtonTitles:nil];
[alert show];
alert = nil;
[alert release];
}
[request release];
This is all running fine except the instruments gives leak of the uialertview may be it is conflicting with the mbprogreshud.
So I thought to remove the alert from the calling method and put it in the caller the method like this:
the caller method now :
UIWindow *window = [UIApplication sharedApplication].keyWindow;
HUD = [[MBProgressHUD alloc] initWithWindow:window];
// Add HUD to screen
[window addSubview:HUD];
// Regisete for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(addingToFavorites) onTarget:self withObject:nil animated:YES];
//it should wait for the above line to be executing ******* then to exexute the be //below condition but how ?
if (doneFlag == NO) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert"
message:#"The network is not available.\n Please check the Internet connection."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
} else {
[favoritesButton setTitle:#"Remove" forState:UIControlStateNormal];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Confirmation"
message:#"Added To favorites"
delegate:nil
cancelButtonTitle:#"OKAY"
otherButtonTitles:nil];
[alert show];
alert = nil;
[alert release];
}
the adding to favorites method :
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
[request setHTTPMethod:#"GET"];
//[request setTimeoutInterval:10];
//NSURLResponse *response = nil;
// NSError *error = nil;
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSData *data1= [NSURLConnection sendSynchronousRequest:request
returningResponse:nil error:nil];
if(data1 == nil)
{
doneFlag = NO;
}
else
{
doneFlag = YES;
}
[request release];
In the launching of the progresshud thread is detaching something like this :
[NSThread detachNewThreadSelector:#selector(launchExecution) toTarget:self withObject:nil]
Now My question is that If I follow the first scenario . How can I assure the the alertview leak will not come
Or If I am following the second scenario How can I assure the if condition will be executed after completing this line executed :
[HUD showWhileExecuting:#selector(addingToFavorites) onTarget:self withObject:nil animated:YES];
Regarding the first scenario, it is in general a bad idea to do UI updates from threads other than the applications main thread. UIKit is NOT thread safe and doing threaded UI updates can cause all sorts of strange things to happen. Now, I'm not sure if this is the cause for the leak but I would avoid showing an UIAlertView in addingToFavorites. Use performSelectorOnMainThread or the second scenario described below.
Regarding the second scenario, move everything below the showWhileExecuting call to the hudWasHidden delegate method. At this point you can be sure that your code was fully executed and the doneFlag was set.
To use performSelectorOnMainThread, define a new method, put your code in it and than call performSelectorOnMainThread.
I.e.,
- (void)showAlert {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:#"The network is not available.\n Please check the Internet connection." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
Call with,
[self performSelectorOnMainThread:#selector(showAlert) withObject:nil waitUntilDone:NO];
I would go with the second scenario though.
Other answers notwithstanding, you were creating the UIAlertView leak with this sequence:
[alert show];
alert = nil;
[alert release];
The last two lines should be swapped:
[alert show];
[alert release];
alert = nil;