If an application is configured to connect to different instances on Parse server using separate application id/secrets, what is the best way to verify if application has connected to correct instance on Parse. Basically it's about pulling down some sort of meta data before actually trying to Write/Read objects stored in backend.
I am currently working with iOS SDK and Parse.h/PFObject.h/PFQuery.h doesn't contain any such information.
Something that I figured out if querying for the application Id and try to match it against predefined values for an environment. Is there a better way to do that ?
Have a look at PFConfig. You can set configuration parameters in each Parse instance that are unique.
Here is a quick example of how to use it:
NSLog(#"Getting the latest config...");
[PFConfig getConfigInBackgroundWithBlock:^(PFConfig *config, NSError *error) {
if (!error) {
NSLog(#"Yay! Config was fetched from the server.");
} else {
NSLog(#"Failed to fetch. Using Cached Config.");
config = [PFConfig currentConfig];
}
NSString *welcomeMessage = config[#"welcomeMessage"];
if (!welcomeMessage) {
NSLog(#"Falling back to default message.");
welcomeMessage = #"Welcome!";
}
NSLog(#"Welcome Messsage = %#", welcomeMessage);
}];
Related
I'm writing an app that stores contact-info fetched through REST and JSON into a container, using CNContactStore. I would like to keep these contacts separate from any other accounts, and only stored locally on the device, but a local store doesn't exist, and I can't find any way to create/activate one?
I'm able to get the default store's ID (as configured on the device, e.g. iCloud), using:
let store = CNContactStore()
let containerID = store.defaultContainerIdentifier()
...and I can (theoretically) identify a local container like this — if one actually exists:
var allContainers = [CNContainer]()
do {
allContainers = try store.containersMatchingPredicate(nil)
for container in allContainers {
if container.type == CNContainerType.Local {
print("Local container is: \(container.identifier)")
break
}
}
} catch {
print("Error fetching containers")
}
But no local container exists. Any ideas on how to store my contacts locally, or in a new separate container?
This was possible as follows with the now deprecated AB API, may still work as a workaround:
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions( nil, nil );
ABRecordRef source = ABAddressBookGetSourceWithRecordID( addressBook, kABSourceTypeLocal );
ABRecordRef contact = ABPersonCreateInSource( source );
The containersMatchingPredicate(nil) returns the default container only.
I have a similar problem. If icloud has been configured, it would only return the CNContainerTypeCardDAV type container else the local container.
What is the minimum number of steps I have to take to send a push notification? I am already getting the deviceToken natively through iOS, do I have to create an Installation object through Parse? I would rather not change any native code right now. Can I do something like below? Or is creating the installation object required before this step? Below is using a node library.. but just imagine it is using REST endpoints.
var notification = {
where : {
"deviceToken": {
"$in": ["deviceTokenHere"]
}
},
data: {
alert: "eat drink and be merry"
}
};
this.client.sendPush(notification, function(err, resp){
});
Yes, you need an Installation object. The implementation on the backend side queries the Installations collection and you can't change this.
Adding the code is to create the Installation and store the token is pretty straight forward and does not require much code:
- (void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
PFInstallation *installation = [PFInstallation currentInstallation];
[installation setDeviceTokenFromData:deviceToken];
[installation saveInBackground];
}
If you currently store the deviceToken you already receive on your server, you can also create the installations via the REST-API.
I am creating a TodayWidget app extension which displays information about user selected folders outside the application directory.
In my main application I am able to use powerbox via NSOpenPanel to select the folder. I can then save a security scoped bookmark to the user defaults of the app group container accessible by my TodayWidget.
The TodayWidget can read in the bookmark data, but when it calls URLByResolvingBookmarkData, it errors out with:
The file couldn’t be opened because it isn’t in the correct format.
Both my main application and the TodayWidget have the below entitlements:
com.apple.security.files.bookmarks.app-scope
com.apple.security.files.user-selected.read-only
From Apple's documentation, only the application that created the security scoped bookmark can use it. I guess these means embedded applications aren't allowed?
I've looked in to using XPC, but that doesn't really help the problem, as XPC can't use security scoped bookmark either, only a normal bookmark. As soon as the computer is restarted, the XPC process will lose access to the directories.
Really all I need is a way for the XPC process to get read access to user specified directories. Is there a way without having to relaunch my main application every restart of the computer?
You have probably already solved this or moved on. But for all those that are attempting something similar I will leave this here for them. In order to access security scoped bookmarks in a different app they have to be transferred as NSData and re-resolved in the other application.
In my case I show an open dialog in the main application and then save the scoped bookmark into a shared NSUserDefaults suite. The other applications are also part of that suite and then access the container of NSData's and resolve them into usable NSURL's
Here are the relevant bits of code:
//Inside my main application's open function
... get url from NSOpenPanel
BookmarkUtils.saveURLForOtherApplications(openPanel.URL!)
//Inside BookmarkUtils.swift
static func saveURLForOtherApplications(url:NSURL)->Bool{
let defaults = NSUserDefaults(suiteName: <#Suite-Name#>)!
//I store them as a dictionary of path->encoded URL
let sandboxedBookmarks:NSMutableDictionary
if let prevBookmarks = defaults.objectForKey(kSandboxKey) as? NSDictionary{
sandboxedBookmarks = NSMutableDictionary(dictionary:prevBookmarks)
}
else{
sandboxedBookmarks = NSMutableDictionary()
}
if let shareData = BookmarkUtils.transportDataForSecureFileURL(url){
sandboxedBookmarks.setObject(shareData, forKey:url.path!)
defaults.setObject(sandboxedBookmarks, forKey:kSandboxKey)
defaults.synchronize()
return true
}
else{
println("Failed to save URL Data");
return false
}
}
static func transportDataForSecureFileURL(fileURL:NSURL)->NSData?{
// use normal bookmark for encoding security-scoped URLs for transport between applications
var error:NSError? = nil
if let data = fileURL.bookmarkDataWithOptions(NSURLBookmarkCreationOptions.allZeros, includingResourceValuesForKeys:nil, relativeToURL:nil, error:&error){
return data;
}
else{
println("Error creating transport data!\(error)")
return nil
}
}
So then in my extension (Today view in my case) I do something like this...
class TodayViewController: ...
...
override func viewDidLoad() {
super.viewDidLoad()
var status = [MyCoolObjects]()
for url in BookmarkUtils.sharedURLSFromApp(){
BookmarkUtils.startAccessingSecureFileURL(url)
status.append(statusOfURL(url))
BookmarkUtils.stopAccessingSecureFileURL(url)
}
self.listViewController.contents = status
}
And the relevant bookmark looks something like:
static func sharedURLSFromApp()->[NSURL]{
var urls = [NSURL]()
if let defaults = NSUserDefaults(suiteName: <#Suite-Name#>){
if let prevBookmarks = defaults.objectForKey(kSandboxKey) as? NSDictionary{
for key in prevBookmarks.allKeys{
if let transportData = prevBookmarks[key as! NSString] as? NSData{
if let url = secureFileURLFromTransportData(transportData){
urls.append(url)
}
}
}
}
}
return urls
}
static func secureFileURLFromTransportData(data:NSData)->NSURL?{
// use normal bookmark for decoding security-scoped URLs received from another application
var bookmarkIsStale:ObjCBool = false;
var error:NSError? = nil;
if let fileURL = NSURL(byResolvingBookmarkData: data, options: NSURLBookmarkResolutionOptions.WithoutUI, relativeToURL: nil, bookmarkDataIsStale: &bookmarkIsStale, error: &error){
return fileURL
}
else if(bookmarkIsStale){
println("Bookmark was stale....")
}
else if let resolveError = error{
println("Error resolving from transport data:\(resolveError)")
}
return nil
}
This solution works for me. Once you resolve the shared URL you can then create a bookmark for that application and save it for later if so desired.There may be better ways out there, hopefully Apple works on this as it is currently painful to share permissions with extensions.
Actually you don't need to startAccessingSecureFileURL and it will return fail to start. just transform bookmark data to url will gain access.
I have a project set up where all data coming from the Server is wrote to a Core Data managed store using a managed model. I have all my entities generated from the Core Data model using mogenerator. I have all RestKit mapping integrated in to my entities.
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"dataModel" ofType:#"momd"]];
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
// Initialize the Core Data stack
[managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore __unused *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(persistentStore, #"Failed to add persistent store: %#", error);
[managedObjectStore createManagedObjectContexts];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];
Now there has been a change of plan due to time constraints. The data should not be stored at all. The data should be read from the server and displayed directly. No saving, no persisting. So I would like to cut out the RKManagedObjectStore, keep the entities and mappings, and read the data from 'RKMappingResult *mappingResult' when a request succeeds or a RKPaginator resutl. Example that works with RKManagedObjectStore and RKPaginator:
[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:[Friend entityMapping:objectManager.managedObjectStore]
method:RKRequestMethodAny
pathPattern:nil
keyPath:#"items"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
[objectManager setPaginationMapping:[self paginationMapping]];
self.paginator = [objectManager paginatorWithPathPattern:#"data"];
self.paginator.perPage = 20;
//Set completion block for this paginator
[self.paginator setCompletionBlockWithSuccess:^(RKPaginator *paginator, NSArray *objects, NSUInteger page) {
[weakSelf.dataArray addObjectsFromArray:objects];
} failure:^(RKPaginator *paginator, NSError *error) {
}];
However, when I start to reomve the RKManagedObjectStore I start to run into problems when mapping.
'You must provide a managedObjectStore. Invoke mappingForClass:inManagedObjectStore: instead.'
Q.1 Can I use Enitiy Mapping without RKManagedObjectStore? Am I going in the right direction.
Q.2 Can I remove the store and keep the model?
Any tips, help or examples would be great before I get too involved and go in the wrong direction.
Thanks Al
You should fight against the requirement change and use Core Data as a temporary cache of information to aid with memory management (so you can scroll up and down lists without having to have everything loaded all the time). This should not take any longer to implement...
No, you can't use RKEntityMapping without an RKManagedObjectStore.
You could keep the model but you wouldn't be able to use it (managed objects need to be created in association with a MOC).
I've managed to understand how to incorporate UIManagedDocument into a simple test application and it works as expected! However, now I'm adding support to this basic application so it will work if the user does not want to use iCloud.
So when the URLForUbiquityContainerIdentifier: method returns 'nil', I return the URL of the local documents directory using the suggested method
NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
return [NSURL fileURLWithPath:documentsDirectoryPath];
However, when I try saving a UIManagedDocument to the local URL (such as: file://localhost/var/mobile/Applications/some-long-identifier/Documents/d.dox) I get the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation.'
Using this save method:
if (![[NSFileManager defaultManager] fileExistsAtPath:self.managedDocument.fileURL.path]) {
[self.documentDatabase saveToURL:self.managedDocument.fileURL
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) {
//
// Add default database stuff here.
//
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.documentDatabase.managedObjectContext performBlock:^{
[Note newNoteInContext:self.managedDocument.managedObjectContext];
}];
});
} else {
NSLog(#"Error saving %#", self.managedDocument.fileURL.lastPathComponent);
}
}];
}
It turns out my persistent store options contained the keys used for the ubiquitous store. These shouldn't be in the documents persistent store options.