Skip to content

Commit

Permalink
Merge pull request #58 from michaelchum/master
Browse files Browse the repository at this point in the history
Updated README.md to latest version of Markdown by GitHub
  • Loading branch information
lukhnos committed Aug 16, 2014
2 parents 104248f + ebccbd3 commit d1f26f9
Showing 1 changed file with 155 additions and 119 deletions.
274 changes: 155 additions & 119 deletions README.markdown → README.md
Expand Up @@ -70,7 +70,9 @@ that make use of the tool to manage ObjectiveFlickr for you.

1. Check out the code from github:

git clone git://github.com/lukhnos/objectiveflickr.git
```bash
git clone git://github.com/lukhnos/objectiveflickr.git
```

2. Supply your own API key and shared secret. You need to copy
`SimpleAPIKey.h.template` to `SimpleAPIKey.h`, and fill in the two
Expand All @@ -81,7 +83,9 @@ that make use of the tool to manage ObjectiveFlickr for you.
3. Remember to make your API key a "web app", and set the *Callback URL*
(not the *Application URL*!) to:

snapnrun://auth?
```xml
snapnrun://auth?
```

4. Build and run SnapAndRun for iPhone. The project is located at
`Examples/SnapAndRun-iPhone`
Expand Down Expand Up @@ -142,8 +146,10 @@ Because iPhone SDK does not allow dynamically linked frameworks and bundles, we
two paths, separately (`<OF root>` is where you checked out
ObjectiveFlickr):

<OF root>/Source
<OF root>/LFWebAPIKit
```xml
<OF root>/Source
<OF root>/LFWebAPIKit
```
6. Use `#import "ObjectiveFlickr.h"` in your project

Expand Down Expand Up @@ -175,41 +181,51 @@ Typically, to develop a Flickr app for Mac or iPhone, you need to follow the fol
1. Get you Flickr API key at <http://www.flickr.com/services/api/keys/apply/>
2. Create an OFFlickrAPIContext object

OFFlickrAPIContext *context = [[OFFlickrAPIContext alloc] initWithAPIKey:YOUR_KEY sharedSecret:YOUR_SHARED_SECRET];
```obj-c
OFFlickrAPIContext *context = [[OFFlickrAPIContext alloc] initWithAPIKey:YOUR_KEY sharedSecret:YOUR_SHARED_SECRET];
```
3. Create an OFFlickrAPIRequest object where appropriate, and set the delegate
OFFlickrAPIRequest *request = [[OFFlickrAPIRequest alloc] initWithAPIContext:context];
// set the delegate, here we assume it's the controller that's creating the request object
[request setDelegate:self];
```obj-c
OFFlickrAPIRequest *request = [[OFFlickrAPIRequest alloc] initWithAPIContext:context];
// set the delegate, here we assume it's the controller that's creating the request object
[request setDelegate:self];
```

4. Implement the delegate methods.

- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary;
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didFailWithError:(NSError *)inError;
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest imageUploadSentBytes:(NSUInteger)inSentBytes totalBytes:(NSUInteger)inTotalBytes;
```obj-c
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary;
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didFailWithError:(NSError *)inError;
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest imageUploadSentBytes:(NSUInteger)inSentBytes totalBytes:(NSUInteger)inTotalBytes;
```

All three methods are optional ("informal protocol" in old Objective-C
speak; optional protocol methods in newspeak). *Nota bene*: If you
are using Mac OS X 10.4 SDK, or if you are using 10.5 SDK but targeting
10.4, then the delegate methods are declared as informal protocols.
In all other cases (OS X 10.5 and above or iPhone apps), you need to
specify you are adopting the OFFlickrAPIRequestDelegate protocol. *E.g.*:

@interface MyViewController : UIViewController <OFFlickrAPIRequestDelegate>

```obj-c
@interface MyViewController : UIViewController <OFFlickrAPIRequestDelegate>
```
5. Call the Flickr API methods you want to use. Here are a few examples.
Calling `flickr.photos.getRecent` with the argument `per_page` = `1`:

[request callAPIMethodWithGET:@"flickr.photos.getRecent" arguments:[NSDictionary dictionaryWithObjectsAndKeys:@"1", @"per_page", nil]]

```obj-c
[request callAPIMethodWithGET:@"flickr.photos.getRecent" arguments:[NSDictionary dictionaryWithObjectsAndKeys:@"1", @"per_page", nil]]
```

Quite a few Flickr methods require that you call with HTTP POST
(because those methods write or modify user data):

[request callAPIMethodWithPOST:@"flickr.photos.setMeta" arguments:[NSDictionary dictionaryWithObjectsAndKeys:photoID, @"photo_id", newTitle, @"title", newDescription, @"description", nil]];
```obj-c
[request callAPIMethodWithPOST:@"flickr.photos.setMeta" arguments:[NSDictionary dictionaryWithObjectsAndKeys:photoID, @"photo_id", newTitle, @"title", newDescription, @"description", nil]];
```
6. Handle the response or error in the delegate methods. If an error
occurs, an NSError object is passed to the error-handling delegate
Expand All @@ -229,9 +245,11 @@ To upload a picture, create an NSInputStream object from a file path
or the image data (NSData), then make the request. Here in the example
we assume we already have obtained the image data in JPEG, and we set
make private the uploaded picture:

NSInputStream *imageStream = [NSInputStream inputStreamWithData:imageData];
[request uploadImageStream:imageStream suggestedFilename:@"Foobar.jpg" MIMEType:@"image/jpeg" arguments:[NSDictionary dictionaryWithObjectsAndKeys:@"0", @"is_public", nil]];
```obj-c
NSInputStream *imageStream = [NSInputStream inputStreamWithData:imageData];
[request uploadImageStream:imageStream suggestedFilename:@"Foobar.jpg" MIMEType:@"image/jpeg" arguments:[NSDictionary dictionaryWithObjectsAndKeys:@"0", @"is_public", nil]];
```
Upload progress will be reported to the delegate method
`flickrAPIRequest:imageUploadSentBytes:totalBytes:`
Expand Down Expand Up @@ -412,97 +430,111 @@ XML to property list following the three simple rules:

So, for example, this is a sample response from flickr.auth.checkToken

<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<auth>
<token>aaaabbbb123456789-1234567812345678</token>
<perms>write</perms>
<user nsid="00000000@N00" username="foobar" fullname="blah" />
</auth>
</rsp>
```xml
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<auth>
<token>aaaabbbb123456789-1234567812345678</token>
<perms>write</perms>
<user nsid="00000000@N00" username="foobar" fullname="blah" />
</auth>
</rsp>
```

Then in your `flickrAPIRequest:didCompleteWithResponse:` delegate method,
if you dump the received response (an NSDictionary object) with NSLog,
you'll see something like (extraneous parts omitted):

{
auth ={
perms = { "_text" = write };
token = { "_text" = "aaaabbbb123456789-1234567812345678"; };
user = {
fullname = "blah";
nsid = "00000000@N00";
username = foobar;
};
```js
{
auth ={
perms = { "_text" = write };
token = { "_text" = "aaaabbbb123456789-1234567812345678"; };
user = {
fullname = "blah";
nsid = "00000000@N00";
username = foobar;
};
stat = ok;
}
};
stat = ok;
}
```

So, say, if we are interested in the retrieved auth token, we can do this:

NSString *authToken = [[inResponseDictionary valueForKeyPath:@"auth.token"] textContent];
```obj-c
NSString *authToken = [[inResponseDictionary valueForKeyPath:@"auth.token"] textContent];
```
Here, our own `-[NSDictionary textContent]` is simply a convenient method
that is equivalent to calling `[authToken objectForKey:OFXMLTextContentKey]`
in our example.
Here is another example returned by `flickr.photos.getRecent`:
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<photos page="1" pages="334" perpage="3" total="1000">
<photo id="3444583634" owner="37096380@N08" secret="7bbc902132" server="3306" farm="4" title="studio_53_1" ispublic="1" isfriend="0" isfamily="0" />
<photo id="3444583618" owner="27122598@N06" secret="cc76db8cf8" server="3327" farm="4" title="IMG_6830" ispublic="1" isfriend="0" isfamily="0" />
<photo id="3444583616" owner="26073312@N08" secret="e132988dc3" server="3376" farm="4" title="Cidade Baixa" ispublic="1" isfriend="0" isfamily="0" />
</photos>
</rsp>
```xml
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<photos page="1" pages="334" perpage="3" total="1000">
<photo id="3444583634" owner="37096380@N08" secret="7bbc902132" server="3306" farm="4" title="studio_53_1" ispublic="1" isfriend="0" isfamily="0" />
<photo id="3444583618" owner="27122598@N06" secret="cc76db8cf8" server="3327" farm="4" title="IMG_6830" ispublic="1" isfriend="0" isfamily="0" />
<photo id="3444583616" owner="26073312@N08" secret="e132988dc3" server="3376" farm="4" title="Cidade Baixa" ispublic="1" isfriend="0" isfamily="0" />
</photos>
</rsp>
```

And the mapped property list looks like:

{
photos = {
page = 1;
pages = 334;
perpage = 3;
photo = (
{
farm = 4;
id = 3444583634;
isfamily = 0;
isfriend = 0;
ispublic = 1;
owner = "37096380@N08";
secret = 7bbc902132;
server = 3306;
title = "studio_53_1";
},
{
farm = 4;
id = 3444583618;
/* ... */
},
{
farm = 4;
id = 3444583616;
/* ... */
}
);
total = 1000;
};
stat = ok;
}

```js
{
photos = {
page = 1;
pages = 334;
perpage = 3;
photo = (
{
farm = 4;
id = 3444583634;
isfamily = 0;
isfriend = 0;
ispublic = 1;
owner = "37096380@N08";
secret = 7bbc902132;
server = 3306;
title = "studio_53_1";
},
{
farm = 4;
id = 3444583618;
/* ... */
},
{
farm = 4;
id = 3444583616;
/* ... */
}
);
total = 1000;
};
stat = ok;
}
```

ObjectiveFlickr knows to translate the enclosed <photo> tags in the plural
<photos> tag into an NSArray. So if you want to retrieve the second photo
in the array, you can do this:

NSDictionary *photoDict = [[inResponseDictionary valueForKeyPath:@"photos.photo"] objectAtIndex:1];
```obj-c
NSDictionary *photoDict = [[inResponseDictionary valueForKeyPath:@"photos.photo"] objectAtIndex:1];
```
Then, with two helper methods from `OFFlickrAPIContext`, you can get the
static photo source URL and the photo's original web page URL:
NSURL *staticPhotoURL = [flickrContext photoSourceURLFromDictionary:photoDict size:OFFlickrSmallSize];
NSURL *photoSourcePage = [flickrContext photoWebPageURLFromDictionary:photoDict];
```obj-c
NSURL *staticPhotoURL = [flickrContext photoSourceURLFromDictionary:photoDict size:OFFlickrSmallSize];
NSURL *photoSourcePage = [flickrContext photoWebPageURLFromDictionary:photoDict];
```

Do remember that Flickr requires you present a link to the photo's web page
wherever you show the photo in your app. So design your UI accordingly.
Expand All @@ -517,23 +549,25 @@ attributes removed to highlight the issue at hand), from the API method
[flickr.activity.userPhotos](
http://www.flickr.com/services/api/flickr.activity.userPhotos.html):

<rsp stat="ok">
<items page="1" pages="1" perpage="50" total="3">
<item type="photo">
<title>Snap and Run Demo</title>
<activity>
<event type="comment">double comment 1</event>
<event type="comment">double comment 2</event>
</activity>
</item>
<item type="photo">
<title>Snap and Run Demo</title>
<activity>
<event type="comment">test comment 1</event>
</activity>
</item>
</items>
</rsp>
```xml
<rsp stat="ok">
<items page="1" pages="1" perpage="50" total="3">
<item type="photo">
<title>Snap and Run Demo</title>
<activity>
<event type="comment">double comment 1</event>
<event type="comment">double comment 2</event>
</activity>
</item>
<item type="photo">
<title>Snap and Run Demo</title>
<activity>
<event type="comment">test comment 1</event>
</activity>
</item>
</items>
</rsp>
```

Note how the `<activity>` tag can enclose *either* one *or* more
`<event>` tags. This is actually a gray area of Flickr API and I'm not
Expand All @@ -544,21 +578,23 @@ could never be comprehensive.
We can make good use of Objective-C's dynamic nature to work around the
problem. We can tell if it's an array:

// get the first element in the items
NSArray *itemArray = [responseDict valueForKeyPath:@"items.item"];
NSDictionary *firstItem = [itemArray objectAtIndex:0];

// get the "event" element and see if it's an array
id event = [firstItem valueForKeyPath:@"activity.event"];
if ([event isKindOfClass:[NSArray class]]) {
// it has more than one elements
NSDictionary *someEvent = [event objectAtIndex:0];
}
else {
// that's the only element
NSDictionary *someEvent = event;
}
```obj-c
// get the first element in the items
NSArray *itemArray = [responseDict valueForKeyPath:@"items.item"];
NSDictionary *firstItem = [itemArray objectAtIndex:0];

// get the "event" element and see if it's an array
id event = [firstItem valueForKeyPath:@"activity.event"];

if ([event isKindOfClass:[NSArray class]]) {
// it has more than one elements
NSDictionary *someEvent = [event objectAtIndex:0];
}
else {
// that's the only element
NSDictionary *someEvent = event;
}
```
On the other hand, the reason why we build the plural tag rule in
`OFXMLMapper` is that writing the boilerplate above for frequently-used
Expand Down

0 comments on commit d1f26f9

Please sign in to comment.