accessing and downloading on demand resources iOS9 - ios

I am trying to implement new iOS9 feature app thinning, I understood how tag an image and enable on demand resource in Xcode 7 but I don't understand how to implement NSBundleResourceRequest in my app, can someone help me, that would greatly appreciated

Most of information is available in Apple documentation.
Basically you need make this:
NSSet *tagsSet = [NSSet setWithObjects:#"resourceTag1", #"resourceTag2", nil];
NSBundleResourceRequest *request = [[NSBundleResourceRequest alloc] initWithTags:tagsSet];
[request conditionallyBeginAccessingResourcesWithCompletionHandler:^(BOOL resourcesAvailable) {
if (resourcesAvailable) {
// Start using resources.
} else {
[request beginAccessingResourcesWithCompletionHandler:^(NSError * _Nullable error) {
if (error == nil) {
// Start using resources.
}
}];
}
}];

First, check if the resources are available. Else download them.
Here is the swift code I use
let tags = NSSet(array: ["tag1","tag2"])
let resourceRequest = NSBundleResourceRequest(tags: tags as! Set<String>)
resourceRequest.conditionallyBeginAccessingResourcesWithCompletionHandler {(resourcesAvailable: Bool) -> Void in
if resourcesAvailable {
// Do something with the resources
} else {
resourceRequest.beginAccessingResourcesWithCompletionHandler {(err: NSError?) -> Void in
if let error = err {
print("Error: \(error)")
} else {
// Do something with the resources
}
}
}
}
I also found this guide very helpful.

Related

iOS Share Extension issue with ItemProvider can't read from Photo library

I used the Solution form iOS Share Extension issue when sharing images from Photo library to get Images from the Photo App. This works great in the Simulator, but on the Device I get an error that I can't Access the NSURL provided by the itemProvider:
2018-02-18 12:54:09.448148+0100 MyApp[6281:1653186] [default] [ERROR] Failed to determine whether URL /var/mobile/Media/PhotoData/OutgoingTemp/554581B2-950C-4CFD-AE67-A2342EDEA04D/IMG_2784.JPG (s) is managed by a file provider
Caused by the Statment:
[itemProvider loadItemForTypeIdentifier:itemProvider.registeredTypeIdentifiers.firstObject options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
}
Searching PHAsset for the Item Name is not a good solution as the user have to grand access to the photo library again.
In didSelectPost you must not dismiss the ShareExtension ViewController until you have processed all the items in the inputItems array. The ViewController is dismissed using [super didSelectPost] or by calling the completion method of the extension context.
Here is my solution in code:
- (void)didSelectPost {
__block NSInteger itemCount = ((NSExtensionItem*)self.extensionContext.inputItems[0]).attachments.count;
__block NSInteger processedCount = 0;
for (NSItemProvider* itemProvider in ((NSExtensionItem*)self.extensionContext.inputItems[0]).attachments ) {
if([itemProvider hasItemConformingToTypeIdentifier:#"public.jpeg"]) {
NSLog(#"itemprovider = %#", itemProvider);
[itemProvider loadItemForTypeIdentifier:#"public.jpeg" 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);
}
NSDictionary *dict = #{
#"imgData" : imgData,
#"name" : self.contentText
};
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.share.extension1"];
[defaults setObject:dict forKey:#"img"];
[defaults synchronize];
processedCount += 1;
if (processedCount == itemCount)
[super didSelectPost];
}];
}
}
The loadItemForTypeIdentifier or in Swift loadItem method is asynchronous so dismissing the UI must be called as the last thing inside its completionHandler.
For example I have:
override func didSelectPost() {
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
if let item = self.extensionContext?.inputItems[0] as? NSExtensionItem, let attachments = item.attachments {
for provider in attachments {
if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
provider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil, completionHandler: {item, error in
// do all you need to do here, i.e.
if let tmpURL = item as? URL {
// etc. etc.
}
// and, at the end, inside the completionHandler, call
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
})
}
}
}
}
I you dismiss the UI via:
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
or via:
super.didSelectPost()
outside of the completionHandler after the async method loadItem you will get all kind of permission errors, further more this errors could be random, sometimes happen and sometimes don't, this is because sometimes your async call to loadItem gets the chance to terminate before the UI is dismissed and sometimes it doesn't.
Just leaving this here, hoping it helps someone. This issue costed me few hours.

Facebook ios sdk (obj-c to swift) issue

I am trying to bring obj-c code to swift (facebook ios sdk), but autocomplete(intellisense) does not work in handler and I get an error (marked in code) : Set NSObject does not have a member named 'containsObject'
#IBAction func loginWithFacebook(sender: AnyObject) {
/*
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login logInWithReadPermissions:#[#"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
// Process error
} else if (result.isCancelled) {
// Handle cancellations
} else {
// If you ask for multiple permissions at once, you
// should check if specific permissions missing
if ([result.grantedPermissions containsObject:#"email"]) {
// Do work
}
}
}];
*/
let fbLoginManager = FBSDKLoginManager()
fbLoginManager.logInWithReadPermissions(["email"], handler: {
result, error in
if ((error) != nil){
}
else if (result.isCancelled){
} else {
if(result.grantedPermissions.containsObject("email")){ //<-- error here
}
}
})
}
Because Swift 1.2 automatically casts all NSSet objects (the ones that coming from external libs/sdks/frameworks etc...) to Set structure you need to call contains instead of containsObject for such things (doc).

Retrieve users top score from Game Center with swift

I need to retrieve a player's top submitted score from Game Center. I think I found the answer in objective C, but I don't know how to write it in swift since I am fairly new to this. Can someone help me translate the following code into swift? Thank you in advance.
GKLeaderboard *leaderboardRequest = [[GKLeaderboard alloc] init];
if (leaderboardRequest != nil) {
[leaderboardRequest loadScoresWithCompletionHandler:^(NSArray *scores, NSError *error){
if (error != nil) {
//Handle error
}
else{
[delegate onLocalPlayerScoreReceived:leaderboardRequest.localPlayerScore];
}
}];
}
Here's a straight translation to Swift:
let leaderboardRequest = GKLeaderboard() as GKLeaderboard!
if leaderboardRequest != nil
{
leaderboardRequest.loadScoresWithCompletionHandler({ (scores:[AnyObject]!, error:NSError!) -> Void in
if error != nil
{
//handle error
}
else
{
delegate.onLocalPlayerScoreReceived(leaderboardRequest.localPlayerScore)
}
})
}

Any working sample code for CKDiscoverAllContactsOperation for IOS8 beta CloudKit?

I have been playing with icloud in the ios 8 beta, and the CloudKitAtlasAnIntroductiontoCloudKit sample project has been very helpful.
https://developer.apple.com/library/prerelease/ios/samplecode/CloudAtlas/Introduction/Intro.html
But I wanted to use the CKDiscoverAllContactsOperation class and I cannot find any sample code for it anywhere at all and the online documentation is not very helpful.
https://developer.apple.com/library/prerelease/ios/documentation/CloudKit/Reference/CKDiscoverAllContactsOperation_class/index.html
If anyone has managed to successfully use CKDiscoverAllContactsOperation could you please help point me in the right direction or show a working example of how it should be called?
I have tried this to see if I could even get an response from iCloud but nothing:
- (void)queryForRecordsOtherUsersInAddressBookcompletionHandler:(void (^)(NSArray *records))completionHandler {
CKDiscoverAllContactsOperation *discoverAllContactsOperation= [[CKDiscoverAllContactsOperation alloc] init];
[discoverAllContactsOperation setContainer:_container];
NSMutableArray *results = [[NSMutableArray alloc] init];
discoverAllContactsOperation.discoverAllContactsCompletionBlock = ^(NSArray *userInfos, NSError *operationError) {
[results addObjectsFromArray:userInfos];
};
discoverAllContactsOperation.discoverAllContactsCompletionBlock=^(NSArray *userInfos, NSError *operationError){
if (operationError) {
// In your app, handle this error with such perfection that your users will never realize an error occurred.
NSLog(#"An error occured in %#: %#", NSStringFromSelector(_cmd), operationError);
abort();
} else {
dispatch_async(dispatch_get_main_queue(), ^(void){
completionHandler(results);
});
}
};
}
and calling with this...
[self.cloudManager queryForRecordsOtherUsersInAddressBookcompletionHandler:^(NSArray *records ) {
if (records.count==0){
NSLog(#"Login name not found");
return;
}
//self.results= records;
//_loggedInRecord = self.results[0];
//NSLog(#"%#,%#",_loggedInRecord[#"lastName"],_loggedInRecord[#"firstName"]);
// [self performSegueWithIdentifier:#"loggedInSegue" sender:self ];
}];
I know the code shouldn't really do anything. Again I was just looking for a response from iCloud.
Here is what I am using. self.container is a CKContainer set with [CKContainer defaultContainer] in the init.
-(void)queryForAllUsers: (void (^)(NSArray *records))completionHandler {
CKDiscoverAllContactsOperation *op = [[CKDiscoverAllContactsOperation alloc] init];
[op setUsesBackgroundSession:YES];
op.queuePriority = NSOperationQueuePriorityNormal;
[op setDiscoverAllContactsCompletionBlock:^(NSArray *userInfos, NSError *error) {
if (error) {
NSLog(#"An error occured in %#: %#", NSStringFromSelector(_cmd), error);
//abort();
} else {
// NSLog(#"Number of records in userInfos is: %ld", (unsigned long)[userInfos count]);
dispatch_async(dispatch_get_main_queue(), ^(void){
completionHandler(userInfos);
});
}
}];
[self.container addOperation:op];
}
Before you can use the CKDiscoverAllContactsOperation operation, you first need to request for permission.
Pls use the method requestApplicationPermission:completion:
func discoverAllContacts() {
let container = CKContainer.defaultContainer()
//Request for user permission
container.requestApplicationPermission([.UserDiscoverability]) { [weak self] status, error in
switch status {
case .Granted where error == nil:
let operation = self?.discoverAllContactsOperation { usersInfo in
//do something here
}
if let operationExists = operation {
//Assuming there is a NSOperationQueue property called operationQueue
self?.operationQueue.addOperation(operationExists)
}
default:
break
}
}
}
func discoverAllContactsOperation(completionHandler: ([CKDiscoveredUserInfo]?) -> ()) -> NSOperation {
let operation = CKDiscoverAllContactsOperation()
operation.discoverAllContactsCompletionBlock = { usersInfo, error in
if error == nil {
print("Discoverd all contacts = \(usersInfo)")
completionHandler(usersInfo)
}
else {
print("Discoverd all contacts error = \(error)")
completionHandler(nil)
}
}
return operation
}

Is Bolts framework[Parse+Facebook] need to use parse webservice?

I already post question How to use Bolts Framework[Facebook+Parse] but Now I've question, Must I use parse webservice if I want to use Bolts-framework?
They provide sample code like below which related(saveAsync:) to Parse webservice. But I've seen in this line "Using these libraries does not require using any Parse services. Nor do they require having a Parse or Facebook developer account" in Boltss' github
[[object saveAsync:obj] continueWithBlock:^id(BFTask *task) {
if (task.isCancelled) {
// the save was cancelled.
} else if (task.error) {
// the save failed.
} else {
// the object was saved successfully.
SaveResult *saveResult = task.result;
}
return nil;
}];
Now I get confusion, Is bolts framework need to use parse webservice?
Note: Don't ask where do you want to use Bolts-framework. see my first line of this question.
Surely it doesn't need Parse webservice. I've the same difficulty in implementing my own task and I'm studying this framework. Take a look at BoltsTest code: you can find some useful code.
I'm trying some experiments in a sample project (https://github.com/giaesp/BoltsFrameworkSample). Basically you need to define your own method returning a BFTask. Here a simple excerpt.
- (BFTask*) parseHTML:(NSURL*)url searchString:(NSString*)searchString {
BFTaskCompletionSource * tcs = [BFTaskCompletionSource taskCompletionSource];
NSURLRequest * request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30];
NSURLResponse * response;
NSError * error;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (!error) {
NSString * receivedData = [NSString stringWithUTF8String:[returnData bytes]];
NSUInteger occurrences = [self countOccurencesOfString:#"iOS" inputString:receivedData];
[tcs setResult:[NSNumber numberWithInt:occurrences]];
}
else {
[tcs setError:error];
}
return tcs.task;
}
Then you can use your method as the docs explains and check the task status.
[[self parseHTML:[NSURL URLWithString:#"http://www.stackoverflow.com"]] continueWithBlock:^id(BFTask *task) {
if (task.isCancelled) {
// the task was cancelled
} else if (task.error) {
// the task failed
} else {
// the task completes
}
return nil;
}];
I know it's been a while since this question was asked but as mani wanted to know if you could use Bolts framework with AFNetworking as well i want to add a quick example that shows usage.
It's written in swift and really just plain and simple.
func taskWithPath(path: String) -> BFTask {
let task = BFTaskCompletionSource()
AFHTTPRequestOperationManager().GET(path, parameters: nil, success: { (operation, response) in
task.setResult(response)
}) { (operation, error) -> Void in
task.setError(error)
}
return task.task
}
Hope this helps :)
The idea with Bolts is to encapsulate any operation using a BFTask. You don't necessarily have to wrap the operation in a method, but it's a good way to imagine how you should structure your code:
- (BFTask*) asynchronousImageProcessOperation;
- (BFTask*) asynchronousNetworkOperation;
...and all of these would follow a similar pattern:
- (BFTask*) asynchronousNetworkOperation {
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
// ... here's the code that does some asynchronous operation on another thread/queue
[someAsyncTask completeWithBlock:^(id response, NSError *error) {
error ? [source setError:error] : [source setResult:response];
}
return task;
}
The beauty of it is that you can them string these tasks together in some way. For example, if you needed to process an image and then upload it, you could do:
[[object methodReturnImageProcessingTask] continueWithBlock:^(BFTask *task) {
[[anotherObject imageUploadTaskForImage:task.result] continueWithBlock:^(BFTask *task) {
self.label.text = #"Processing and image complete";
}]
}]
Of course you could also encapsulate that two-stage task in its own task:
- (BFTask*) processAndUploadImage:(UIImage* image);
Typing from memory here. It's the sequencing and grouping that's really powerful. Great framework.

Resources