Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated README.md to latest version of Markdown by GitHub #58

Merged
merged 4 commits into from Aug 16, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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