Disappointed by the lack of Evernote love for the iPhone SDK I decided to port their python example to Cocoa. This code snippet should work with Cocoa and Cocoa Touch.
#import "EvernoteBackup.h"
#import "THTTPClient.h"
#import "TBinaryProtocol.h"
#import "UserStore.h"
#import "NoteStore.h"
@implementation EvernoteBackup
- (void)Test
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Keep this key private
NSString *consumerKey = [[[NSString alloc]
initWithString: @"YOUR_CONSUMER_KEY_HERE" ] autorelease];
NSString *consumerSecret = [[[NSString alloc]
initWithString: @"YOUR_CONSUMER_SECRET_HERE"] autorelease];
// For testing we use the sandbox server.
NSURL *userStoreUri = [[[NSURL alloc]
initWithString: @"https://sandbox.evernote.com/edam/user"] autorelease];
NSString *noteStoreUriBase = [[[NSString alloc]
initWithString: @"http://sandbox.evernote.com/edam/note/"] autorelease];
// These are for test purposes. At some point the user will provide his/her own.
NSString *username = [[[NSString alloc]
initWithString: @"YOUR_USERNAME_HERE"] autorelease];
NSString *password = [[[NSString alloc]
initWithString: @"YOUR_PASSWORD_HERE"] autorelease];
THTTPClient *userStoreHttpClient = [[[THTTPClient alloc]
initWithURL:userStoreUri] autorelease];
TBinaryProtocol *userStoreProtocol = [[[TBinaryProtocol alloc]
initWithTransport:userStoreHttpClient] autorelease];
EDAMUserStoreClient *userStore = [[[EDAMUserStoreClient alloc]
initWithProtocol:userStoreProtocol] autorelease];
EDAMNotebook* defaultNotebook = NULL;
BOOL versionOk = [userStore checkVersion:@"Cocoa EDAMTest" :
[EDAMUserStoreConstants EDAM_VERSION_MAJOR] :
[EDAMUserStoreConstants EDAM_VERSION_MINOR]];
if (versionOk == YES)
{
EDAMAuthenticationResult* authResult =
[userStore authenticate:username :password
:consumerKey :consumerSecret];
EDAMUser *user = [authResult user];
NSString *authToken = [authResult authenticationToken];
NSLog(@"Authentication was successful for: %@", [user username]);
NSLog(@"Authentication token: %@", authToken);
NSURL *noteStoreUri = [[[NSURL alloc]
initWithString:[NSString stringWithFormat:@"%@%@",
noteStoreUriBase, [user shardId]] ]autorelease];
THTTPClient *noteStoreHttpClient = [[[THTTPClient alloc]
initWithURL:noteStoreUri] autorelease];
TBinaryProtocol *noteStoreProtocol = [[[TBinaryProtocol alloc]
initWithTransport:noteStoreHttpClient] autorelease];
EDAMNoteStoreClient *noteStore = [[[EDAMNoteStoreClient alloc]
initWithProtocol:noteStoreProtocol] autorelease];
NSArray *notebooks = [[noteStore listNotebooks:authToken] autorelease];
NSLog(@"Found %d notebooks", [notebooks count]);
for (int i = 0; i < [notebooks count]; i++)
{
EDAMNotebook* notebook = (EDAMNotebook*)[notebooks objectAtIndex:i];
if ([notebook defaultNotebook] == YES)
{
defaultNotebook = notebook;
}
NSLog(@" * %@", [notebook name]);
}
NSLog(@"Creating a new note in default notebook: %@", [defaultNotebook name]);
// Skipping the image resource section...
EDAMNote *note = [[[EDAMNote alloc] init] autorelease];
[note setNotebookGuid:[defaultNotebook guid]];
[note setTitle:@"Test note from Cocoa Test."];
NSMutableString* contentString = [[[NSMutableString alloc] init] autorelease];
[contentString setString: @"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"];
[contentString appendString:@"<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml.dtd\">"];
[contentString appendString:@" <en-note>Here is the Olive Tree Test note.<br/>"];
[contentString appendString:@" </en-note>"];
[note setContent:contentString];
[note setCreated:(long long)[[NSDate date] timeIntervalSince1970] * 1000];
EDAMNote *createdNote = [noteStore createNote:authToken :note];
if (createdNote != NULL)
{
NSLog(@"Created note: %@", [createdNote title]);
}
}
[pool drain];
}
@end
Again your mileage may vary but I think this gives you the gist of how to talk to the Evernote servers.
#1 by Jason Purdy on October 10, 2009 - 5:41 pm
Quote
Hello again, I first had a crash on this line:
BOOL versionOk = [userStore checkVersion:@"Cocoa EDAMTest" :
[EDAMUserStoreConstants EDAM_VERSION_MAJOR] :
[EDAMUserStoreConstants EDAM_VERSION_MINOR]];
Xcode complained that the EDAM_VERSION_MINOR wasn’t known. I justed used actual values (1 and 13) and then ran into my second crash. An user exception on this line:
if ([result userExceptionIsSet]) {
@throw [result userException];
}
I’ve email evernote as well, but I’m sure it’s just a problem with my settings. Do i need to do anything to set up my soapbox?
thanks!
jason
#2 by Jason Purdy on October 10, 2009 - 5:54 pm
Quote
ahhhhhhh, duh. had to set up my account on the sandbox. So, if the user enters their name wrong an exception is throw? I’m guessing I’ll need to add in a lot of try/catches here?
#3 by Jason Purdy on October 10, 2009 - 5:54 pm
Quote
but ya, forgot to mention, i got it WORKING! Thanks so much for all this…now time to figure out how to post an image.
#4 by dctrotz on October 10, 2009 - 10:06 pm
Quote
Glad you got it working. Yeah you need to at the very least put a
@try{
// Evernote calls here.
}@catch(NSException *e) {
}
around your calls into the Evernote API otherwise the app will just die.
Hope that helps.
–
David
#5 by Jason Purdy on October 12, 2009 - 7:00 pm
Quote
thank you again for all your help. error handling is turning into a very difficult addition as i’m getting lots of crashes for each various error but working through it and i got my image to upload. thank you again for all your help!
#6 by Jason Purdy on October 14, 2009 - 12:59 pm
Quote
Hello David,
Thanks for the quick reply! I understand the en-media tag and have added that but I’m afraid I’m not quite understanding the MD5 hashing. This is my first time using MD5 encryption and I’m a bit confused about how it should work.
When creating the actual note’s resource I’m asked for a data object but then when creating the en-media tag I’m asked for a string. This is causing a userException in recv_createNote (NoteStore.m line 16233). I suspect my problem is here is the data vs string MD5 hashing. Here is my most recent creation of the content string:
NSString *imageDataMD5 = [self md5HexDigest:[[NSString alloc] initWithData:imageRawData encoding:NSASCIIStringEncoding]];
NSString *enMedia = [NSString stringWithFormat:@"%@%@%@", @""];
NSMutableString* contentString = [[[NSMutableString alloc] init] autorelease];
[contentString setString: @""];
[contentString appendString:@""];
[contentString appendString:@" Qipit White!"];
[contentString appendString:enMedia];
[contentString appendString:@" "];
[note setContent:contentString];
[note setCreated:(long long)[[NSDate date] timeIntervalSince1970] * 1000];
EDAMNote *createdNote = [noteStore createNote:authToken :note];
if (createdNote != NULL)
{
NSLog(@”Created note: %@”, [createdNote title]);
}
I’m basically having an issue with the image not quite being uploaded:
I have successfully added in Evernote support to our application and (I think) uploaded an image. When I click on the note in the web browser it just shows the message associated with the image. Not the image itself. Is this normal? When I use JotNot and look at my webpage in the normal URL (evernote.com) I see the full image. Am I uploading it wrong? Any reason the thumbnail would work fine like this: http://skitch.com/jasonpurdy/ndbuy/evernote-web-jasonpurdy-s-notebook but when I click on the thumbnail I don’t see anything?http://skitch.com/jasonpurdy/ndbub/evernote-web-test-note-from-cocoa-test
How I create the note:
//Image Data Init
NSData *imageRawData = [NSData dataWithContentsOfFile:[R3DUtils userFile:self.white.fileName]];
EDAMResource *imageResource = [[[EDAMResource alloc] init] autorelease];
EDAMData *imageData = [[EDAMData alloc] initWithBodyHash:imageRawData size:[imageRawData length] body:imageRawData];
[imageResource setData:imageData];
[imageResource setRecognition:imageData];
NSString *mime = [[[NSString alloc] initWithString:@”image/jpeg”]autorelease];
[imageResource setMime:mime];
NSArray *imageArray = [[NSArray alloc] initWithObjects:imageResource, nil];
[note setResources:imageArray];
Any help would be greatly appreciated!
Best,
Jason
#7 by Jason Purdy on October 14, 2009 - 1:01 pm
Quote
I guess I should reiterate that I’m pretty confident the issue is how I’m using MD5 for both my creating of the EDAMData (just pasing the MD5 NSData the raw data) and the contentString wanting an MD5 string.
thanks as always,
jason
#8 by Jason Purdy on October 14, 2009 - 1:17 pm
Quote
I just tried using the md5String i created and converting that to an NSData object for the note constructor to no avail:
NSData *md5Data = [imageDataMD5 dataUsingEncoding:NSASCIIStringEncoding];
any idea?
#9 by Jason Purdy on October 14, 2009 - 1:18 pm
Quote
also, can you have this email when you reply? i didn’t get a response the last time you responded. Thanks man!
#10 by Jason Purdy on October 15, 2009 - 10:20 am
Quote
ok, i’ve nailed down the specific part of my issue to being my creating of the MD5. As I mentioned this is my first foray into using MD5 and I’m a bit confused as to how this should work. I’ve found several examples for encryptions that take a string and return a string but I’m looking for something to take a NSData object and return a string. The below is the method I’m using. Specifically my NSData object is a UIImage. I’m trying to simple create an NSString using the “initWithData” but my MD5 is still incorrect. Any help would be greatly appreciated!
Best,
Jason
NSData *imageRawData = [NSData dataWithContentsOfFile:[myImage.fileName]];
NSString *imageDataMD5 = [self md5HexDigest:[[NSString alloc] initWithData:imageRawData encoding:NSASCIIStringEncoding]];
- (NSString*)md5HexDigest:(NSString*)input
{
const char* str = [input UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, strlen(str), result);
return [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]
];
}
#11 by dctrotz on October 15, 2009 - 10:33 am
Quote
Jason,
Thanks for trying to do the image stuff. I myself have not looked into this so I have nothing to offer here. Have you pinged the Evernote forums? They are pretty helpful with this stuff. Let me know how it turns out for you.
–
David
#12 by Jason Purdy on October 15, 2009 - 12:04 pm
Quote
Ya, i’ve been in touch with the evernote forums and their support staff. ended up using CocoaCrytoHashing : http://projects.stoneship.org/hg/cocoa_crypto_hashing
worked like a charm. All done! I’m sure you’re probably sick of me at this point.
thanks again for letting me rant on and on. Hopefully your email inbox didn’t get too full. hehe.
have a great day.
jason
#13 by Wesley on February 3, 2010 - 8:37 am
Quote
This code runs as is for me with one ahhhhh exception
It crash’s at [pool drain]; with EXC_BAD_ACCESS. The stack trace shows that it’s trying to dealloc the listnotebooks_results:
#1 0×10001469f in -[EDAMListNotebooks_result dealloc] at NoteStore.m:4200
I’m 3 months into Objective-C programing and still trying to get a good grasp on memory management. It’s not clear to me what the problem is. Might you have a pointer or 2? Puns are us…
I’ll look into putting in exception handling, but I’m guessing that may not be the issue given where the crash occurs.
Thanks for the code, it’s a huge help.
#14 by Matt on February 8, 2010 - 2:44 pm
Quote
Thanks so much for this. I had my first app accessing evernote up in an evening, there is NO WAY that would have happened without your converting this sample (I’m very new to objective-c
).
FYI, you might just want to update your post on getting set up to mention that the sdk downloaded from EN now includes the prerequisite stuff. I initially followed your step-by-step and got really confused with the multiply defined linker symbols :p
#15 by Edgar on April 14, 2010 - 6:06 am
Quote
Hi.
I also had the same problems as Jason, and I spent a few hours looking for a solution. But I ended up using CocoaCrytoHashing and now I am able to see my images inside the note.
Thanks a lot for this post. It helped me a lot
#16 by Senseful on May 18, 2010 - 3:25 pm
Quote
For others that are getting the EXC_BAD_ACCESS, you need to fix the code. On the line that creates the notebooks:
NSArray *notebooks = [[noteStore listNotebooks:authToken] autorelease];
You need to remove the autorelease so that it becomes:
NSArray *notebooks = [noteStore listNotebooks:authToken];
This is because the object being returned is already marked as autorelease. It is a bug in the example and should be corrected.