writeToFile how to tell when it is completed - iphone

I'm doing some writing of data to my documents directory and I was wondering if there is a way to tell when my writeToFile method completes and the file I am creating has be fully created. Thanks in advance here is the method I am calling I am just looking for a way wether it is a delegate method or some other way to tell when this completes.
[imageData writeToFile:fullPathToFile atomically:YES];
Nick

The method writeToFile:atomically is "synchronous". It will write the file and then return YES or NO depending on wether the file was successfully written or not.
That means that the operation is complete as soon as the method returns.
BOOL success = [imageData writeToFile:fullPathToFile atomically:YES];
// Now, the operation is complete
// success indicates whether the file was successfully written or not

Swift 5:
do {
try data.write(to: path)
// here's when write operation is completed without errors thrown
} catch {
Swift.print(error)
}

Related

Custom block inside dispatch_async

This code works
[[MyManager sharedManager] makeRequestAndParsingfor:someParameters
success:^(NSDictionary * dictionary){
// Sucessful response
NSLog(#"Success!!");
}
failure:^(NSError* error){
// Error response
NSLog(#"Failure!");
}];
But this whenever I run the same in the background it never enters in success or failure block.
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[[MyManager sharedManager] makeRequestAndParsingfor:someParameters
success:^(NSDictionary * dictionary){
// Sucessful response
NSLog(#"Success!!");
}
failure:^(NSError* error){
// Error response
NSLog(#"Failure!");
}];
}];
Can anybody explain me what happens? The method makeRequestAndParsingfor: makes an asynchronous request (with blocks again) and then parses the result. My debugger shows that it never gets in its own success/failure in the second case. In the first case it works like a charm. Any ideas?
Does your method 'makeRequestAndParsingfor' use block_copy() of the args, and store the return in a strong variable (to get the blocks in the heap)? Also add asserts() so you can verify you get both blocks in 'makeRequestAndParsingfor', and even retest before calling one or the other. [In the past use of block_copy() was necessary but now not 100% sure.]
Note that in the second case, where 'makeRequestAndParsingfor' runs in a concurrent queue that multiple of these can call the blocks concurrently - not sure what your success/failure block does, but it better be thread safe, or you should run the block on the main queue in 'makeRequestAndParsingfor' (which I do in my similarly constructed app).

Streaming with MKNetworkKit on iOS

I'm trying to figure out how to make MKNetworkKit working with data from stream. I can see that some data is beeing downloaded (the indicator on status bar), but I don't have any idea what happens with that data after it's actually downloaded. I put a NSLog statement inside body of connection: didReceiveData: but it's not called during streaming. Any pointers how to fix that issue ?
Edit
Sorry my question was inaccurate. I know how to stream to a file but I need to stream to memory (NSData instance preferably). Okay it seems simple again due to NSOutputStream method initWithBytes:capacity:. And my problem is here, my stream has undefined length so there would be enormous impact on memory. I don't know what to do. My perfect solution works like this. Small chunks of data from the stream are processed having been downloaded and then they are discarded.
You could use the outputStreamToBuffer:capacity: method to create the stream.
As for the buffer, you can use a circular buffer, so that you can read from it as the stream writes to it. A great implementation (and explanation) is here.
Streaming a file download is a three line magic with MKNetworkKit.
//Create a MKNetworkOperation for the remote URL.
MKNetworkOperation *op = [self operationWithURLString:remoteURL
params:nil
httpMethod:#"GET"];
// add your output stream, in this case a file
[op addDownloadStream:[NSOutputStream outputStreamToFileAtPath:filePath
append:YES]];
// enqueue the operation to a MKNetworkEngine.
[self enqueueOperation:op];
That's it.

NSData's dataWithBytesNoCopy and writeToFile

I have a category method that does somethign like this:
#implementation NSData (additions)
- (id)someFunc {
char *buffer = malloc(255);
NSUInteger length = 0;
while (true) {
// . . . fill buffer[length++];
}
realloc(buffer, length);
return [NSData dataWithBytesNoCopy:buffer length:length freeWhenDone:NO];
}
I am then trying to write the returned data (well call this NSData *fileData):
NSError *error;
NSData fileData = [NSData someFunc];
[fileData writeToFile:somePath options:NSDataWritingAtomic error:&error];
I get an error:
Error Domain=NSCocoaErrorDomain
Code=512 "The operation couldn’t be
completed. (Cocoa error 512.)"
UserInfo=0x20deffd0
{NSFilePath=/Users/user/Library/Application
Support/iPhone
Simulator/4.3/Applications/4C315580-153D-4FA7-9474-E17B58832515/Library/Caches/file.pdf,
NSUnderlyingError=0x20de1fe0 "The
operation couldn’t be completed. Bad
address"}
The path exists and is valid. I think the issue is the returned NSData is just a light wrapper around an array allocated with malloc and that writeToFile does not know how to handle this. Does anyone see what I am doing wrong?
When you call realloc you must save the returned pointer, since realloc may allocate a new buffer and copy the content of the old location to that new location. Afterwards the old location is freed and thus invalid. A different malloc call may overwrite that old location.
So you need to do:
buffer = realloc(buffer, length);
That code doesn't make any sense.
show the code that you use to fill the buffer
you have a malloc(), why do you realloc() after the loop? That is a waste of cycles and, if the goal were to expand the buffer if the input data were too long, it won't work as written.
you must check the return value from writeToFile:options:error: to know whether or not there is an error; you cannot check the error directly.
also, don't stick a category like that onto an existing class. In general, it is indicative of an architecture that is fragile and poorly layered. If you do go this route, at least prefix your method with something unique.
It isn't clear why that particular error came up. That path seems a little wonky maybe, how are you generating it?

AudioFileWriteBytes returns error -50 randomly

I have the following code snippet:
OSStatus status = AudioFileWriteBytes(self.audioFile, FALSE, self.startingByte, &ioNumBytes, theData);
The status code randomly returns noErr and -50 on the iPhone simulator.
It then works if I re-run it.
Any pointer is appreciated why the above code behaves randomly.
Thanks in advance for your help.
I think I found my issue.
The original code with the problem:
// Start
OSStatus status = AudioOutputUnitStart(self.ioUnit);
// Record the audio samples and save it to a file
[self createFile];
The new code that fixed the problem. Notice the "createFile" is called first before calling AudioOutputUnitStart
// Record the audio samples and save it to a file
[self createFile];
// Start
// Once AudioOutputUnitStart is called, it will start calling callback method quickly. We need to call the above [self createFile] first.
OSStatus status = AudioOutputUnitStart(self.ioUnit);
The AudioOutputUnitStart calls the callback method which will write audio samples to a file. Since the file has been created/opened before AudioOutputUnitStart, now the audio samples are written to the file without any error.

concurrent background downloads on iphone

I am trying to create class that will handle multiple downloads at same time (I need to download a lot of small files) and I have problems with "disappearing" connections.
I have function addDonwload that adds url to list of urls to download, and checks if there is free download slot available. If there is one it starts download immediately. When one of downloads finishes, I pick first url form list and start new download.
I use NSURLConnection for downloading, here is some code
- (bool) TryDownload:(downloadInfo*)info
{
int index;
#synchronized(_asyncConnection)
{
index = [_asyncConnection indexOfObject:nullObject];
if(index != NSNotFound)
{
NSLog(#"downloading %# at index %i", info.url, index);
activeInfo[index] = info;
NSURLRequest *request = [NSURLRequest requestWithURL:info.url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15];
[_asyncConnection replaceObjectAtIndex:index withObject:[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE]];
//[[_asyncConnection objectAtIndex:i] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
return true;
}
}
return false;
}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
[self performSelectorOnMainThread:#selector(DownloadFinished:) withObject:connection waitUntilDone:false];
}
- (void)DownloadFinished:(id)connection
{
NSInteger index = NSNotFound;
#synchronized(_asyncConnection)
{
index = [_asyncConnection indexOfObject:(NSURLConnection*)connection];
}
[(id)activeInfo[index].delegate performSelectorInBackground:#selector(backgroundDownloadSucceededWithData:) withObject:_data[index]];
[_data[index] release];
[activeInfo[index].delegate release];
#synchronized(_asyncConnection)
{
[[_asyncConnection objectAtIndex:index] release];
[_asyncConnection replaceObjectAtIndex:index withObject:nullObject];
}
#synchronized(downloadQueue)
{
[downloadQueue removeObject:activeInfo[index]];
[self NextDownload];
}
}
- (void)NextDownload
{
NSLog(#"files remaining: %i", downloadQueue.count);
if(downloadQueue.count > 0)
{
if([self TryDownload:[downloadQueue objectAtIndex:0]])
{
[downloadQueue removeObjectAtIndex:0];
}
}
}
_asyncConnection is my array of download slots (NSURLConnections)
downloadQueue is list of urls to download
What happens is, at the beginning everything works ok, but after few downloads my connections start to disappear. Download starts but connection:didReceiveResponse: never gets called. There is one thing in output console that I don't understand I that might help a bit. Normaly there is something like
2010-01-24 21:44:17.504 appName[3057:207]
before my NSLog messages. I guess that number in square brackets is some kind of app:thread id? everything works ok while there is same number, but after some time, "NSLog(#"downloading %# at index %i", info.url, index);" messages starts having different that second number. And when that happens, I stop receiving any callbacks for that urlconnection.
This has been driving me nuts as I have strict deadlines and I can't find problem. I don't have many experiences with iphone dev and multithreaded apps. I have been trying different approaches so my code is kinda messy, but I hope you will see what I am trying to do here :)
btw is anyone of you know about existing class/lib I could use that would be helpful as well. I want parallel downloads with ability o dynamically add new files to download (so initializing downloader at the beginning with all urls is not helpful for me)
You've got a bunch of serious memory issues, and thread synchronization issues in this code.
Rather than go into them all, I'll ask the following question: You are doing this on a background thread of some kind? Why? IIRC NSURLConnection already does it's downloads on a background thread and calls your delegate on the thread that the NSURLConnection was created upon (e.g., your main thread ideally).
Suggest you step back, re-read NSURLConnection documentation and then remove your background threading code and all the complexity you've injected into this unnecessarily.
Further Suggestion: Instead of trying to maintain parallel positioning in two arrays (and some sketchy code in the above relating to that), make one array and have an object that contains both the NSURLConnection AND the object representing the result. Then you can just release the connection instance var when the connection is done. And the parent object (and thus the data) when you are done with the data.
I recommend that you take a look at this:
http://allseeing-i.com/ASIHTTPRequest/
It's a pretty sophisticated set of classes with liberal licensing terms (free too).
It may provide a lot of the functionality that you are wanting.
This snippet can be the source of the bug, you release the object pointed to by the activeInfo[index].delegate pointer right after issuing async method call on that object.
[(id)activeInfo[index].delegate performSelectorInBackground:#selector(backgroundDownloadSucceededWithData:) withObject:_data[index]];
[_data[index] release];
[activeInfo[index].delegate release];
Do you use connection:didFailWithError: ? There may be a timeout that prevents the successful download completion.
Try to get rid of the #synchronized blocks and see what happens.
The string inside the square brackets seems to be thread identifier as you guessed. So maybe you get locked in the #synchronized. Actually, I don't see a reason for switching thread - all the problematic code should run in the main thread (performSelectorOnMainThread)...
Anyhow, there is no need to use both the #synchronized and the performSelectorOnMainThread.
BTW, I didn't see the NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; line. Where do you initiate the connection?
As for the parallel downloads - I think that you can download more than one file in a time with the same code that you use here. Just create a separate connection for each download.
Consider just keeping a download queue along with a count of active connections, popping items off the top of the queue when downloads complete and a slot becomes free. You can then fire off NSURLConnection objects asynchronously and process events on the main thread.
If you find that your parallel approach prohibits doing all of the processing on the main thread, consider having intermediary manager objects between your main thread download code and NSURLConnection. Using that approach, you'd instantiate your manager and get it to use NSURLConnection synchronously on a background thread. That manager then completely deals with the downloading and passes the result back to its main thread delegate using a performSelectorOnMainThread:withObject: call. Each download is then just a case of creating a new manager object when you've a slot free and setting it going.