trouble with iOS9 search API NSUserActivity - ios

When I watch videos about iOS9 search API WWDC2015,there is a demo like this:
var activity:NSUserActivity = NSUserActivity(activityType:"com.yummly.browseRecipe")
activity.title = "Baked Potato Chips"
activity.userInfo = ["id":"http://www.yummly.com/recipe/BPC-983195"]
activity.eligibleForSearch = true
activity.becomeCurrent()
I copy this code to my Xcode and run it, when I search by Spotlight, there is no results. What's wrong with it? Bug for iOS9?

For some reason, you must keep a reference to your NSUserActivity object after you call activity.becomeCurrent(). Like this (Swift):
activity.becomeCurrent()
self.lastActivity = activity
where "lastActivity" is a property of the class you are in.

I did like this, is in objective-C, but you can easily translate to swift
if ([CSSearchableItemAttributeSet class]) {
CSSearchableItemAttributeSet* attributes = [[CSSearchableItemAttributeSet alloc]initWithItemContentType:#"kUTTypePackage"];//Set you content type
attributes.title = model.name;
attributes.contentDescription = model.description;
attributes.identifier = model.fileURL.lastPathComponent;
UIImage* backImage = [UIImage imageWithData:model.imageData];
if (backImage == nil) {
attributes.thumbnailData = UIImageJPEGRepresentation([UIImage imageNamed:#"DefaultImage"], 0.5);
}else{
attributes.thumbnailData = model.imageData;
}
NSString* domainID = #"com.myapp.mycompany";
NSString* uniqueID = model.fileURL.lastPathComponent;
CSSearchableItem* item = [[CSSearchableItem alloc]initWithUniqueIdentifier:uniqueID //value passed from NSUserActivity inside .userInfo
domainIdentifier:domainID
attributeSet:attributes];
NSLog(#"Item Attributes:%#",item.attributeSet.identifier);
CSSearchableIndex* index = [CSSearchableIndex defaultSearchableIndex];
[index indexSearchableItems:#[item] completionHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(#"Item \"%#\" indexed",model.name);
}else{
NSLog(#"Error, \"%#\" not indexed: %#, %#",model.name,error, error.userInfo);
}
}];
}else{
NSLog(#"iOS < 9");
}

You need to attach the NSUserActivity to the controller like below.
controller.userActivity = activity

Related

How to Implement and using nextTokenPage using Using GTLRYoutube iOS Objective C

so I have a question how to use & implement nextPageToken Using GTLRYoutube in iOS Objective c which the example result is here :
( https://developers.google.com/youtube/v3/docs/playlistItems/list ) and set the maxResults to "10" and the "nextPageToken will generate it".
btw my flow to get the :
ChannelID
and getting PlaylistID -> to get PlaylistItems
and VideoID one by one
and the code like this :
1.
- (void)fetchChannelResource {
GTLRYouTubeQuery_ChannelsList *query =
[GTLRYouTubeQuery_ChannelsList queryWithPart:#"snippet,contentDetails"];
query.mine = true;
[self.service executeQuery:query
delegate:self
didFinishSelector:#selector(displayResultWithTicket:finishedWithObject:error:)];
}
and execute query :
- (void)displayResultWithTicket:(GTLRServiceTicket *)ticket
finishedWithObject:(GTLRYouTube_ChannelListResponse *)channels
error:(NSError *)error {
if (error == nil) {
NSMutableString *output = [[NSMutableString alloc] init];
if (channels.items.count > 0) {
self.youtubeChannelDataArray = channels.items;
[output appendString:#"Channel information:\n"];
for (GTLRYouTube_Channel *channel in channels) {
NSString *title = channel.snippet.title;
NSString *description = channel.snippet.description;
NSNumber *viewCount = channel.statistics.viewCount;
[output appendFormat:#"Title: %#\nDescription: %#\nViewCount: %#\n", title, description, viewCount];
NSString *uploadID = channel.contentDetails.relatedPlaylists.uploads;
self.youtubeUploadID = uploadID;
}
//CALL fetchPlaylistItemResource
[self fetchPlaylistItemResource];
} else {
[output appendString:#"Channel not found."];
}
// self.output.text = output;
} else {
// [self showAlert:#"Error" message:error.localizedDescription];
}
}
all the data saved in NSArray and NSMutableDictionary.
and in other viewController, i need to display them all.. but the problem is how to implement with nextPageToken which is will display 5 items resultPerPage ??
because when I use this "GTLRYoutube", looks like we don't need to make a new model, just use the model from GTLRYoutube, besides that also didn't make mapping and access the youtube API in the Datamanager.m
anyone have a suggestion or advise ???
thanks before!

NSDictionary get property within list of objects - Objective C

How would I get "Dog" from the following dictionary?
{
Id = "123";
Animal = [{
Id = "456";
Type = "Dog";
Sound = "Bark";
},
{
Id = "789";
Type = "Cat";
Sound = "Meow";
}]
}
I tried
NSString *firstAnimalType = dictionary[#"Animal"][#"Type"];
However since there are multiple animals, it can't recognize what I am trying to find. How would I get the first animal out of the list so that I can access its type? Thanks!
You can use some thing like this, first get animal object and if it exists then find its type from it
NSDictionary *firstAnimal = [dictionary[#"Animal"] firstObject];
if(firstAnimal) //in case your firstAnimal is Empty or it may be nil then may create any unwanted issues in further code so just check it first.
{
NSString *firstAnimalType = firstAnimal[#"Type"];
}
Enumeration method will help and you can stop when and where you want
[myDict[#"Animal"] enumerateObjectsUsingBlock:^(id _Nonnull objList, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(#"%#",objList[#"Type"]);
*stop = YES; //You can stop where you want
}];
You can simply access the first element of the array with castings and get its Type value.
NSString *firstAnimalType = ((NSDictionary *)[((NSArray *)dictionary[#"Animal"]) objectAtIndex: 0])[#"Type"];
for (NSDictionary *animal in dictionary[#"Animal"]) {
NSString *type = animal[#"Type"];
if ([type isKindOfClass:[NSString class]] && [type isEqualToString:#"Dog"]) {
// Dog found
}
}
Here you go:
NSArray *aryFinalAni = [dicMain valueForKey:#"Animal"];
NSArray *aryType = [aryFinalAni valueForKeyPath:#"Type"];
if([aryType containsObject:#"Dog"])
{
int indexOfDog = (int)[aryType indexOfObject:#"Dog"];
NSMutableDictionary *dicDog = [aryFinalAni objectAtIndex:indexOfDog];
NSLog(#"%#",dicDog);
}
else
{
NSLog(#"There is no Dog found.");
}
Try this code:
{
Id = "123";
Animal = [{
Id = "456";
Type = "Dog";
Sound = "Bark";
},
{
Id = "789";
Type = "Cat";
Sound = "Meow";
}]
}
1> NSArray *items = dictionary["Animal"];
2>
NSPredicate *predicate1 = [NSPredicate predicateWithFormat: #"Type CONTAINS[cd] %#", "Dog"];
NSArray *arrData = [items filteredArrayUsingPredicate:predicate1];
if arrData.count > 0 {
dictionary = [arrData objectAtIndex:0];
}
Result:
{
Id = "456";
Type = "Dog";
Sound = "Bark";
}

How to get VCF data with contact images using CNContactVCardSerialization dataWithContacts: method?

I'm using CNContacts and CNContactUI framework and picking a contact via this
CNContactPickerViewController *contactPicker = [CNContactPickerViewController new];
contactPicker.delegate = self;
[self presentViewController:contactPicker animated:YES completion:nil];
and
-(void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact
{
NSArray *array = [[NSArray alloc] initWithObjects:contact, nil];
NSError *error;
NSData *data = [CNContactVCardSerialization dataWithContacts:array error:&error];
NSLog(#"ERROR_IF_ANY :: %#",error.description);
}
This contact object have contact.imageData and coming in logs. But when I tried to cross check this data by
NSArray *contactList = [NSArray arrayWithArray:[CNContactVCardSerialization contactsWithData:data error:nil]];
CNContact *contactObject = [contactList objectAtIndex:0];
This is getting null:
//contactObject.imageData
Why am I getting this null and this contact has image when check in contacts?
I'd like to improve upon and modernise for Swift 3 the excellent answer by kudinovdenis.
Just put the following extension into your project
import Foundation
import Contacts
extension CNContactVCardSerialization {
internal class func vcardDataAppendingPhoto(vcard: Data, photoAsBase64String photo: String) -> Data? {
let vcardAsString = String(data: vcard, encoding: .utf8)
let vcardPhoto = "PHOTO;TYPE=JPEG;ENCODING=BASE64:".appending(photo)
let vcardPhotoThenEnd = vcardPhoto.appending("\nEND:VCARD")
if let vcardPhotoAppended = vcardAsString?.replacingOccurrences(of: "END:VCARD", with: vcardPhotoThenEnd) {
return vcardPhotoAppended.data(using: .utf8)
}
return nil
}
class func data(jpegPhotoContacts: [CNContact]) throws -> Data {
var overallData = Data()
for contact in jpegPhotoContacts {
let data = try CNContactVCardSerialization.data(with: [contact])
if contact.imageDataAvailable {
if let base64imageString = contact.imageData?.base64EncodedString(),
let updatedData = vcardDataAppendingPhoto(vcard: data, photoAsBase64String: base64imageString) {
overallData.append(updatedData)
}
} else {
overallData.append(data)
}
}
return overallData
}
}
and then you can use it similarly to the existing serialisation method:
CNContactVCardSerialization.data(jpegPhotoContacts: [contact1, contact2])
Note that this takes care of serialisation, you'll need to write a similar method for deserialisation if you are also importing.
As a workaround you can create PHOTO field inside of VCard.
NSError* error = nil;
NSData* vCardData = [CNContactVCardSerialization dataWithContacts:#[contact] error:&error];
NSString* vcString = [[NSString alloc] initWithData:vCardData encoding:NSUTF8StringEncoding];
NSString* base64Image = contact.imageData.base64Encoding;
NSString* vcardImageString = [[#"PHOTO;TYPE=JPEG;ENCODING=BASE64:" stringByAppendingString:base64Image] stringByAppendingString:#"\n"];
vcString = [vcString stringByReplacingOccurrencesOfString:#"END:VCARD" withString:[vcardImageString stringByAppendingString:#"END:VCARD"]];
vCardData = [vcString dataUsingEncoding:NSUTF8StringEncoding];
For some reasons CNContactVCardSerialization does not use any photo of contact. VCard after serialization is looks like:
BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//iPhone OS 9.3.2//EN
N:Contact;Test;;;
FN: Test Contact
END:VCARD
After insertion the PHOTO field inside VCard you will get
BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//iPhone OS 9.3.2//EN
N:Contact;Test;;;
FN: Test Contact
PHOTO;TYPE=JPEG;ENCODING=BASE64:<photo base64 string>
END:VCARD
After this insertion contact will looks fine in CNContactViewController
For N number of contacts, you can add image data into VCF by using simple method as below.
-(NSData*)getVCFDataWithImagesFromContacts:(NSArray*)arrContacts
{
//---- Convert contacts array into VCF data.
NSError *error;
NSData *vcfData = [CNContactVCardSerialization dataWithContacts:arrContacts error:&error];
//--- Convert VCF data into string.
NSString *strVCF = [[NSString alloc] initWithData:vcfData encoding:NSUTF8StringEncoding];
//--- Split contacts from VCF.
NSMutableArray *arrSplit = (NSMutableArray*)[strVCF componentsSeparatedByString:#"END:VCARD"];
[arrSplit removeLastObject];//-- if object is "\r\n" otherwise comment this line.
//--- Validate array count
if (arrSplit.count == arrContacts.count)
{
for (int index=0;index<arrContacts.count;index++)
{
//--- Get current contact and VCF contact string.
CNContact *contact = arrContacts[index];
NSString *strContact = arrSplit[index];
//--- Get base64 string of image.
NSString* base64Image = [UIImagePNGRepresentation([ViewController imageWithImage:[UIImage imageWithData:contact.imageData] scaledToSize:CGSizeMake(50,50)]) base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
//--- Append image tag into contact string.
NSString* vcardImageString = [[#"PHOTO;ENCODING=BASE64;JPEG:" stringByAppendingString:base64Image] stringByAppendingString:#"\r\n"];
strContact = [strContact stringByAppendingString:[NSString stringWithFormat:#"%#%#",vcardImageString,#"END:VCARD\r\n"]];
//--- Update contact string from array.
[arrSplit replaceObjectAtIndex:index withObject:strContact];
NSLog(#"strContact :%#",strContact);
}
}
//--- Combine all contacts together in VCF.
vcfData = [[arrSplit componentsJoinedByString:#""] dataUsingEncoding:NSUTF8StringEncoding];
strVCF = [[NSString alloc] initWithData:vcfData encoding:NSUTF8StringEncoding];//--- VCF Data
NSLog(#"Contact VCF error :%#",error.localizedDescription);
return vcfData;
}
+(UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

Downloading calendar data in Objective-C

I am trying to download google calendar data.
I am following a tutorial this link according to which implementing some of the GoogleOAuth delegate methods will let me get my desired data.
First I converted the response JSON data into an NSDictionary object and after that, I, NSLog this dictionary, to see the way the returned data is formed which is as shown below.
calendarInfoDict is {
etag = "\"1436255371893000\"";
items = (
{
accessRole = owner;
backgroundColor = "#9a9cff";
colorId = 17;
defaultReminders = (
{
method = popup;
minutes = 30;
}
);
etag = "\"1436255371893000\"";
foregroundColor = "#000000";
id = "sabiranthapa#gmail.com";
kind = "calendar#calendarListEntry";
notificationSettings = {
notifications = (
{
method = email;
type = eventCreation;
},
{
method = email;
type = eventChange;
},
{
method = email;
type = eventCancellation;
},
{
method = email;
type = eventResponse;
}
);
};
primary = 1;
selected = 1;
summary = "sabiranthapa#gmail.com";
timeZone = "Asia/Calcutta";
},
{
accessRole = reader;
backgroundColor = "#92e1c0";
colorId = 13;
defaultReminders = (
);
description = "Displays birthdays of people in Google Contacts and optionally \"Your Circles\" from Google+. Also displays anniversary and other event dates from Google Contacts, if applicable.";
etag = "\"1436255358367000\"";
foregroundColor = "#000000";
id = "#contacts#group.v.calendar.google.com";
kind = "calendar#calendarListEntry";
summary = Birthdays;
timeZone = "Asia/Calcutta";
}
);
kind = "calendar#calendarList";
nextSyncToken = 00001436255371893000;
}
According to tutorial there is a block containing a bunch of information regarding every calendar I have created in Google Calendars inside curly bracket. But I am not getting any events that I have saved which can be seen in Google Calendar after signing in gmail but not in my apps where I need to work with it.
My code is
-(void)responseFromServiceWasReceived:(NSString *)responseJSONAsString andResponseJSONAsData:(NSData *)responseJSONAsData{
NSError *error;
if ([responseJSONAsString rangeOfString:#"calendarList"].location != NSNotFound) {
NSDictionary *calendarInfoDict = [NSJSONSerialization JSONObjectWithData:responseJSONAsData options:NSJSONReadingMutableContainers error:&error];
NSLog(#"calendarInfoDict is %#", calendarInfoDict);
if (error) {
NSLog(#"%#", [error localizedDescription]);
}
else{
NSArray *calendarsInfo = [calendarInfoDict objectForKey:#"items"];
if (_arrGoogleCalendars == nil) {
_arrGoogleCalendars = [[NSMutableArray alloc] init];
}
for (int i=0; i<[calendarsInfo count]; i++) {
NSDictionary *currentCalDict = [calendarsInfo objectAtIndex:i];
NSArray *values = [NSArray arrayWithObjects:[currentCalDict objectForKey:#"id"],
[currentCalDict objectForKey:#"summary"],
nil]; NSArray *keys = [NSArray arrayWithObjects:#"id", #"summary", nil];
[_arrGoogleCalendars addObject:
[[NSMutableDictionary alloc] initWithObjects:values forKeys:keys]];
}
_dictCurrentCalendar = [[NSDictionary alloc] initWithDictionary:[_arrGoogleCalendars objectAtIndex:0]];
[_barItemPost setEnabled:YES];
[_barItemRevokeAccess setEnabled:YES];
[self showOrHideActivityIndicatorView];
But I always end up with condition
if (_arrGoogleCalendars == nil) {
_arrGoogleCalendars = [[NSMutableArray alloc] init];
}
without able to access my events.
How can I download (or access) my Events from google calendar?

Parse YouTube JSON for UITableView

I am trying to have a UITableView that displays the title, thumbnail, viewcount, and duration of some youtube videos on a specific channel. The link is this
https://gdata.youtube.com/feeds/api/videos?q=remedyLIVE&max-results=5&v=2&alt=jsonc&orderby=published
And the JSON looks like this
{
apiVersion = "2.1";
data = {
items = (
{
accessControl = {
autoPlay = allowed;
comment = allowed;
commentVote = allowed;
embed = allowed;
list = allowed;
rate = allowed;
syndicate = allowed;
videoRespond = moderated;
};
aspectRatio = widescreen;
category = Music;
commentCount = 0;
content = {
1 = "rtsp://r3---sn-jc47eu7l.c.youtube.com/CiILENy73wIaGQkbMrdTdbNpexMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp";
5 = "https://www.youtube.com/v/e2mzdVO3Mhs?version=3&f=videos&app=youtube_gdata";
6 = "rtsp://r3---sn-jc47eu7l.c.youtube.com/CiILENy73wIaGQkbMrdTdbNpexMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp";
};
description = "In this episode of IchthusTV My Epic talks about seeking help within the church. -- Text 'Remedy' to 313131 if you need to chat. RemedyLIVE: We Chat, Listen, and Love www.remedyLIVE.com.";
duration = 109;
id = e2mzdVO3Mhs;
player = {
default = "https://www.youtube.com/watch?v=e2mzdVO3Mhs&feature=youtube_gdata_player";
mobile = "https://m.youtube.com/details?v=e2mzdVO3Mhs";
};
thumbnail = {
hqDefault = "https://i.ytimg.com/vi/e2mzdVO3Mhs/hqdefault.jpg";
sqDefault = "https://i.ytimg.com/vi/e2mzdVO3Mhs/default.jpg";
};
title = "My Epic - Help within the Church";
updated = "2014-11-11T20:00:03.000Z";
uploaded = "2014-11-11T20:00:03.000Z";
uploader = chatlistenlove;
},
{
accessControl = {
autoPlay = allowed;
comment = allowed;
commentVote = allowed;
embed = allowed;
list = allowed;
rate = allowed;
syndicate = allowed;
videoRespond = moderated;
};
aspectRatio = widescreen;
category = People;
commentCount = 0;
content = {
etc... I won't post the whole thing but you can see it on the link above! As this is my first time working with JSON I understand that I need to parse this to an array or dictionary but I am just a bit confused on what exactly the "objectforkey" methods look like once I have the data. None of the tutorials I see apply to what I am doing so specific help would be awesome! Here is my code so far
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self.tableView registerNib:[UINib nibWithNibName:#"RemedyYouTubeTableViewCell"
bundle:[NSBundle mainBundle]]
forCellReuseIdentifier:#"RemedyYouTubeTableViewCell"];
NSURL *youtubeURL = [NSURL URLWithString:#"https://gdata.youtube.com/feeds/api/videos?q=remedyLIVE&max-results=5&v=2&alt=jsonc&orderby=published"];
NSData *data = [NSData dataWithContentsOfURL:youtubeURL];
if (data == nil)
{
NSLog(#"data is nil");
}
else
{
NSError *error;
titleArray = [[NSMutableArray alloc]init];
videoIDArray = [[NSMutableArray alloc]init];
thumbArray = [[NSMutableArray alloc]init];
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"%#", json);
for (NSDictionary *item in json)
{
NSDictionary* snippet = [item objectForKey:#"snippet"];
title = [snippet objectForKey:#"title"];
videoID = [[snippet objectForKey:#"resourceId"] objectForKey:#"videoId"];
thumbURL = [[[snippet objectForKey:#"thumbnails"] objectForKey:#"default"] objectForKey:#"url"];
[titleArray addObject:title];
[videoIDArray addObject:videoID];
[thumbArray addObject:thumbURL];
}
[self.tableView reloadData];
}
For example why is the word "snippet" chosen? I found this on another stack overflow post about this but I don't understand. Thanks in advance!
As mentioned in the comments, you can parse NSDictionary objects (translated by parsing JSON objects) using short hand notation. To get to the value you want, use something like this:
NSURL* playbackUrl = [NSURL urlWithString:yourDictionary[#"data"][#"items"][#"player"]["mobile"]];
To prevent crashes when the property doesn't exist, you'll want to use a try catch block:
#try {
NSURL* playbackUrl = [NSURL urlWithString:yourDictionary[#"data"][#"items"][#"player"]["mobile"]];
}
#catch (NSException *exception) {
// handle exception
}

Resources