Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

ObjectiveFlickr, a Flickr API framework for Objective-C

NotificationsYou must be signed in to change notification settings

lukhnos/objectiveflickr

Repository files navigation

ObjectiveFlickr is a Flickr API framework designed for Mac and iPhone apps.

OAuth Support

ObjectiveFlickr now supports Flickr's new OAuth-based authentication process.I'll post more about it in the coming week. The SnapAndRun sample is updatedto reflect the usage. A new Mac sample, OAuthTransitionMac, demonstrateshow to use the new OAuth-based API and also how to migrate your existingauth tokens. OAuthTransitionMac uses ARC and therefore also shows how touse ObjectiveFlickr, which is a non-ARC library, with an ARC app.

iOS support in ObjectiveFlickr was developed mostly during the iPhone OS 2.0days and it shows. More update work will be required to reflect the changesin the iOS development process. Your contribution (like updating thisREADME, submitting new samples or test cases) is greatly welcome -- and itwill benefit the iOS open source development community, too!

Update. Please refer tomy blog postfor the steps you need to take for the transition.

What's New in 2.0

Version 2.0 is a complete rewrite, with design integrity and extensibility inmind. Differences from 0.9.x include:

  • The framework now builds with all major Apple SDKs: Mac OS X 10.4,10.5, iPhone OS 2.2.x, and other beta version platforms to which I haveaccess. It also builds on both 32-bit and 64-bit platforms.
  • Ordinary request and upload request are now unified into oneOFFlickrAPIRequest class
  • 2.0 no longer depends on NSXMLDocument, which is not available in iPhoneSDK. It now maps Flickr's XML response into an NSDictionary using onlyNSXMLParser, which is available on all Apple platforms.
  • Image uploading employs temp file. This allows ObjectiveFlickr to operatein memory-constrained settings.
  • Error reporting now uses NSError to provide more comprehensive information,especially error code and message from Flickr.

If you already use ObjectiveFlickr 0.9.x, the bad news is that 2.0 is notbackward compatible. The good news, though, is that it uses a different setof class names. Some migration tips are offered near the end of this document.

What's Not (Yet) There

There are of course quite a few to-do's:

  • In-source API documentation
  • Unit testings
  • Flickr API coverage tests (challenging though—how do you test a movingtarget?)
  • ObjectiveFlickr 0.9.x has a few convenient methods and tricks to simplifymethod calling; they are not ported here (yet, or will never be)

Quick Start: Example Apps You Can Use

UPDATE 2.0.4: If you useCocoaPods, you shouldcheck outthe new sample projectsthat 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
  1. Supply your own API key and shared secret. You need to copySampleAPIKey.h.template toSampleAPIKey.h, and fill in the twomacros there. If you don't have an API key, apply for yours at:http://www.flickr.com/services/api/keys/apply/ .Make sure you have understood their terms and conditions.

  2. Remember to make your API key a "web app", and set theCallback URL(not theApplication URL!) to:

snapnrun://auth?
  1. Build and run SnapAndRun for iPhone. The project is located atExamples/SnapAndRun-iPhone

  2. Build and run RandomPublicPhoto for Mac. The project is atExamples/RandomPublicPhoto

Adding ObjectiveFlickr to Your Project

UPDATE 2.0.4: This section shows its age and needs updating. Pull requestson an up-to-date instruction will be appreciated! Meanwhile, if you useCocoaPods, you can easily add ObjectiveFlickr to yourproject by adding this one line to youpodfile:

pod 'objectiveflickr'

Then just runpod install and start using ObjectiveFlickr.

Adding ObjectiveFlickr to Your Mac App Project

  1. Add ObjectiveFlickr.xcodeproj to your Mac project (from Xcode menuProject > Add to Project...)
  2. On your app target, open the info window (usingGet Info on thetarget), then in theGeneral tab, addObjectiveFlickr (framework)toDirect Dependencies
  3. Add a newCopy Files phase, and chooseFramework for theDestination (in its own info window)
  4. DragObjecitveFlickr.framework from the Groups & Files panel in Xcode(under the addedObjectiveFlickr.xcodeproj) to the newly createdCopyFiles phase
  5. DragObjecitveFlickr.framework once again to the target'sLinked BinaryWith Libraries group
  6. Open the Info window of your target again. SetConfiguration toAllConfigurations, then in theFramework Search Paths property, add$(TARGET_BUILD_DIR)/$(FRAMEWORKS_FOLDER_PATH)
  7. Use#import <ObjectiveFlickr/ObjectiveFlickr.h> in your project

Adding ObjectiveFlickr to Your iPhone App Project

Because iPhone SDK does not allow dynamically linked frameworks and bundles, we need to link against ObjectiveFlickr statically.

  1. Add ObjectiveFlickr.xcodeproj to your Mac project (from Xcode menuProject > Add to Project...)
  2. On your app target, open the info window (usingGet Info on thetarget), then in theGeneral tab, addObjectiveFlickr (library) toDirect Dependencies
  3. Also, in the same window, addCFNetwork.framework toLinked Libraries
  4. DraglibObjecitveFlickr.a to the target'sLinked Binary With Librariesgroup
  5. Open the Info window of your target again. SetConfiguration toAllConfigurations, then in theHeader Search Paths property, add thesetwo paths, separately (<OF root> is where you checked outObjectiveFlickr):
<OF root>/Source<OF root>/LFWebAPIKit
  1. Use#import "ObjectiveFlickr.h" in your project

Key Ideas and Basic Usage

ObjectiveFlickr is an asynchronous API. Because of the nature of GUIapp, all ObjectiveFlickr requests are asynchronous. You make a request, thenObjectiveFlickr calls back your delegate methods and tell you if a requestsucceeds or fails.

ObjectiveFlickr is a minimalist framework. The framework has essentiallyonly two classes you have to deal with:OFFlickrAPIContext andOFFlickrAPIRequest. Unlike many other Flickr API libraries, ObjectiveFlickrdoesnot have classes like FlickrPhoto, FlickrUser, FlickrGroup orwhathaveyou. You call a Flickr method, likeflickr.photos.getInfo, and get back a dictionary (hash or map in other languages) containing the key-valuepairs of the result. The result isdirectly mapped from Flickr's ownXML-formatted response. Because they are alreadystructured data,ObjectiveFlickr does not translate further into other object classes.

Because of the minimalist design, you also need to have basic understanding ofhow Flickr API works. Refer tohttp://www.flickr.com/services/api/ forthe details. But basically, all you need to know is the methods you want tocall, and which XML data (the key-values) Flickr will return.

Typically, to develop a Flickr app for Mac or iPhone, you need to follow the following steps:

  1. Get you Flickr API key athttp://www.flickr.com/services/api/keys/apply/
  2. Create an OFFlickrAPIContext object
OFFlickrAPIContext *context = [[OFFlickrAPIContextalloc]initWithAPIKey:YOUR_KEYsharedSecret:YOUR_SHARED_SECRET];
  1. Create an OFFlickrAPIRequest object where appropriate, and set the delegate
OFFlickrAPIRequest *request = [[OFFlickrAPIRequestalloc]initWithAPIContext:context];// set the delegate, here we assume it's the controller that's creating the request object[requestsetDelegate:self];
  1. 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;
All three methods are optional ("informal protocol" in old Objective-C speak; optional protocol methods in newspeak). *Nota bene*: If youare using Mac OS X 10.4 SDK, or if you are using 10.5 SDK but targeting10.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 tospecify you are adopting the OFFlickrAPIRequestDelegate protocol. *E.g.*:
@interfaceMyViewController :UIViewController <OFFlickrAPIRequestDelegate>
  1. Call the Flickr API methods you want to use. Here are a few examples.

    Callingflickr.photos.getRecent with the argumentper_page =1:

[requestcallAPIMethodWithGET:@"flickr.photos.getRecent"arguments:[NSDictionarydictionaryWithObjectsAndKeys:@"1",@"per_page",nil]]
Quite a few Flickr methods require that you call with HTTP POST(because those methods write or modify user data):
[requestcallAPIMethodWithPOST:@"flickr.photos.setMeta"arguments:[NSDictionarydictionaryWithObjectsAndKeys:photoID,@"photo_id", newTitle,@"title", newDescription,@"description",nil]];
  1. Handle the response or error in the delegate methods. If an erroroccurs, an NSError object is passed to the error-handling delegatemethod. If the error object's domain isOFFlickrAPIReturnedErrorDomain,then it's a server-side error. You can refer to Flickr's API documentationfor the meaning of the error. If the domain isOFFlickrAPIRequestErrorDomain, it's client-side error, usually causedby lost network connection or transfer timeout.

    We will now talk about the response.

How to Upload a Picture

To upload a picture, create an NSInputStream object from a file pathor the image data (NSData), then make the request. Here in the examplewe assume we already have obtained the image data in JPEG, and we setmake private the uploaded picture:

NSInputStream *imageStream = [NSInputStreaminputStreamWithData:imageData];[requestuploadImageStream:imageStreamsuggestedFilename:@"Foobar.jpg"MIMEType:@"image/jpeg"arguments:[NSDictionarydictionaryWithObjectsAndKeys:@"0",@"is_public",nil]];

Upload progress will be reported to the delegate methodflickrAPIRequest:imageUploadSentBytes:totalBytes:

The reason why ObjectiveFlickr asks for an NSInputStream object as inputis that we don't want to read in the whole image into memory for thepreparation of upload data. With NSInputStream you have the flexibilityof feeding ObjectiveFlickr an in-memory image data, a file, or even avirtualized image byte stream that comes from different (e.g. partitioned)sources.

Make sure you have readFlickr's upload API documentation so that you understandhow to pick up the upload result. Please note that between the completionof uploading and the completion of the HTTP POST request itself (the momentat which you receive the response), there can be along wait. So make sureyou have a long timeout interval, especially when you upload a large image,and also design your UI accordingly.

Auth Considerations

Deprecated. The original authentication process is now deprecated by Flickr.Please refer tomy blog postfor the steps you need to take for the new setup.

If your app does not just read public photos, your app will need to getuser permission for accessing their photos. You need to use Flickr'sauthentication/authorization mechanism (hereafter "auth" to cover bothsteps) to get theauthToken for your later access.

This is, frankly, the most difficult part in using the whole Flickr API;anything that comes after that is easy and (usually) smooth. That aloneis worth a whole tutorial, but I'll try to explain the essentials.

Before that, get to know Flickr's own doc here:http://www.flickr.com/services/api/misc.userauth.html.

There are two types of app auth:

There is actually a "mobile app" auth, designed for feature phones orsmart phones that aren't, well, not really smart, if you buy what Applesays. But since we are talking aboutMac andiPhone apps, and theyaren't any ye olde mobile platform, we'll skip that one and go straightinto the two major types of app auth.

Desktop App Auth, the Old Way (Deprecated)

Deprecated. The original authentication process is now deprecated by Flickr.Please refer tomy blog postfor the steps you need to take for the new setup.

Before, Mac developers were only interested in Desktop app auth. If youhave used any Mac Flickr app before (FlickrExport, HoudahGeo, Posterino, andmany others), you know how it works:

  1. Open the app
  2. The app presents a dialog box, telling you it's going to open the webbrowser. You log into Flickr, Flickr asks you if you grant permissionto the app currently asking for your permission (read, write, ordelete access).
  3. After you grant the permission, youswitch back to the app,hit some "Continue" button on the app the dialog box.
  4. The app fetches the auth token from Flickr, and completes the process.

To map that into your app's internal workings, you need to do these:

  1. Callflickr.auth.getFrob
  2. After you receive the frob, pass the wholeinResponseDictionary to-[OFFlickrAPIContext loginURLFromFrobDictionary:requestedPermission:]and get the returned NSURL object.
  3. Tell user that you're going to open the browser for them, prompt foraction.
  4. Open the browser with the URL you just got, then wait
  5. After user completes the auth, she will click on the "Continue" button(or something like that).
  6. Your app then callsflickr.auth.getToken to getthe auth token
  7. Assign the auth token to your current Flickr API context with-[OFFlickrAPIContext setAuthToken:]
  8. That's it. ObjectiveFlickr will add theauth_token argument to allyour subsequent API calls, and you now have the access to all the APIsto which the user has grant you permission.

iPhone App Auth and the New Way (Deprecated)

Deprecated. The original authentication process is now deprecated by Flickr.Please refer tomy blog postfor the steps you need to take for the new setup.

iPhone and iPod Touch posed a challenge to the auth model above: Openingup Mobile Safari then ask the forgetful user to come back is a bad idea.

So many iPhone developers have come up with this brilliant idea: UseURL scheme to launch your app. It turns out that Flickr's web app authserves the idea well. Here is how it works:

  1. The app prompts user that it's going to open up browser to ask forpermission.
  2. The user taps some "Open" button, and the app closes, Mobile Safaripops up with Flickr's login (and then app auth) page.
  3. Then magically, Mobile Safari closes, and the app is launched again.
  4. There's no Step 4.

What's behind the scene is that the iPhone app in question has registereda URL scheme, for examplesomeapp:// in itsInfo.plist and the appdeveloper has configured their Flickr API key, so that when the usergrants the app permission, Flickr will redirect the web page to thatURLthe app developer has previously designated. Mobile Safari opens thatURL, and then the app is launched.

In fact, Mac app can do that, too!

Here's what you need to do:

  1. Register the URL scheme. Take a look at my own SnapAndRun-iPhoneexample app'sSnapAndRun-Info.plist and thisCocoaDev article for details.
  2. Configure your Flickr API key so that thecallback URL is set tothat URL scheme.
  3. Get a login URL by calling-[OFFlickrAPIContext loginURLFromFrobDictionary:requestedPermission:],note that you don't need to have a frob dictionary for getting web applogin (auth) URL, so just pass nil. You still need to pass thepermission argument, of course.
  4. Now, in your app launch URL handler (Mac and iPhone apps do itdifferently, see Apple doc for details), get the frob that Flickrhas passed to you with the URL.
  5. Your app then callsflickr.auth.getToken to getthe auth token
  6. Assign the auth token to your current Flickr API context with-[OFFlickrAPIContext setAuthToken:]
  7. That's it. ObjectiveFlickr will add theauth_token argument to allyour subsequent API calls, and you know have the access to all the APIsto which the user has grant you permission.

Now you have done the most difficult part of using the Flickr API.

How Flickr's XML Responses Are Mapped Back

Flickr's default response format is XML. You can opt for JSON. Whicheverformat you choose, the gist is thatthey are already structured data.When I first started designing ObjectiveFlickr, I found it unnecessary tocreate another layer of code that maps those data to and from "native"objects. So we don't have things likeOFFlickrPhoto orOFFlickrGroup.In essence, when an request object receives a response, it maps the XMLinto a data structure consisting of NSDictionary's, NSArray's andNSString's. In Apple speak, this is known as "property list". And we'lluse that term to describe the mapped result. You then read out in the propertylist the key-value pairs you're interested in.

ObjectiveFlickr uses the XML format to minimize dependency. It parses theXML with NSXMLParser, which is available on all Apple platforms. It mapsXML to property list following the three simple rules:

  1. All XML tag properties are mapped to NSDictionary key-value pairs
  2. Text node (e.g.<photoid>12345</photoid>) is mapped as a dictionarycontaining the keyOFXMLTextContentKey (a string const) with its valuebeing the text content.
  3. ObjectiveFlickr knows when to translate arrays. We'll see how this isdone now.

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

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

Then in yourflickrAPIRequest: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;};};stat=ok;}

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

NSString *authToken = [[inResponseDictionaryvalueForKeyPath:@"auth.token"]textContent];

Here, our own-[NSDictionary textContent] is simply a convenient methodthat is equivalent to calling[authToken objectForKey:OFXMLTextContentKey]in our example.

Here is another example returned byflickr.photos.getRecent:

<?xml version="1.0" encoding="utf-8" ?><rspstat="ok"><photospage="1"pages="334"perpage="3"total="1000"><photoid="3444583634"owner="37096380@N08"secret="7bbc902132"server="3306"farm="4"title="studio_53_1"ispublic="1"isfriend="0"isfamily="0" /><photoid="3444583618"owner="27122598@N06"secret="cc76db8cf8"server="3327"farm="4"title="IMG_6830"ispublic="1"isfriend="0"isfamily="0" /><photoid="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;}

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

NSDictionary *photoDict = [[inResponseDictionaryvalueForKeyPath:@"photos.photo"]objectAtIndex:1];

Then, with two helper methods fromOFFlickrAPIContext, you can get thestatic photo source URL and the photo's original web page URL:

NSURL *staticPhotoURL = [flickrContextphotoSourceURLFromDictionary:photoDictsize:OFFlickrSmallSize];NSURL *photoSourcePage = [flickrContextphotoWebPageURLFromDictionary:photoDict];

Do remember that Flickr requires you present a link to the photo's web pagewherever you show the photo in your app. So design your UI accordingly.

Wacky XML Mappings

Unfortunately, there are some Flickr responses that don't rigorously followthe "plural tag == array" rule. Consider the following snippet (tagattributes removed to highlight the issue at hand), from the API methodflickr.activity.userPhotos:

<rspstat="ok"><itemspage="1"pages="1"perpage="50"total="3"><itemtype="photo"><title>Snap and Run Demo</title><activity><eventtype="comment">double comment 1</event><eventtype="comment">double comment 2</event></activity></item><itemtype="photo"><title>Snap and Run Demo</title><activity><eventtype="comment">test comment 1</event></activity></item></items></rsp>

Note how the<activity> tag can encloseeither oneor more<event> tags. This is actually a gray area of Flickr API and I'm notentirely sure if I should write that exception into the book (i.e. thelogic ofOFXMLMapper, which handles the job). The list of exceptionscould never be comprehensive.

We can make good use of Objective-C's dynamic nature to work around theproblem. We can tell if it's an array:

// get the first element in the itemsNSArray *itemArray = [responseDictvalueForKeyPath:@"items.item"];NSDictionary *firstItem = [itemArrayobjectAtIndex:0];// get the "event" element and see if it's an arrayid event = [firstItemvalueForKeyPath:@"activity.event"];if ([eventisKindOfClass:[NSArrayclass]]) {// it has more than one elementsNSDictionary *someEvent = [eventobjectAtIndex:0];}else {// that's the only elementNSDictionary *someEvent = event;}

On the other hand, the reason why we build the plural tag rule inOFXMLMapper is that writing the boilerplate above for frequently-usedtags again and again is very tedious. SinceOFXMLMapper already handlesthe plural tags, the borderline cases are easier to tackle.

Design Patterns and Tidbits

Design Your Own Trampolines

OFFlickrAPIRequest has asessionInfo property that you can use to providestate information of your app. However, it will soon become tedious to writetons ofif-elses in the delegate methods. My experience is that I designa customized "session" object with three properties: delegate (that doesn'thave to be the originator of the request), selector to call on completion,selector to call on error. Then the delegate methods forOFFlickrAPIRequestsimply dispatches the callbacks according to the session object.

If your controller calls a number of Flickr methods or involves multiplestages/states, this design pattern will be helpful.

Thread-Safety

Each OFFlickrAPIRequest object can be used in the thread on which it is created. Do not pass them across threads. Delegate methods are also called in the thread in which the request object is running.

CFNetwork-Based

ObjectiveFlickr uses LFHTTPRequest, which uses only the CFNetwork stack.NSURLConnection is reported to have its own headaches. On the other hand,LFHTTPRequest does not handle non-HTTP URLs (it does handle HTTPS witha catch: on iPhone you cannot use untrusted root certs) and does not doHTTP authentication. It also does not manage caching. For web APIintegration, however, LFHTTPRequest provides a lean way of making andmanaging requests.

One side note: LFHTTPRequest will use your system's shared proxy settingson your Mac or iPhone. This is how it requiresSystemConfiguration.frameworkon Mac when being built alone.

Possible Migration Path from ObjectiveFlickr 0.9.x

I didn't really seriously investigate this, but here are some thoughts:

  • In theory 0.9.x and 2.0 should co-exist as there is no class name clashes.
  • OFFlickrContext becomes OFFlickrAPIContext
  • OFFlickrInvocation becomes OFFlickrAPIRequest
  • OFFlickrUploader is now merged into OFFlickrAPIRequest
  • Delegate methods are greatly simplified and redesigned

History

ObjectiveFlickr was first released in late 2006. The previous version, 0.9.x,has undergone one rewrite and is hosted onGoogleCode. It also has a Ruby versionavailable as aRuby gem.

The present rewrite derives from the experiences that I have had indeveloping Mac and iPhone products (I run my own company,Lithoglyph). It's a great learning process.

Acknowledgements

Many people have given kind suggestions and directions to the developmentof ObjectiveFlickr. And there are a number of Mac apps that use it. I'd liketo thank Mathieu Tozer, Tristan O'Tierney, Christoph Priebe, Yung-Lun Lan,and Pierre Bernard for the feedbacks that eventually lead to the framework'spresent design and shape.

Copyright and Software License

ObjectiveFlickr Copyright (c) 2006-2009 Lukhnos D. Liu.

LFWebAPIKit Copyright (c) 2007-2009 Lukhnos D. Liu and Lithoglyph Inc.

One test in LFWebAPIKit (Tests/StreamedBodySendingTest) makesuse ofGoogle Toolbox for Mac, Copyright (c) 2008 Google Inc. Refer toCOPYING.txt in the directory for the full text of the Apache License, Version 2.0, under which the said software is licensed.

Both ObjectiveFlickr and LFWebAPIKit are released under the MIT license,the full text of which is printed here as follows. You can alsofind the text at:https://opensource.org/licenses/mit-license.php

Permission is hereby granted, free of charge, to any personobtaining a copy of this software and associated documentationfiles (the "Software"), to deal in the Software withoutrestriction, including without limitation the rights to use,copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom theSoftware is furnished to do so, subject to the followingconditions:

The above copyright notice and this permission notice shall beincluded in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIESOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE ANDNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHTHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISINGFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OROTHER DEALINGS IN THE SOFTWARE.

Contact

  • lukhnos {at} lukhnos {dot} org

Links

About

ObjectiveFlickr, a Flickr API framework for Objective-C

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors14


[8]ページ先頭

©2009-2025 Movatter.jp