Inside nsurlsession and background transfer - nsurlsession

I am working in project, where I need to download large JSON response in background (Even if app crashes).
I also want to understand internals of background transfer service, i.e. what iOS is doing internally and how. So I have saved call stack in UserDefaults.
there were 2 scenarios.
1) Without app crash.
(
Download task created with taskidentifier : 1,
didfinishdownloadingtourl,
File downloaded for taskidentifier : 1,
didCompleteWithError->success for taskIdentifier 1,
)
2) When I crash app manually using null pointer dereference after downloading started, results were weird.
(
Download task created with taskidentifier : 1,
Download task created with taskidentifier : 2,
handleEventsForBackgroundURLSession,
didfinishdownloadingtourl,
File downloaded for taskidentifier : 1,
didCompleteWithError->success for taskIdentifier 1,
URLSessionDidFinishEventsForBackgroundURLSession,
didfinishdownloadingtourl,
File downloaded for taskidentifier : 2,
didCompleteWithError->success for taskIdentifier 2
)
I was only creating 1 task.
Does iOS automatically creates second task ?
What is happening here.
Here is my code
#import "KiviSyncLogin.h"
#import "AppDelegate.h"
#implementation KiviSyncLogin
-(instancetype)init{
self = [super init];
return self;
}
-(void)startSync{
if(self.session != nil){
NSLog(#"session is not nil");
return;
}
self.session = [self setUpSession]; // init session with once token
NSLog(#"This is session : %#",self.session);
[self pullDataFromServerWithSession:self.session];
}
-(NSURLSession *)setUpSession{
static dispatch_once_t onceToken;
static NSURLSession *session = nil;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"com.kivi.login.sync"];
config.HTTPMaximumConnectionsPerHost = 1;
session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
});
return session;
}
-(void)pullDataFromServerWithSession:(NSURLSession *)session{
NSString *serverUrlString = #"apiurl";
NSMutableDictionary *mydic = [[NSMutableDictionary alloc] init];
[mydic setObject:#"email" forKey:#"email"];
[mydic setObject:#"password" forKey:#"password"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverUrlString]];
request.HTTPMethod = #"POST";
request.HTTPBody = [[self encodedString:mydic] dataUsingEncoding:NSUTF8StringEncoding];
self.downloadTask = [self.session downloadTaskWithRequest:request];
NSLog(#"Download task %# created with identifier : %ld", self.downloadTask,self.downloadTask.taskIdentifier);
[self.downloadTask resume];
}
/* common delegate Start */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
NSLog(#"file downloaded");
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *documentsDirectory = [URLs objectAtIndex:0];
NSURL *originalURL = [[downloadTask originalRequest] URL];
NSURL *destinationURL = [documentsDirectory URLByAppendingPathComponent:[originalURL lastPathComponent]];
NSError *errorCopy;
[fileManager removeItemAtURL:destinationURL error:NULL];
BOOL success = [fileManager copyItemAtURL:location toURL:destinationURL error:&errorCopy];
if (success){
/* Store data in database */
}
else{
NSLog(#"Error during the copy: %#", [errorCopy localizedDescription]);
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if(task.taskIdentifier == self.downloadTask.taskIdentifier){
if(error){
NSLog(#"Download task completed with error : %#",error);
/* Show error that data is not downloaded */
[self windUpSession];
}else{
NSLog(#"Download task completed successfully");
}
}
}
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
NSLog(#"Session %# URL Session Did Finish Events For Background URL Session\n", session);
dispatch_async(dispatch_get_main_queue(), ^{
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
[session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([downloadTasks count] == 0 && [uploadTasks count] == 0) {
if (appDelegate.backgroundTransferCompletionHandler != nil) {
NSLog(#"I have completion handler");
void(^completionHandler)(void) = appDelegate.backgroundTransferCompletionHandler;
appDelegate.backgroundTransferCompletionHandler = nil;
[self windUpSession];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
completionHandler();
}];
}
}
}];
});
}
- (void)windUpSession{
//[self.session invalidateAndCancel];
self.session = nil;
self.downloadTask = nil;
}
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error{
if(error == nil){
NSLog(#"Session is invalidated successfully");
}else{
NSLog(#"Error while invalidating session");
}
}
/* Common Delegates End */
/* Helper methods */
-(NSString *)encodedString:(NSDictionary *)mydic{
NSString *params = #"";//[NSString stringWithFormat:#"email=%#&password=%#",email,token];
int i = 0;
for (NSString *key in [mydic allKeys]) {
NSString *stringToBeAppended;
if(i++ == 0)
stringToBeAppended = [NSString stringWithFormat:#"%#=%#",key,mydic[key]];
else
stringToBeAppended = [NSString stringWithFormat:#"&%#=%#",key,mydic[key]];
params = [params stringByAppendingString:stringToBeAppended];
}
return params;
}
#end
If I add [self.session finishTasksAndInvalidate]; while creating download task, it will create 2nd task but do not execute it as session is invalidated.
in this case call stack is (If I carsh app manually.)
(
Download task created with taskidentifier : 1,
Download task created with taskidentifier : 2,
handleEventsForBackgroundURLSession,
didfinishdownloadingtourl,
File downloaded for taskidentifier : 1,
didCompleteWithError->success for taskIdentifier 1,
URLSessionDidFinishEventsForBackgroundURLSession
)
Edit 1
I have also noticed that when my app crashes, viewDidLoad method of viewController from where I am crashing app and from which I am initializing above class and start fetching is being called.

Related

Can VideoToolbox decode H264 Annex B natively? Error Code -8969 BadData

My goal is to mirror the screen of an iDevice to OSX, as lag-free as possible.
To my knowledge there are 2 ways to this:
Airplay Mirroring (e.g. Reflector)
CoreMediaIO via Lightning (e.g. Quicktime Recording)
I have chosen to pursue the second method, because (to my knowledge) connected iDevices can be recognized as DAL devices automatically after a one-time setup.
The main resource on how to do this is this blog: https://nadavrub.wordpress.com/2015/07/06/macos-media-capture-using-coremediaio/
That blog goes very deep into how to use CoreMediaIO, however it seems like you can work with AVFoundation once you have recognized the connected iDevice as an AVCaptureDevice.
This question: How to mirror iOS screen via USB? has posted a solution on how to grab each frame of the H264 (Annex B) muxxed datastream supplied by the iDevice.
However, my problem is that VideoToolbox will not correctly decode (Error Code -8969, BadData), even though there shouldn't be any difference in the code.
vtDecompressionDuctDecodeSingleFrame signalled err=-8969 (err) (VTVideoDecoderDecodeFrame returned error) at /SourceCache/CoreMedia_frameworks/CoreMedia-1562.240/Sources/VideoToolbox/VTDecompressionSession.c line 3241
Complete Code:
#import "ViewController.h"
#import CoreMediaIO;
#import AVFoundation;
#import AppKit;
#implementation ViewController
AVCaptureSession *session;
AVCaptureDeviceInput *newVideoDeviceInput;
AVCaptureVideoDataOutput *videoDataOutput;
- (void)viewDidLoad {
[super viewDidLoad];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
// Allow iOS Devices Discovery
CMIOObjectPropertyAddress prop =
{ kCMIOHardwarePropertyAllowScreenCaptureDevices,
kCMIOObjectPropertyScopeGlobal,
kCMIOObjectPropertyElementMaster };
UInt32 allow = 1;
CMIOObjectSetPropertyData( kCMIOObjectSystemObject,
&prop, 0, NULL,
sizeof(allow), &allow );
// Get devices
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed];
BOOL deviceAttahced = false;
for (int i = 0; i < [devices count]; i++) {
AVCaptureDevice *device = devices[i];
if ([[device uniqueID] isEqualToString:#"b48defcadf92f300baf5821923f7b3e2e9fb3947"]) {
deviceAttahced = true;
[self startSession:device];
break;
}
}
}
return self;
}
- (void) deviceConnected:(AVCaptureDevice *)device {
if ([[device uniqueID] isEqualToString:#"b48defcadf92f300baf5821923f7b3e2e9fb3947"]) {
[self startSession:device];
}
}
- (void) startSession:(AVCaptureDevice *)device {
// Init capturing session
session = [[AVCaptureSession alloc] init];
// Star session configuration
[session beginConfiguration];
// Add session input
NSError *error;
newVideoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (newVideoDeviceInput == nil) {
dispatch_async(dispatch_get_main_queue(), ^(void) {
NSLog(#"%#", error);
});
} else {
[session addInput:newVideoDeviceInput];
}
// Add session output
videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
videoDataOutput.videoSettings = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey: (id)kCVPixelBufferPixelFormatTypeKey];
dispatch_queue_t videoQueue = dispatch_queue_create("videoQueue", NULL);
[videoDataOutput setSampleBufferDelegate:self queue:videoQueue];
[session addOutput:videoDataOutput];
// Finish session configuration
[session commitConfiguration];
// Start the session
[session startRunning];
}
#pragma mark - AVCaptureAudioDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
//NSImage *resultNSImage = [self imageFromSampleBuffer:sampleBuffer];
//self.imageView.image = [self nsImageFromSampleBuffer:sampleBuffer];
self.imageView.image = [[NSImage alloc] initWithData:imageToBuffer(sampleBuffer)];
}
NSData* imageToBuffer( CMSampleBufferRef source) {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(source);
CVPixelBufferLockBaseAddress(imageBuffer,0);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
void *src_buff = CVPixelBufferGetBaseAddress(imageBuffer);
NSData *data = [NSData dataWithBytes:src_buff length:bytesPerRow * height];
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
return data;
}
No, you must remove annex b start codes and replace them with size values. Same format as MP4

How to upload in ios app extension

How to upload using AFNetworking in ios app extension?
apple's example uses NSURLSession, can you explain to me how this works?
- (void)didSelectPost {
NSExtensionItem *imageItem = [self.extensionContext.inputItems lastObject];
// Verify that we have a valid NSExtensionItem
if (!imageItem) {
return;
}
// Verify that we have a valid NSItemProvider
NSItemProvider *imageItemProvider = [[imageItem attachments] firstObject];
if (!imageItemProvider) {
return;
}
// Look for an image inside the NSItemProvider
if ([imageItemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) {
[imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(id item, NSError *error) {
if (item)
{
NSData *data = [NSData dataWithContentsOfURL:item];
[self method:data];
}
[self.extensionContext completeRequestReturningItems:nil completionHandler:nil];
}];
}
}
How do I upload this data using this method or using AFNetworking or using my app to upload this?
- (void)method:(NSData *)data
{
NSString *confName = #"com.example.photoblog.backgroundconfiguration";
NSURLSessionConfiguration *conf = [NSURLSessionConfiguration backgroundSessionConfiguration:confName];
NSURLSession *session = [NSURLSession sessionWithConfiguration:conf delegate:self delegateQueue:nil];
NSURLRequest *requeust = [self requestForExtensionItems];
NSURLSessionUploadTask *upload = [session uploadTaskWithStreamedRequeust:request];
[upload resume];
}
You should setting app group both Your extension and containing app,then config session like this
config.sharedContainerIdentifier = #"group.xxxxx";
You can refer more info by this tutorial
http://www.shinobicontrols.com/blog/posts/2014/07/21/ios8-day-by-day-day-2-sharing-extension

Capturing blank stills from a AVCaptureScreenInput?

I'm working on sampling the screen using AVCaptureScreenInput and outputting it using a AVCaptureVideoDataOutput, and it's not working. The images it does output are blank, but it appears like I'm doing everything right according to all the documentation I've read.
I've made sure I make the AVCaptureVideoDataOutput to something that could be read by a CGImage (kCVPixelFormatType_32BGRA). When I run this same code and have it output to a AVCaptureMovieFileOutput, the movie renders fine and everything looks good - but what I really want is a series of images.
#import "ScreenRecorder.h"
#import <QuartzCore/QuartzCore.h>
#interface ScreenRecorder() <AVCaptureFileOutputRecordingDelegate, AVCaptureVideoDataOutputSampleBufferDelegate> {
BOOL _isRecording;
#private
AVCaptureSession *_session;
AVCaptureOutput *_movieFileOutput;
AVCaptureStillImageOutput *_imageFileOutput;
NSUInteger _frameIndex;
NSTimer *_timer;
NSString *_outputDirectory;
}
#end
#implementation ScreenRecorder
- (BOOL)recordDisplayImages:(CGDirectDisplayID)displayId toURL:(NSURL *)fileURL windowBounds:(CGRect)windowBounds duration:(NSTimeInterval)duration {
if (_isRecording) {
return NO;
}
_frameIndex = 0;
// Create a capture session
_session = [[AVCaptureSession alloc] init];
// Set the session preset as you wish
_session.sessionPreset = AVCaptureSessionPresetHigh;
// Create a ScreenInput with the display and add it to the session
AVCaptureScreenInput *input = [[[AVCaptureScreenInput alloc] initWithDisplayID:displayId] autorelease];
if (!input) {
[_session release];
_session = nil;
return NO;
}
if ([_session canAddInput:input]) {
[_session addInput:input];
}
input.cropRect = windowBounds;
// Create a MovieFileOutput and add it to the session
_movieFileOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
[((AVCaptureVideoDataOutput *)_movieFileOutput) setVideoSettings:[NSDictionary dictionaryWithObjectsAndKeys:#(kCVPixelFormatType_32BGRA),kCVPixelBufferPixelFormatTypeKey, nil]];
// ((AVCaptureVideoDataOutput *)_movieFileOutput).alwaysDiscardsLateVideoFrames = YES;
if ([_session canAddOutput:_movieFileOutput])
[_session addOutput:_movieFileOutput];
// Start running the session
[_session startRunning];
// Delete any existing movie file first
if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]])
{
NSError *err;
if (![[NSFileManager defaultManager] removeItemAtPath:[fileURL path] error:&err])
{
NSLog(#"Error deleting existing movie %#",[err localizedDescription]);
}
}
_outputDirectory = [[fileURL path] retain];
[[NSFileManager defaultManager] createDirectoryAtPath:_outputDirectory withIntermediateDirectories:YES attributes:nil error:nil];
// Set the recording delegate to self
dispatch_queue_t queue = dispatch_queue_create("com.schaefer.lolz", 0);
[(AVCaptureVideoDataOutput *)_movieFileOutput setSampleBufferDelegate:self queue:queue];
//dispatch_release(queue);
if (0 != duration) {
_timer = [[NSTimer scheduledTimerWithTimeInterval:duration target:self selector:#selector(finishRecord:) userInfo:nil repeats:NO] retain];
}
_isRecording = YES;
return _isRecording;
}
- (void)dealloc
{
if (nil != _session) {
[_session stopRunning];
[_session release];
}
[_outputDirectory release];
_outputDirectory = nil;
[super dealloc];
}
- (void)stopRecording {
if (!_isRecording) {
return;
}
_isRecording = NO;
// Stop recording to the destination movie file
if ([_movieFileOutput isKindOfClass:[AVCaptureFileOutput class]]) {
[_movieFileOutput performSelector:#selector(stopRecording)];
}
[_session stopRunning];
[_session release];
_session = nil;
[_timer release];
_timer = nil;
}
-(void)finishRecord:(NSTimer *)timer
{
[self stopRecording];
}
//AVCaptureVideoDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(imageBuffer,0); // Lock the image buffer
uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); // Get information of the image
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGImageRef image = CGBitmapContextCreateImage(newContext);
CGContextRelease(newContext);
CGColorSpaceRelease(colorSpace);
_frameIndex++;
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
dispatch_async(dispatch_get_main_queue(), ^{
NSURL *URL = [NSURL fileURLWithPath:[_outputDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.jpg", (int)_frameIndex]]];
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((CFURLRef)URL, kUTTypeJPEG, 1, NULL);
CGImageDestinationAddImage(destination, image, nil);
if (!CGImageDestinationFinalize(destination)) {
NSLog(#"Failed to write image to %#", URL);
}
CFRelease(destination);
CFRelease(image);
});
}
#end
Your data isn't planar, so there is no base address for plane 0--there's no plane 0. (To be sure, you can check with CVPixelBufferIsPlanar.) You'll need CVPixelBufferGetBaseAddress to get a pointer to the first pixel. All the data will be interleaved.

didReceiveData only getting called once on resuming download

I have a problem which I could really do with some help with please
I am using NSURLconnection to download a large file (27MB). the code work fine when there is no network interruption. In order to allow for network issues and only partially downloaded file I have added code to check to see how much of the file is downloaded and then using a server request to download the missing portion.
The code works as it should IF I download part of file, stop the program running and then run again - the download then commences where it left off and i have complete file.
However if I hit the download button a second time without stopping the program then didReceiveData only gets called once and adds just 200KB to the file and it tells me file has been succesfully downloaded.
Help please - I have spent ages trying to figure out what I'm doing wrong.
Code below:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"Response code = %d",httpResponse.statusCode );
file = [NSFileHandle fileHandleForUpdatingAtPath:filename] ;// file is in .h
if (file) {
[file seekToEndOfFile];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (file) {
[file seekToEndOfFile];
NSLog(#"file is %#",file);
}
[self.file writeData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if([[NSFileManager defaultManager] fileExistsAtPath:filename])
{
[file closeFile];
file = nil;
theConnection = nil;
filename = nil;
theRequest = nil;
}
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[file closeFile];
file = nil;
theConnection = nil;
filename = nil;
}
- (IBAction)downloadFile:(id)sender {
filename = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:movie1]; // filename is in .h file
theRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:movieDownload1] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSUInteger downloadedBytes = 0;
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:filename]) {
NSError *error = nil;
NSDictionary *fileDictionary = [fm attributesOfItemAtPath:filename error:&error];
if (!error && fileDictionary)
downloadedBytes = [fileDictionary fileSize];
} else {
[fm createFileAtPath:filename contents:nil attributes:nil];
}
if (downloadedBytes > 0) {
NSString *requestRange = [NSString stringWithFormat:#"bytes=%d-",downloadedBytes];
[theRequest setValue:requestRange forHTTPHeaderField:#"Range"];
}
theConnection = nil;
theConnection = [NSURLConnection connectionWithRequest:theRequest delegate:self];
}
Instead of using seekToEndOfFile in didReceiveResponse and didReceiveData, you can try the following code snippet. It worked well for me.
- (id)initWithURL:(NSString *)downloadString
{
if (![super init])
return nil;
// Construct the URL to be downloaded
self.downloadURL = [[NSURL alloc]initWithString:downloadString];
self.downloadData = [[NSMutableData alloc] init];
self.downloadedFilename = [[self.downloadURL path] lastPathComponent];
[self downloadFile];
return self;
}
-(void) downloadFile
{
// set the filePath
docFolderPath = [NSHomeDirectory() stringByAppendingPathComponent: [NSString stringWithFormat: #"Documents/%#", self.downloadedFilename]];
self.downloadStream = [NSOutputStream outputStreamToFileAtPath:docFolderPath append:NO];
if (!self.downloadStream)
{
self.error = [NSError errorWithDomain:[NSBundle mainBundle].bundleIdentifier
code:-1
userInfo:#{#"message": #"Unable to create NSOutputStream", #"function" : #(__FUNCTION__), #"path" : self.downloadedFilename}];
return;
}
[self.downloadStream open];
self.downloadConnection = [[NSURLConnection alloc] initWithRequest:downloadRequest delegate:self];
[self.downloadConnection start];
}
//code snippet for the Resume functionality after your downloading gets paused/cancel
-(void) resumeInterruptedDownload
{
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:docFolderPath])
{
NSError *error = nil;
NSDictionary *fileDictionary = [fm attributesOfItemAtPath:docFolderPath
error:&error];
if (!error && fileDictionary)
self.downloadedBytes = [fileDictionary fileSize];
} else
{
[fm createFileAtPath:docFolderPath contents:nil attributes:nil];
}
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.downloadURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
// Define the bytes we wish to download.
if(self.downloadedBytes != 0)
{
NSString *range = [NSString stringWithFormat:#"bytes=%i-", self.downloadedBytes];
[request setValue:range forHTTPHeaderField:#"Range"];
}
// Data should immediately start downloading after the connection is created.
self.downloadConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE];
}
It worked perfectly for me in case of the large files in Mbs but for the small files in Kbs it sometimes fails. You don't have to try much in all these delegate methods of NSURLConnection. One thing you can do is that set the macros for each state i.e, cancel, pause, downloading,downloaded so that you can come to know that when do you want to resume the downloading. Also you can try the following http://nachbaur.com/blog/resuming-large-downloads-with-nsurlconnection.
I know it is too late to reply, but I just got into IOS. If you try this, please let me know whether it worked or not. Thanks :) :)

Automatic Lightwieight Migration for core data Problem

I'm having problem use Automatic Lightweight Migration Code in my App delegate !
I read all the apple's documentations about "Automatic Lightweight Migration" but after all I can't find my way to use the codes that is prepared to Automatic Lightweight Migration.
Recently, I just added some new Attribute to an Entity in my data model and I want to keep my old data.
my app delegate code is like this :
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
if (__persistentStoreCoordinator) {
return __persistentStoreCoordinator;
}
NSManagedObjectModel *mom = [self managedObjectModel];
if (!mom) {
NSLog(#"%#:%# No model to generate a store from", [self class], NSStringFromSelector(_cmd));
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
NSError *error = nil;
NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:[NSArray arrayWithObject:NSURLIsDirectoryKey] error:&error];
if (!properties) {
BOOL ok = NO;
if ([error code] == NSFileReadNoSuchFileError) {
ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
}
if (!ok) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
else {
if ([[properties objectForKey:NSURLIsDirectoryKey] boolValue] != YES) {
// Customize and localize this error.
NSString *failureDescription = [NSString stringWithFormat:#"Expected a folder to store application data, found a file (%#).", [applicationFilesDirectory path]];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:101 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:#"FinancingPro.storedata"];
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
[[NSApplication sharedApplication] presentError:error];
[__persistentStoreCoordinator release], __persistentStoreCoordinator = nil;
return nil;
}
return __persistentStoreCoordinator;
Now I don't know how to change this code to have Automatic Lightweight Migration !
Please Note that my db is NOT SQLlite.
You need to set the options dictionary with the NSInferMappingModelAutomaticallyOption key here:
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
By passing a nil value for options you are telling the store to ignore any migration.

Resources