Write NSDictionary to ONLINE .plist. Is it possible? - objective-c

I'm writing an app for instant messaging aaand I'm stuck.
I am able to read data (dictionary) from plist that's on my dropbox, but I can't modify it from my app, what is a thing I actually want to achieve.
Here is how I read the online .plist file:
#Implementation
NSDictionary *wholePlist;
-(void)viewDidLoad
{
wholePlist = [[NSDictionary alloc]initWithContentsOfURL: [NSURL URLWithString:[NSString stringWithFormat:#"https://dl.dropboxusercontent.com/s/cfpree9see19t00/users.plist"]]];
self.allUsers = [wholePlist objectForKey:#"allUsers"];
} //self.allUsers is NSDictionary, also.
And this is how I am trying to save it if I change it
- (IBAction)registerButtonPressed:(UIButton *)sender {
NSString *username = self.usernameTextField.text;
NSString *password = self.setPassTextField.text;
NSMutableArray *myContacts = [[NSMutableArray alloc]init];
NSMutableArray *inbox = [[NSMutableArray alloc]init];
NSDictionary *user = [[NSDictionary alloc]initWithObjects:#[username, password, myContacts, inbox] forKeys:#[#"username",#"pass",#"myContacts",#"inbox"]];
if ([user isEqualToDictionary:[self.allUsers objectForKey:username]]) {
[[[UIAlertView alloc]initWithTitle:#"Registration error" message:#"Username already taken. Please, choose another username." delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil]show];
} else {
NSURL *plistURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://dl.dropboxusercontent.com/s/cfpree9see19t00/users.plist"]];
[self.allUsers setValue:user forKey:username];
[self.allUsers writeToURL:plistURL atomically:YES];
}
}
If I do it locally/offline (in some folder inside my Mac or app directory) using writeToFile: it works. When I use writeToURL: it doesn't work.
My questions are:
Is this even possible, what am I trying to achieve?
Is it possible with any other storage client?
If it's possible with some other storage client, please give me source link on how to OR explain how to.
Thanks!

Instant messaging applications are almost always best done using sockets. I'd HIGHLY recommend against using a file on a server to read and write from. While it's possible, you're asking for a world of pain and slugish-ness.
So to answer your questions in a striaght forward manner:
Yes... Don't do it.
Yes. of course you can use CloudKit either the DB or file upload part. Again, I recommend against this method because it's slow and has high overhead on the network.
I highly recommend reading up on sockets: http://en.wikipedia.org/wiki/Network_socket to better understand this approach. I have a chat socket written in C++. However it does a bit more than what you may need: https://github.com/theMonster/ModularServer. Also, there's a very popular chat server example for node.js here: http://socket.io/get-started/chat/
Let me know if you have any questions.

Related

How to create .plist file under /Library/LaunchAgents

I'm trying to develop a launch agent for macOS via Apple Doc
https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html
One of my requirements is that the agent should work for all users. What I understood from above document is I have to put my .plist under "/Library/LaunchAgents" folder.
When I try to create this file programatically nothing happens with the below code.
NSMutableDictionary *plist = [[NSMutableDictionary alloc] init];
[plist setObject:#"test" forKey: #"test 1"];
NSString *userLaunchAgentsPath = [[NSString alloc] initWithFormat:#"%#", #"/Library/LaunchAgents/com.xxx.agent.plist"];
[plist writeToFile:userLaunchAgentsPath atomically:YES];
Probably the reason is a privilege issue. Do you have any ideas for solving this issue?
As to privileges, the plist should be owned by root and if you want the app to run as a different user, you can do that easily by providing the username/password in the plist. Your app is probably not running as root.

Passing a Facebook Places id, from placesPicker in iOS... to postParams for a feed publish

This has been driving me bananas! I'm trying to get my app to publish a message to the users facebook feed, and a checkin to a facebook Place after selecting a local place from the placesPicker in iOS.
I've pretty much got everything working in testing, meaning when I just hand type string values for all the postParams for the NSMutableDictionary, it posts perfectly. ex:
self.postParams =
[[NSMutableDictionary alloc] initWithObjectsAndKeys:
#"I am doing XYZ", #"message",
#"123456abc",#"tags",
#"123456xyz,#"place",
nil];
The above postParams work perfectly when published to feed, it displays the message, then checks in to the "place" id and says you are with the userid in "tags". The problem is that I am really getting the Place id from the Facebook SDK's placePicker. I've got the picker setup and working perfectly, but I can not figure out how to replace the hardcoded #"123456xyz" with the id from the picked Place.
this is currently what I have in there now:
self.postParams =
[[NSMutableDictionary alloc] initWithObjectsAndKeys:
#"I am doing XYZ", #"message",
#"123456abc",#"tags",
self.selectedPlace.id,#"place",
nil];
self.selectedplace.id does successfully contain the id of the Place, when i print it with debug/NSLog in console, it is holding the value accurately. but when i try to publish this after picking a place, I get "error: domain = com.facebook.sdk, code = 5"
Here is how self.selectedPlace is defined & synthesized:
#property (strong, nonatomic) NSObject<FBGraphPlace> *selectedPlace;
...
#synthesize selectedPlace = _selectedPlace;
...
[placePicker presentModallyFromViewController:self
animated:YES
handler:^(FBViewController *sender, BOOL donePressed) {
if (donePressed) {
self.selectedPlace = placePicker.selection;
}
}];
So, to sum my question: How can I successfully get this Place id that is stored in self.selectedPlace.id into the postParams dictionary, so that it can be successfully interpreted & published to the user's feed?
Also, I should warn everyone upfront... I am self taught and still learning, and although I can look at the code, understand what it does, and write it... I'm afraid I'm not great with the terminology & discussing/understanding some of the language... lol so it would help me a great deal if you keep that in mind in any answers or comments :) Thanks, and I'm looking forward to breaking out of my stackoverflow newb shell and becoming more apart of the community here.
Figured it out... rather than setting the post params collectively with initWithObjectsAndKeys, like this:
self.postParams =
[[NSMutableDictionary alloc] initWithObjectsAndKeys:
#"I am doing XYZ", #"message",
#"123456abc",#"tags",
self.selectedPlace.id,#"place",
nil];
Just create separate dictionaries for each param you are passing and set each one to the final postParams with setValue: forKey:
[fbPostParams setValue:self.selectedPlace.id forKey:#"place"];

I want to delete all items in my self created KeyChain on Mac OS X

I'm writing a little tool to synchronize passwords. I'm using my own KeyChain for this purpose. Prior to saving, I want to clear this KeyChain. However, it seems I don't understand how to use the SecItemDelete function.
NSMutableDictionary *deleteQuery = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
kSecClassGenericPassword, kSecClass,
kSecMatchLimit, kSecMatchLimitAll, nil];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
NSLog(#"%#", SecCopyErrorMessageString(status, NULL));
This is what I've written so far, but unfortunately my items (called Root.Foo and Root.Bar) remain in the KeyChain. Also I'm wondering, how this function knows, which KeyChain should be searched? Most examples I'm fonding are about iOS, where every Application has it own KeyChain by default.
Thanks for any help :)
Solved it:
I've missed passing in an array of KeyChains to look for! It seems on iOS, always the default KeyChain of an app is used but on Mac OS you need to specify the KeyChain, as an array containing SecKeychainRefs:
NSMutableDictionary *q = [NSMutableDictionary dictionary];
[q setObject:kSecClassGenericPassword forKey:kSecClass];
[q setObject:[NSArray arrayWithObject:(__bridge id)keyChain] forKey:kSecMatchSearchList];
[q setObject:kSecMatchLimitAll forKey:kSecMatchLimit];
SecItemDelete((__bridge CFDictionaryRef)q);
This code worked perfectly.

Access iTunes user preferences in OSX Lion

I want to access iTunes user preferences such as playlists programmatically.
I use to do it with the following code, however since OSX Lion, I get a nil in response.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *userPreferences = [userDefaults persistentDomainForName:#"com.apple.iApps"];
NSArray *databasePaths = [userPreferences objectForKey:#"iTunesRecentDatabasePaths"];
I've also made sure my app has all of its entitlements enabled.
Any suggestions on how I can fix this?
Long story made short: You just can not do it using a Sandboxed app. Turn off Sandboxing and you will see that it works. Why? Well, that's because of containers. A sandbox lives in its own container and so when you call [NSUserDefaults standardUserDefaults] Cocoa uses the path of your container rather than the POSIX path of ~/Library/Preferences which is where com.apple.iApps.plist resides. That sums up why you get nil. Also, there is a blurb on this here in NSUserDefaults: link
How to fix this?
It's really not to bad. First you have to do a little bit of work to get the POSIX path of your home directory. I setup a bunch of NSURL category methods. However the root path is POSIX based. Here is the code snippet to get you started.
1.
+ (NSURL *) homePOSIXURL {
struct passwd *pwUser = getpwuid(getuid());
const char *homeDir = pwUser->pw_dir;
return [NSURL fileURLWithPath:[NSString stringWithUTF8String:homeDir] isDirectory:YES]; }
When all is said and done, construct a full path to the plist. It hasn't changed for years so you can consider it sticky.
So you might get something that looks like this now:
2.
NSURL * prefURL = [[NSURL libraryPOSIXURL] URLByAppendingPathComponent:#"Preferences" isDirectory:YES];
prefURL = [prefURL URLByAppendingPathComponent:#"com.apple.iApps.plist"];
Now let's turn this plist, which is fairly small into something we can play with. Perhaps NSData? That sounds good, right?
3.
NSData * prefData = [NSData dataWithContentsOfURL:prefURL];
Ok, finally we can use this now to get an NSDictionary. Here is how I do it.
4.
NSDictionary * prefDict = [NSDictionary collectionFromPropertyList:prefData];
Yeah, yeah another Category on NSDictionary. I must have a million of them.
Because I'm in a sharing mood, here ya go:
+ (id) collectionFromPropertyList:(NSData *)pList {
if ( [pList length] > 0 )
return [NSPropertyListSerialization propertyListWithData:pList
options:NSPropertyListImmutable
format:nil error:nil];
return nil;
}
So, you think we are done? Well, almost. If you get this far, you will get a deny like so:
deny file-read-data
/Users/UserName/Library/Preferences/com.apple.iApps.plist
Are we loving our Sandboxed app?! Basically add your temporary entitlement and you should be off to the races again. Best of luck to ya!

How to make QTMovie play file from URL with forced (MP3) type?

I'm using QTKit to progressively download and play an MP3 from a URL. According to this documentation, this is the code I should use to accomplish that:
NSURL *mp3URL = [NSURL URLWithString:#"http://foo.com/bar.mp3"];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithURL:mp3URL error:&error];
[sound play];
This works, and does exactly what I want — the MP3 URL is lazily downloaded and starts playing immediately. However, if the URL does not have the ".mp3" path extension, it fails:
NSURL *mp3URL = [NSURL URLWithString:#"http://foo.com/bar"];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithURL:mp3URL error:&error];
[sound play];
No error is given, no exception is raised; the duration of the sound is just set to zero, and nothing plays.
The only way I have found to work around this is to force a type by loading the data manually and using a QTDataReference:
NSURL *mp3URL = [NSURL URLWithString:#"http://foo.com/bar"];
NSData *mp3Data = [NSData dataWithContentsOfURL:mp3URL];
QTDataReference *dataReference =
[QTDataReference dataReferenceWithReferenceToData:mp3Data
name:#"bar.mp3"
MIMEType:nil];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithDataReference:dataReference error:&error];
[sound play];
However, this forces me to completely download ALL of the MP3 synchronously before I can start playing it, which is obviously undesirable. Is there any way around this?
Thanks.
Edit
Actually, it seems that the path extension has nothing to do with it; the Content-Type is simply not being set in the HTTP header. Even so, the latter code works and the former does not. Anyone know of a way to fix this, without having access to the server?
Edit 2
Anyone? I can't find information about this anywhere, and Google frustratingly now shows this page as the top result for most of my queries...
Two ideas. (The first one being a bit hacky):
To work around the missing content type, you could embed a small Cocoa webserver that supplements the missing header field and route your NSURL over that "proxy".
Some Cocoa http server implementations:
http://code.google.com/p/cocoahttpserver/
http://cocoawithlove.com/2009/07/simple-extensible-http-server-in-cocoa.html
http://culturedcode.com/cocoa/
The second one would be, to switch to a lower level framework (From QTKit to AudioToolbox).
You'd need more code, but there are some very good resources out there on how to stream mp3 using AudioToolbox.
e.g.:
http://cocoawithlove.com/2008/09/streaming-and-playing-live-mp3-stream.html
Personally I'd go with the second option. AudioToolbox isn't as straightforward as QTKit but it offers a clean solution to your problem. It's also available on both - iOS and Mac OS - so you will find plenty of information.
Update:
Did you try to use another initializer? e.g.
+ (id)movieWithAttributes:(NSDictionary *)attributes error:(NSError **)errorPtr
You can insert your URL for the key QTMovieURLAttribute and maybe you can compensate the missing content type by providing other attributes in that dictionary.
This open source project has a QTMovie category that contains methods to accomplish similar things:
http://vidnik.googlecode.com/svn-history/r63/trunk/Source/Categories/QTMovie+Async.m
If you thought weichsel's first solution was hacky, you're going to love this one:
The culprit is the Content-Type header, as you have determined. Had QTKit.framework used Objective-C internally, this would be a trivial matter of overriding -[NSHTTPURLResponse allHeaderFields] with a category of your choosing. However, QTKit.framework (for better or worse) uses Core Foundation (and Core Services) internally. These are both C-based frameworks and there is no elegant way of overriding functions in C.
That said, there is a method, just not a pretty one. Function interposition is even documented by Apple, but seems to be a bit behind the times, compared to the remainder of their documentation.
In essence, you want something along the following lines:
typedef struct interpose_s {
void *new_func;
void *orig_func;
} interpose_t;
CFStringRef myCFHTTPMessageCopyHeaderFieldValue (
CFHTTPMessageRef message,
CFStringRef headerField
);
static const interpose_t interposers[] __attribute__ ((section("__DATA, __interpose"))) = {
{ (void *)myCFHTTPMessageCopyHeaderFieldValue, (void *)CFHTTPMessageCopyHeaderFieldValue }
};
CFStringRef myCFHTTPMessageCopyHeaderFieldValue (
CFHTTPMessageRef message,
CFStringRef headerField
) {
if (CFStringCompare(headerField, CFSTR("Content-Type"), 0) == kCFCompareEqualTo) {
return CFSTR("audio/x-mpeg");
} else {
return CFHTTPMessageCopyHeaderFieldValue(message, headerField);
}
}
You might want to add logic specific to your application in terms of handling the Content-Type field lest your application break in weird and wonderful ways when every HTTP request is determined to be an audio file.
Try replacing http:// with icy://.
Just create an instance like this...
QTMovie *aPlayer = [QTMovie movieWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
fileUrl, QTMovieURLAttribute,
[NSNumber numberWithBool:YES], QTMovieOpenForPlaybackAttribute,
/*[NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute,*/
nil] error:error];