Below is a code that I use to share images within my "ShareViewController.m".
NSExtensionItem *item = [self.extensionContext.inputItems objectAtIndex:i];
NSItemProvider *itemProvider = item.attachments.firstObject;
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeURL]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeURL options:nil completionHandler:^(NSData *data, NSError *error) {
NSLog(#"%#", data);
// the rest of uploading script goes here
}];
}
It all works fine if I share an image from WhatsApp. But it doesn't work if I want to share an image from Photo Library or from Facebook Messenger.
Does anyone know what the problem might be?
Thanks
Here is how I solved it. I got rid of (NSString *)kUTTypeURL] and added itemProvider.registeredTypeIdentifiers to get array with all the available type identifiers. Then I'm just using the first one available as registeredTypeIdentifiers.firstObject.
Also, very important, NSData *data got changed to id<NSSecureCoding> item which makes it a bit different to get the NSData from it. That's important especially when sharing images from Messenger - they have type identifier "public.image" rather than "public.jpeg" or "public.url" like in Photos library or WhatsApp.
NSExtensionItem *item = [self.extensionContext.inputItems objectAtIndex:i];
NSItemProvider *itemProvider = item.attachments.firstObject;
// get type of file extention (jpeg, file, url, png ...)
NSArray *registeredTypeIdentifiers = itemProvider.registeredTypeIdentifiers;
if ([itemProvider hasItemConformingToTypeIdentifier:registeredTypeIdentifiers.firstObject) {
[itemProvider loadItemForTypeIdentifier:registeredTypeIdentifiers.firstObject options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSData *imgData;
if([(NSObject*)item isKindOfClass:[NSURL class]]) {
imgData = [NSData dataWithContentsOfURL:(NSURL*)item];
}
if([(NSObject*)item isKindOfClass:[UIImage class]]) {
imgData = UIImagePNGRepresentation((UIImage*)item);
}
// the rest of uploading script goes here
}];
}
Related
I have the following issue:
I am trying to share an image from Messenger (I have implemented a share-extension) to my application and I am using this code to load the image:
[provider loadItemForTypeIdentifier:identifier options:nil completionHandler:^(id<NSSecureCoding> _Nullable item, NSError * _Null_unspecified error) {
if (item) {
NSData *data = [NSData dataWithContentsOfURL:(NSURL*)item];
if (isImage)
[images addObject:[UIImage imageWithData:data]];
else
[images addObjectsFromArray:[self createImageFromPDFWithData:data]];
[dataToUpload addObject:data];
}
count++;
}];
Where identifier is (NSString*) kUTTypeImage. And for some reason this is causing a leak or something like that - it's entering didReceiveMemoryWarning. Note that this is happening only if I try to share the image using Messenger, if I try to share it from the Photo app, it's working fine. Can somebody give me an idea what to do in order to fix this?
I wanna share a PDF file from APP1 by the function of share extension,
but I just got a URL path from method of loadItemForTypeIdentifier,which is a path of APP1 sandbox, I can not get the PDF file by URL,so how can I get the PDF or Doc through the method loadItemForTypeIdentifier?
I tried many types but I can only get NSURL.
If you want share pdf to other app you don't need a share extention.you can do it with documentViewController if to a special app you use activityviewcontroller
When I assign item to NSData, I get a NSData.
[itemProvider loadItemForTypeIdentifier:fileType options:nil completionHandler:^(NSData *item, NSError * _Null_unspecified error)
{
NSData *data = (NSData *)item;
if ([data isKindOfClass:[NSData class]]) {
weakSelf.dataToSave = data;
}
blockExcuteCount ++;
if (blockExcuteCount == 2 && completion) {
completion();
}
}];
I am trying to download only image and text(probably HTML string) of a Evernote's note in my iOS app. I have successfully downloaded image from a note . But I did not find any method or process which help me to get text which are written on the note . I have used
ENSDK.framework
-(void)findAllNotes {
NSLog(#"finding all notes..");
[self.session findNotesWithSearch:nil
inNotebook:nil
orScope:ENSessionSearchScopeAll
sortOrder:ENSessionSortOrderNormal
maxResults:255
completion:^(NSArray* findNotesResults,
NSError* findNotesError) {
if (findNotesError) {
[self.session unauthenticate];
NSAssert(NO, #"Could not find notes with error %#", findNotesError);
} else {
[self processFindNotesResults:findNotesResults];
}
}];
}
- (void)processFindNotesResults:(NSArray*)results {
NSParameterAssert(results);
NSLog(#"processing find notes results..");
for (ENSessionFindNotesResult* result in results) {
[self.session downloadNote:result.noteRef
progress:NULL
completion:^(ENNote* note,
NSError* downloadNoteError) {
NSAssert(!downloadNoteError, #"Could not download note with error %#",
downloadNoteError);
[self getDataFromNote:note];
}];
}
}
-(void)getDataFromNote:(ENNote*)note {
for (ENResource* resource in note.resources) {
if ([resource.mimeType hasPrefix:#"image"]) {
UIImage* image = [[UIImage alloc] initWithData:resource.data];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *docs = [paths objectAtIndex:0];
NSString* path = [docs stringByAppendingFormat:#"/image1.jpg"];
NSData* imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, .8)];
NSError *writeError = nil;
if(![imageData writeToFile:path options:NSDataWritingAtomic error:&writeError]) {
NSLog(#"%#: Error saving image: %#", [self class], [writeError localizedDescription]);
}
}
}
}
The content of the note is available to you in the content property of your variable note; i.e. it's in the content property of an ENNote object.
Also note that in addition to accessing the content directly, the Evernote iOS SDK also includes a special method that makes it easy to display a note's content in a UIWebView:
We've made this easy-- rather than serializing it to HTML and fussing with attached image resources, we've provided a method to generate a single Safari "web archive" from the note; this is a bundled data type which UIWebView natively knows how to load directly.
I am trying to implement share extension for my app. Its working good in safari browser and youtube app (i.e) when i share from these apps i get the public.url which is the url to be shared.
When i tried the same in chrome it was not showing the extension. When i added the NSExtensionActivationSupportsText under the NSExtensionActivationRule to true its started showing. But when i try to share the contents i am unable to fetch the URL String which is to be shared. I get only contentText.
I have followed the approach shown in this link https://stackoverflow.com/a/31942744/6199038.
I have also added the demoProcessor.js
var MyPreprocessor = function() {};
MyPreprocessor.prototype = {
run: function(arguments) {
arguments.completionFunction({"URL": document.URL, "pageSource": document.documentElement.outerHTML, "title": document.title, "selection": window.getSelection().toString()});
}
};
var ExtensionPreprocessingJS = new MyPreprocessor;
I am using SLComposeServiceViewController
In my ShareViewController.m i am trying to get the data as shown below,
for safari i used this which is working fine
NSExtensionItem *item = self.extensionContext.inputItems.firstObject;
NSItemProvider *itemProvider = [[item.userInfo valueForKey:NSExtensionItemAttachmentsKey] objectAtIndex:0];
if ([itemProvider hasItemConformingToTypeIdentifier:#"public.url"]) {
[itemProvider loadItemForTypeIdentifier:#"public.url" options:nil completionHandler:^(NSURL *url, NSError *error) {
urlString = url.absoluteString;
NSLog(#"urlString %#",urlString);
}];
}
Then i modified my code to this to get the URL from chrome, Which is not working.
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypePropertyList]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypePropertyList options:nil completionHandler:^(NSDictionary *jsDict, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *jsPreprocessingResults = jsDict[NSExtensionJavaScriptPreprocessingResultsKey];
NSString *selectedText = jsPreprocessingResults[#"selection"];
NSString *pageTitle = jsPreprocessingResults[#"title"];
NSString *URL = jsPreprocessingResults[#"URL"];
NSLog(#"selectedText %#",selectedText);
NSLog(#"pageTitle %#",pageTitle);
NSLog(#"URL %#",URL);
});
}];
break;
}
}
}
PLEASE ADVICE
I have solved it myself. As it was not entering inside the "loadItemForTypeIdentifier" method. So i had modified my method to the below code
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
NSString *URLinPlainText = [item.attributedContentText string];
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeURL]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeURL options:nil completionHandler:^(NSURL *url, NSError *error) {
urlString = url.absoluteString;
NSLog(#"<< URL >> %#",urlString);
return;
}];
}
else if (URLinPlainText) {
// In Some app i got the URL in a Plain text mode
if([URLinPlainText containsString:#"http"]){
urlString = [sharedPlainText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"<< URL >> %#",urlString);
return;
}
}
}
}
And also i have removed the demoProcessor.js.
Now i am able to get the URL from almost all the News Apps, Browsers like (Chrome, firefox, safari) and in some of the apps i am getting the shared url in the form of plain text which is then i had to remove the empty spaces and convert it to NSString.
According to my understanding the demoprocessor.js is used if user wants to access the url page properties for ex(title, baseURL, og:image, og:description etc...) which works only for safari browser.
I am Writing an app which has share extension to save selected photo to my app' local storage from iphone photo gallery.
NSData WriteToFile returns YES but I couldn't find the stored file into the directory in of which I gave path while writing.
So, in short NSData WriteToFile fails to save a photo at given path.
Below is my code.
- (IBAction)acceptButtonTapped:(id)sender
{
__block UIImage *photo;
for (NSExtensionItem *item in self.extensionContext.inputItems)
{
for (NSItemProvider *itemProvider in item.attachments)
{
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage])
{
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(UIImage *image, NSError *error) {
if(image)
{
dispatch_async(dispatch_get_main_queue(), ^{
photo = image;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy_MM_dd_hh_mm_ss"];
NSString *fileName;
fileName = [NSString stringWithFormat:#"%#.jpeg",[formatter stringFromDate:[NSDate date]]];
dataPath = [dataPath stringByAppendingPathComponent:fileName];
NSData * imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0)];
BOOL isdone = [imageData writeToFile:dataPath atomically:NO];
NSLog(#"%u", isdone);
});
}
}];
break;
}
}
}
[self.extensionContext completeRequestReturningItems:#[] completionHandler:nil];
}
Any Help would be much appreciable.
Thank you.
If you're trying to access the Document directory from the share extension, NO you can't do that. Share extension or other widgets are separate application from their containing app and therefore have their own sandbox. So you will need to use App Groups to share files.
Application groups are primarily targeted for extensions, more specifically, for widgets.
NSFileManager has a method on it containerURLForSecurityApplicationGroupIdentifier: where you can pass in the identifier you created when turning on App Groups for your apps
NSURL *containerURL = [[NSFileManager defaultManager]
containerURLForSecurityApplicationGroupIdentifier:#"group.com.company.app"];
You can save the files to this location, because you can access the shared application groups from both extension and host app.
You're modifying dataPath on each pass through the loop, appending another filename to it. That will create an ever-growing series of badly formed paths that contain all the filenames.
Don't do that. Create a new local variable filePath, and construct a filename into filePath using
filePath = [docsPath stringByAppendingPathComponent: filename];
Log your path and LOOK AT IT. When your program doesn't behave as expected, don't trust any of your assumptions, because one or more of them may be wrong.