I have created a very simple app to download a text file from my web server. I have this working perfectly with NSURLConnection, but am trying to migrate over to NSURLSession instead.
The issue I am having is that none of the delegate methods are being called.
My server is password protected so I need to use the basic http authentication to access the file, but when the didReceiveChallenge method is never called.
The line of code [getFileTask resume] seems to have no effect on anything.
My setup is as follows:
#interface ViewController : UIViewController <NSURLSessionDelegate, NSURLSessionDownloadDelegate, NSURLSessionTaskDelegate>
{
NSURLSession *session;
}
The following method is called from viewDidLoad:
-(void)setUpTheNetworking
{
NSString *fileURL = #"www.mywebsite.com/utility/file.txt";
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.allowsCellularAccess = YES;
sessionConfig.timeoutIntervalForRequest = 10;
sessionConfig.timeoutIntervalForResource = 10;
sessionConfig.HTTPMaximumConnectionsPerHost = 1;
session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *getFileTask = [session downloadTaskWithURL:[NSURL URLWithString:fileURL]];
[getFileTask resume];
}
The delegate methods I have implemented are:
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
NSLog(#"Here we go");
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
NSLog(#"Here we go");
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSLog(#"Here we go");
}
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
if (challenge.previousFailureCount == 0)
{
NSURLCredentialPersistence persistence = NSURLCredentialPersistenceForSession;
NSURLCredential *credential = [NSURLCredential credentialWithUser:user password:#password persistence:persistence];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}
else
{
// handle the fact that the previous attempt failed
NSLog(#"%s: challenge.error = %#", __FUNCTION__, challenge.error);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
{
if (challenge.previousFailureCount == 0)
{
NSURLCredential *credential = [NSURLCredential credentialWithUser:user password:password persistence:NSURLCredentialPersistenceForSession];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}
else
{
NSLog(#"%s; challenge.error = %#", __FUNCTION__, challenge.error);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
}
Thanks!
SOLVED!
The following line of code was the culprit:
NSString *fileURL = #"www.mywebsite.com/utility/file.txt";
Turns out it needed http:// in there as well, so this one works
NSString *fileURL = #"http://www.mywebsite.com/utility/file.txt";
It still seems weird to me that it just didn't work. I would have expected an error to popup.
Related
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.
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
My app crashes when I try to perform sent or cancel button actions in MFMailComposeViewController.
here is my code.
I have imported message framework and added delegates in .h file.
NSString *emailTitle = #"Test Email";
NSString *messageBody = #"Welcome Guest";
NSArray *recipentsArray = [[NSArray alloc]initWithObjects:#"xxx#gmail.com", nil];
[[UINavigationBar appearance] setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundColor:nil];
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self;
[mc setSubject:emailTitle];
[mc setMessageBody:messageBody isHTML:NO];
[mc setToRecipients:recipentsArray];
[self presentViewController:mc animated:YES completion:NULL];(void) mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
switch (result)
{
case MFMailComposeResultCancelled:
NSLog(#"Mail cancelled");
break;
case MFMailComposeResultSaved:
NSLog(#"Mail saved");
break;
case MFMailComposeResultSent:
NSLog(#"Mail sent");
break;
case MFMailComposeResultFailed:
NSLog(#"Mail sent failure: %#", [error localizedDescription]);
break;
default:
break;
}
// Close the Mail Interface
[self dismissViewControllerAnimated:YES completion:NULL];
}
This is how my mf mail composer looks like :----
Just make the MFMailComposeViewController *mc in global.
I mean declare it outside this method. It's crashing because at the end of method Your mc gets deallocated.
Interface
#interface Demo()
{
MFMailComposeViewController *mc;
}
#end
Implementation
-(void)ShareViaEmail {
objMailComposeController = [[MFMailComposeViewController alloc] init];
objMailComposeController.mailComposeDelegate = self;
if ([MFMailComposeViewController canSendMail]) {
[objMailComposeController setSubject:#"Hello"];
[objMailComposeController setMessageBody:#"This is Body of Message" isHTML:NO];
NSData *ImageData = [NSData dataWithContentsOfFile:self.aStrSourceName];
NSString *mimeType;
if ([[self CheckExtensionOfURL:self.aStrSourceName]isEqualToString:#"Image"]) {
mimeType = #"image/png";
}
else if ([[self CheckExtensionOfURL:self.aStrSourceName]isEqualToString:#"PDF"]) {
mimeType = #"application/pdf";
} else if ([[self CheckExtensionOfURL:self.aStrSourceName]isEqualToString:#"Audio"]) {
mimeType = #"audio/mpeg";
} else if ([[self CheckExtensionOfURL:self.aStrSourceName]isEqualToString:#"Video"]) {
mimeType = #"video/mp4";
}
[objMailComposeController addAttachmentData:ImageData mimeType:mimeType fileName:#"attachement"];
}
[self.navigationController presentViewController:objMailComposeController animated:YES completion:Nil];
}
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
switch (result) {
case MFMailComposeResultCancelled:
[self ShowAlertView:kMsgMailCancelled];
break;
case MFMailComposeResultFailed:
[self ShowAlertView:kMsgMailFailed];
break;
case MFMailComposeResultSaved:
[self ShowAlertView:kMsgMailSaved];
break;
case MFMailComposeResultSent:
[self ShowAlertView:kMsgSent];
break;
default:
break;
}
// CHECK OUT THIS, THIS MIGHT BE IN YOUR CASE
[self.navigationController dismissViewControllerAnimated:controller completion:nil];
}
I'm trying to develop a simple OS X application that should do the following:
Read a file to get username and password
Obtain Mac serial number from server
Connect to a remote service to login with username, password and Mac serial number
If the user is authorized to be connected with this service it should run an infinite
Infinite loop should call a service that connect to another service to get news
If there are news it should send a local notification
This application should work without GUI, so I'm using an Xcode command line tool. The application is organized so:
main.m read file with username and password and obtain the Mac serial number
Connection.m should connect to the login service and run the checkNews service
CheckNews.m should connect to the news service and if there are a news should run a class to send local notification
NotificationCenterDelegate.m should simply send the local notification with news inside
My problem is that if I run the app it doesn't connect to the login service, because it doesn't calls the NSConnectionDelegate methods. I searched on the web and I found that the connection should be in the main thread, I tested it by using the following code: NSLog(#"Is%# main thread", ([NSThread isMainThread] ? #"YES" : #" NOT")); and it returned me that the connection is in the main thread, so I don't know why it doesn't run the NSConnectionDelegate methods. If you need I can post the code of my Connection.m class to understand why it doesn't working well. I hope you can help me to fix that issue.
Connection.m
#import "Connection.h"
#import "CheckNews.h"
#interface Connection() {
NSMutableData *responseData;
}
#property(nonatomic,strong)NSString *fakeBundleIdentifier;
#property BOOL installNSBundleHook;
#end
#implementation Connection
- (id)initWithFakeBundle:(NSString *)fakeBundleIdentifier andInstallNSbundleHook:(BOOL)installNSBundleHook {
if (self) {
self.fakeBundleIdentifier = fakeBundleIdentifier;
self.installNSBundleHook = installNSBundleHook;
}
return self;
}
- (id)sendRequestToURL:(NSString *)urlString withMethod:(NSString *)method withUsername:(NSString *)username withPassword:(NSString *)password andSerialNumber:(NSString *)serialNumber {
NSURL *finalURL;
if ([method isEqualToString:#"POST"]) {
finalURL = [NSURL URLWithString:urlString];
} else {
NSLog(#"Metodo no previsto");
}
NSString *post = [NSString stringWithFormat:#"username=%#&password=%#&serialNumber=%#", username, password, serialNumber];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)postData.length];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]init];
[request setURL:finalURL];
[request setHTTPMethod:method];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
if (connection) {
[connection start];
}
return connection;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
responseData = [[NSMutableData alloc]init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"%#", error);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSDictionary *json;
NSError *error;
json = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&error];
if ([self loginWithSuccessFromJson:json]) {
[self checkNews];
}
}
- (BOOL)loginWithSuccessFromJson:(NSDictionary *)json {
int success = [[json objectForKey:#"success"]intValue];
if (success == 1) {
return YES;
} else {
return NO;
}
}
- (void) checkNews {
CheckNews *checkNews = [[CheckNews alloc]initWithFakeBundle:self.fakeBundleIdentifier andInstallNSbundleHook:self.installNSBundleHook];
for(;;) {
[checkNews sendRequestToURL:#"" withMethod:#"GET"];
}
}
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