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.
Related
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
}];
}
This is my first time that I am using Baidu API. I am having problem implementing Baidu places auto-complete API in my project. I am using the Baidu developers link to http://lbsyun.baidu.com/index.php?title=iossdk.
someone please give me to some tutorial in this regard?
i am following this tutorial. link
but in this tutorial i can not receive json file, give me a error
{ "Status": 102, "message": "MCODE parameter is not present, mobile
type mcode required parameter"}
Seems you should use the POI Search module of BaiduMapKit.Try this.
BMKCitySearchOption *citySearchOption = [[BMKCitySearchOption alloc]init];
citySearchOption.pageIndex = curPage;//here is the page index , you can set it to 0
citySearchOption.pageCapacity = 10;
citySearchOption.city= #"上海";//here is the city where you want to search the road
citySearchOption.keyword = #"淮海路";//here is the road name or someplace name you want to search
BOOL flag = [_poisearch poiSearchInCity:citySearchOption];
if(flag) {
_nextPageButton.enabled = true;
NSLog(#"success");
}
else {
_nextPageButton.enabled = false;
NSLog(#"fail");
}
Implement AutoComplete In Baidu Map using Baidu Web API
- (void)viewDidLoad {
BaseString = #"http://api.map.baidu.com/place/v2/suggestion?query=";
ak = #"56dIEtBAp1CU7u8ZMcq8DyUH2mVsn38x"; mcode = #"com.baidu.Baidu-Map-Demo";
regionkey = #"中国";
PathString = #"http://api.map.baidu.com/direction/v2/transit?origin=";
self .mapView .userTrackingMode = BMKUserTrackingModeFollow;
// 2. Set the map type self.mapView.mapType = BMKMapTypeStandard;
// 3. Set Agent self.mapView.delegate = self;
[super viewDidLoad];
mapView.frame = CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height);
mapView.delegate = self; anotation = [[BMKPointAnnotation alloc]init];
destination = [[BMKPointAnnotation alloc]init];
PathUrl = [[NSURL alloc]init];
finalPathArray = [[NSMutableArray alloc]init];
session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
downloadURL = [[NSURL alloc]init];
path = [[BMKPolyline alloc]init];
flag = 0;
}
-(void)GetSuggestion: (NSString *)query {
NSString *stringUrl = [NSString stringWithFormat:#"%#%#&page_size=10&page_num=0&scope=1®ion=%#&output=json&ak=%#&mcode=%#",BaseString,query,regionkey,ak,mcode]; stringUrl = [stringUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
downloadURL = [NSURL URLWithString:stringUrl];
if (downloadURL != nil) {
if (DownloadTask != nil) {
[DownloadTask suspend];
}
DownloadTask = [session dataTaskWithURL:downloadURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary *AutocompleteData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
resultArray = AutocompleteData[#"result"];
tbl_result.hidden = NO;
[tbl_result reloadData];
}];
[DownloadTask resume];
}
}
MCODE parameter means your bundle id must spacify bundle id with urlFor example write url for autocomplete FOR Autocomplete use this function
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.
In my host App I am downloading custom emojis images folder after unzipping successfully saving by below url.
NSURL* shareContainerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.company.app.PushServiceExtn"];
And without any issue whenever user tapping on emojis icon all the custom emojis shows in grid in place of keyboard by shareContainerURL.
I have created PushNotification Service Extension where I need to show the custom emojis image by fetching emoji name from payload whenever push comes. using below code.
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
NSDictionary* mediaAttachment = [self.bestAttemptContent.userInfo objectForKey:#"media-attachment"];
NSString* attachType = [mediaAttachment objectForKey:#"attachType"];
if ([attachType isEqualToString:#"emoji"]) {
NSString* strEmojiURL = [mediaAttachment objectForKey:#"url"];
self.bestAttemptContent.title = strEmojiURL;
NSString* emojiName = [[strEmojiURL stringByRemovingPercentEncoding] lastPathComponent];
NSString* strUnpresseedEmojiPath = [self getFullPath:#"emoji/Pressed"];
NSString* strImagePath = [NSString stringWithFormat:#"%#/%# Pressed.png",strUnpresseedEmojiPath, emojiName];
NSURL* fileURL = [NSURL fileURLWithPath:strImagePath];
NSData *imageData = [NSData dataWithContentsOfURL:fileURL];
UIImage *image = [UIImage imageWithData:imageData];
if (image) {
NSError* error;
// CGRect rect = CGRectMake(0,0,50,50);
// #{UNNotificationAttachmentOptionsThumbnailClippingRectKey:(__bridge NSDictionary*)CGRectCreateDictionaryRepresentation(rect)} option dict;
UNNotificationAttachment * attachement = [UNNotificationAttachment attachmentWithIdentifier:strImagePath.lastPathComponent URL:fileURL options:nil error:&error];
if (error == nil) {
self.bestAttemptContent.attachments = #[attachement];
}
}
}
self.contentHandler(self.bestAttemptContent);
}
- (NSString *)getFullPath:(NSString *)file {
NSURL* shareContainerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.company.app.PushServiceExtn"];
return [shareContainerURL.path stringByAppendingPathComponent: file];
}
I am always getting valid url but second time I get image nil but first time of every image it works. Couldn't get the root cause. Any help would appreciated.
Below is the error that occurred second time for every image.
2016-10-27 17:34:59.081026 pushNotificationServiceExtension[651:34632] Attachement Error = Error Domain=UNErrorDomain Code=100 "Invalid attachment file URL" UserInfo={NSLocalizedDescription=Invalid attachment file URL}
Also please let me know how to view App Group shared container, Couldn't find way to view the files contained inside.
*Update = * File is getting deleted after showing in push notification.
From apple "UNNotificationAttachment Once validated, attached files are moved into the attachment data store so that they can be accessed by the appropriate processes. Attachments located inside an app’s bundle are copied instead of moved."
So I copy my emoji image to duplicate URL and assign it to UNNotificationAttachment.
if (imageFileURL) {
NSURL* duplicateImageURL = [self getFullPath:#"EmojiAttachment"];
if (![fileManager fileExistsAtPath:duplicateImageURL.path]) {
[fileManager createDirectoryAtPath:duplicateImageURL.path withIntermediateDirectories:NO attributes:nil error:&error];
}
emojiName = [NSString stringWithFormat:#"%# Unpressed.png", emojiName];
duplicateImageURL = [duplicateImageURL URLByAppendingPathComponent:emojiName];
[[NSFileManager defaultManager]copyItemAtURL:imageFileURL toURL:duplicateImageURL error:&error];
UNNotificationAttachment * attachement = [UNNotificationAttachment attachmentWithIdentifier:emojiName URL:[duplicateImageURL filePathURL] options:nil error:&error];
if (error == nil) {
self.bestAttemptContent.attachments = #[attachement];
}
else{
NSLog(#"Attachement Error = %#",error);
}
}
In the "Live Control Room" of a YouTube Live broadcast, I can see a "Stream Status" view which shows me details of the video being sent to YouTube's RTMP endpoint.
I hit the liveStreams endpoint to get the "status" of the stream, but that only returns active, meaning that the video stream is being successfully sent to YouTube's RTMP endpoint, but no information about video data or quality.
Is this information exposed somewhere in the API? Can I also see additional details about the video, such as the bitrate, fps, etc. being sent to YouTube so I can verify my encoder is working correctly? Or does that check need to be done on the client-side and check the video right after it leaves the encoder before hitting the RTMP endpoint. I'm writing an iOS application, so using the "Live Control Room" on the web isn't a viable solution for me.
Here's what I'm doing on the broadcasting side to check the liveStream status:
- (void)checkStreamStatus {
[self getRequestWithURL:[NSString stringWithFormat:#"https://www.googleapis.com/youtube/v3/liveStreams?part=id,snippet,cdn,status&id=%#", self.liveStreamId] andBlock:^(NSDictionary *responseDict) {
NSLog(#"response: %#", responseDict);
// if stream is active, youtube is receiving data from our encoder
// ready to transition to live
NSArray *items = [responseDict objectForKey:#"items"];
NSDictionary *itemsDict = [items firstObject];
NSDictionary *statusDict = [itemsDict objectForKey:#"status"];
if ([[statusDict objectForKey:#"streamStatus"] isEqualToString:#"active"]) {
NSLog(#"stream ready to go live!");
if (!userIsLive) {
[self goLive]; // transition the broadcastStatus from "testing" to "live"
}
} else {
NSLog(#"keep refreshing, broadcast object not ready on youtube's end");
}
}];
}
getRequestWithURL is just a generic method I created to do GET requests:
- (void)getRequestWithURL:(NSString *)urlStr andBlock:(void (^)(NSDictionary *responseDict))completion {
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
[request addValue:[NSString stringWithFormat:#"Bearer %#", [[NSUserDefaults standardUserDefaults] objectForKey:#"accessToken"]] forHTTPHeaderField:#"Authorization"];
[request setHTTPMethod:#"GET"];
// Set the content type
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
[self parseJSONwithData:data andBlock:completion];
}];
}
- (void)parseJSONwithData:(NSData *)data andBlock:(void (^)(NSDictionary * responseDict))completion {
NSError *error = nil;
NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
if (error) {
NSLog(#"error: %#", [error localizedDescription]);
}
completion(responseDict);
}
Here's what I'm doing on the consumer side to check the video quality:
I am using the YTPlayerView library from Google.
- (void)notifyDelegateOfYouTubeCallbackUrl: (NSURL *) url {
NSString *action = url.host;
// We know the query can only be of the format http://ytplayer?data=SOMEVALUE,
// so we parse out the value.
NSString *query = url.query;
NSString *data;
if (query) {
data = [query componentsSeparatedByString:#"="][4]; // data here is auto, meaning auto quality
}
...
if ([action isEqual:kYTPlayerCallbackOnPlaybackQualityChange]) {
if ([self.delegate respondsToSelector:#selector(playerView:didChangeToQuality:)]) {
YTPlaybackQuality quality = [YTPlayerView playbackQualityForString:data];
[self.delegate playerView:self didChangeToQuality:quality];
}
...
}
But the quality "auto" doesn't seem to be a supported quality constant in this library:
// Constants representing playback quality.
NSString static *const kYTPlaybackQualitySmallQuality = #"small";
NSString static *const kYTPlaybackQualityMediumQuality = #"medium";
NSString static *const kYTPlaybackQualityLargeQuality = #"large";
NSString static *const kYTPlaybackQualityHD720Quality = #"hd720";
NSString static *const kYTPlaybackQualityHD1080Quality = #"hd1080";
NSString static *const kYTPlaybackQualityHighResQuality = #"highres";
NSString static *const kYTPlaybackQualityUnknownQuality = #"unknown";
...
#implementation YTPlayerView
...
/**
* Convert a quality value from NSString to the typed enum value.
*
* #param qualityString A string representing playback quality. Ex: "small", "medium", "hd1080".
* #return An enum value representing the playback quality.
*/
+ (YTPlaybackQuality)playbackQualityForString:(NSString *)qualityString {
YTPlaybackQuality quality = kYTPlaybackQualityUnknown;
if ([qualityString isEqualToString:kYTPlaybackQualitySmallQuality]) {
quality = kYTPlaybackQualitySmall;
} else if ([qualityString isEqualToString:kYTPlaybackQualityMediumQuality]) {
quality = kYTPlaybackQualityMedium;
} else if ([qualityString isEqualToString:kYTPlaybackQualityLargeQuality]) {
quality = kYTPlaybackQualityLarge;
} else if ([qualityString isEqualToString:kYTPlaybackQualityHD720Quality]) {
quality = kYTPlaybackQualityHD720;
} else if ([qualityString isEqualToString:kYTPlaybackQualityHD1080Quality]) {
quality = kYTPlaybackQualityHD1080;
} else if ([qualityString isEqualToString:kYTPlaybackQualityHighResQuality]) {
quality = kYTPlaybackQualityHighRes;
}
return quality;
}
I created a issue for this on the project's GitHub page.
I received a reply from Ibrahim Ulukaya about this issue:
We are hoping to have more information to that call, but basically active indicates the good streaming, and your streaming info is https://developers.google.com/youtube/v3/live/docs/liveStreams#cdn.format where you set, and can see the format.
So the answer for the time being is no, this information is not available from the YouTube Livestreaming API for the time being. I will updated this answer if/when the API is updated.
It seems Youtube Live streaming API has been updated to show Live stream health status with this property: status.healthStatus.status
See their latest API for more info.