How to upload in ios app extension - ios8

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

Related

Inside nsurlsession and background transfer

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.

Facebook publish_actions deprecated

What can I do to use as an alternative to publish_actions? I have a photo app that lets users share their photos to their profile. Currently it uses logInWithPublishPermissions:#[#"publish_actions"] and then shares the files through FBSDKSharePhoto. But after August 1 this will be removed. Any thoughts? This is my current code but soon it won't work.
-(IBAction)loginToFacebook:(id)sender
{
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
login.loginBehavior = FBSDKLoginBehaviorWeb;
[login logInWithPublishPermissions:#[#"publish_actions"]
fromViewController:self
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
NSLog(#"Process error");
} else if (result.isCancelled) {
NSLog(#"Cancelled");
nonpressTimer = [NSTimer scheduledTimerWithTimeInterval:15.0 target:self selector:#selector(timeout) userInfo:nil repeats:NO];
} else {
NSLog(#"Logged in");
[self shareSegmentWithFacebookComposer];
}
}];
}
- (void)shareBoothOnFacebook {
NSUserDefaults *defaults2 = [NSUserDefaults standardUserDefaults];
NSData *data6 = [defaults2 objectForKey:#"layoutphoto"];
UIImage *myImage = [NSKeyedUnarchiver unarchiveObjectWithData:data6];
NSMutableArray *photos = [[NSMutableArray alloc] init];
for (int i = 0; i < [self.arrSlidshowImg count]; i++) {
UIImage *image = [UIImage imageWithData:[_arrSlidshowImg objectAtIndex:i]];
FBSDKSharePhoto *photo = [[FBSDKSharePhoto alloc] init];
photo.image = image;
photo.userGenerated = YES;
[photos addObject:photo];
}
FBSDKSharePhotoContent *content = [[FBSDKSharePhotoContent alloc] init];
content.photos = [photos copy];
[FBSDKShareAPI shareWithContent:content delegate:nil];
}
}
You'll need to switch to using Facebook's iOS sharing APIs which require that the Facebook Messenger app is installed. See their documentation on sharing.
You might also want to investigate the use of the iOS share sheet but that also requires the Facebook Messenger app to be installed to enable sharing to Facebook.

How to make an OSX app use a sqlite database in the bundle?

I have an app that is intended to ship with a pre-populated sqlite database. I will include that database on the bundle. How do I modify the delegate to use that database instead of generating a new one?
This is my first serious app on OSX.
The current methods on the delegate is like this right now:
// Returns the directory the application uses to store the Core Data store file. This code uses a directory named "com.addfone.LoteriaMac" in the user's Application Support directory.
- (NSURL *)applicationFilesDirectory
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *appSupportURL = [[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
return [appSupportURL URLByAppendingPathComponent:#"com.myApp.BotMax"];
}
// Creates if necessary and returns the managed object model for the application.
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"BotMax" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
// Returns the persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. (The directory for the store is created, if necessary.)
- (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:#[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[NSURLIsDirectoryKey] boolValue]) {
// 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:#"BotMax.storedata"];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![coordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
_persistentStoreCoordinator = coordinator;
return _persistentStoreCoordinator;
}
The database file is named like BotMax.sqlite
thanks.
The simplest way to ship a pre-filled SQLite DB would be to copy the .sqlite file into the Application Support folder before the persistentStoreCoordinator is initialised.
The below code just copies the bundled DB. There are some things to consider before using that approach:
This simple approach only copies the initial DB once.
It does not consider merging or migrating existing stores (e.g when you ship an update with a changed model)
Core Data is using a new journal_mode since iOS 7 & OS X 10.9. This mode creates an additional file when saving the DB. (*.sqlite-wal). If the program that creates your pre-filled SQLite file was linked against OS X 10.9/iOS 7 or later SDKs, you'll also have to ship the .sqlite-wal file.
Alternatively, you can disable the WAL journal mode in the app that creates your pre-filled DB by passing #{ NSSQLitePragmasOption : #{ #"journal_mode" : #"DELETE" } }; as option dict to addPersistentStoreWithType
The following code is based on the standard Xcode template for non-document based Core Data apps:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator) {
return _persistentStoreCoordinator;
}
NSString* dbFilename = #"BotMax.sqlite";
NSURL* initialDBURL = [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:dbFilename];
NSURL* workingDBURL = [[self applicationFilesDirectory] URLByAppendingPathComponent:dbFilename];
NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* applicationFilesDirectory = [self applicationFilesDirectory];
NSError* error = nil;
if(![fileManager fileExistsAtPath:[workingDBURL path]])
{
BOOL result = [fileManager copyItemAtURL:initialDBURL toURL:workingDBURL error:&error];
if(!result)
{
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
...
}
objc.io issue #4 has a section about shipping pre-filled Core Data stores: http://www.objc.io/issue-4/importing-large-data-sets-into-core-data.html

How can i get name of the picked image from photolibrary

In my app i have to use photos from photolibrary ,i can get photo from library and can use that in my app but i'm unable to get the name of selected image
I'm using this
//accessing photo library like this
-(IBAction)btnAttachFileClicked:(id)sender
{
if([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypePhotoLibrary])
{
UIImagePickerController *picker= [[UIImagePickerController alloc]init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentModalViewController:picker animated:YES];
[picker release];
}
}
//to get name of the selected image i'm using this
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info;
{
NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL];
__block NSString *fileName = nil;
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
[library assetForURL:assetURL resultBlock:^(ALAsset *asset) {
fileName = [asset.defaultRepresentation filename];
} failureBlock:nil];
}
but i cannot get image name when testing on simulator from this code,i imported AssetsLibrary framework ,is there any modifications to do please help me
i'm getting assetUrl as "assets-library://asset/asset.PNG?id=8542B1A1-7EAD-4CCB-A85B-8450826B49FA&ext=PNG"

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 :) :)

Resources