ObjectiveFlickr 是一个为 Mac 和 iPhone 应用设计的 Flickr API 框架。
ObjectiveFlickr 现在支持基于 OAuth 的 Flickr 新认证流程。我会在接下来的几周内发布更多关于它的信息。SnapAndRun 示例已更新以反映使用情况。一个新的 Mac 示例,OAuthTransitionMac,演示了如何使用基于 OAuth 的新 API,以及如何迁移现有的认证令牌。OAuthTransitionMac 使用 ARC,因此还展示了如何在非 ARC 库中使用 ObjectiveFlickr,这是一个非 ARC 库,同时与 ARC 应用一起使用。
ObjectiveFlickr 中的 iOS 支持主要是在 iPhone OS 2.0 时期开发的,这也有所体现。还需要做更多的工作来反映 iOS 开发过程中的变化。您的贡献(例如更新本 README、提交新示例或测试用例)将非常受欢迎——这也会造福 iOS 开源开发社区!
更新。请参阅我的博客文章了解 transition 需要采取的步骤。
2.0 版是一个重写,目的是确保设计完整性和可扩展性。与 0.9.x 的不同之处包括
如果您已经使用 ObjectiveFlickr 0.9.x,那么坏消息是 2.0 不向后兼容。好消息是它使用了不同的类名集合。本文件末尾提供了一些迁移提示。
当然还有一些待办事项
查看GitHub上的代码
git clone git://github.com/lukhnos/objectiveflickr.git
提供您的API密钥和共享密钥。您需要将SimpleAPIKey.h.template
复制到SimpleAPIKey.h
中,并填写那里的两个宏。如果您没有API密钥,可以在http://www.flickr.com/services/api/keys/apply/处申请。请确保您已经理解了他们的条款和条件。
请记得将您的API密钥设置为“网络应用”,并将回调URL应用URL!)设置为
snapnrun://auth?
构建并运行SnapAndRun for iPhone。项目位于Examples/SnapAndRun-iPhone
构建并运行RandomPublicPhoto for Mac。项目在Examples/RandomPublicPhoto
与Microsoft Visual Studio不同,Xcode在跨项目开发方面并不突出。幸运的是,您不需要经常这样做。如果有什么问题,请参阅我们的示例应用程序以了解项目文件的结构。
将ObjectiveFlickr.xcodeproj
添加到您的Mac项目中(从Xcode菜单项目 > 添加到项目...
)获取信息
),然后在常规
选项卡中,将ObjectiveFlickr (框架)
添加到直接依赖项
复制文件
阶段,并将目的地
设置为框架
(在其自己的信息窗口中)ObjecitveFlickr.framework
从Xcode中的组与文件面板中拖动到新创建的复制文件
阶段(在添加的ObjectiveFlickr.xcodeproj
下)ObjecitveFlickr.framework
拖动到目标组的链接二进制与库
中配置
设置为所有配置
,然后在框架搜索路径
属性中,添加$(TARGET_BUILD_DIR)/$(FRAMEWORKS_FOLDER_PATH)
#import <ObjectiveFlickr/ObjectiveFlickr.h>
由于iPhone SDK不允许动态链接框架和捆绑包,我们需要静態链接到ObjectiveFlickr。
将ObjectiveFlickr.xcodeproj
添加到您的Mac项目中(从Xcode菜单项目 > 添加到项目...
)获取信息
),然后在常规
选项卡中,将ObjectiveFlickr (库)
添加到直接依赖项
CFNetwork.framework
添加到链接库
libObjecitveFlickr.a
拖动到目标的链接二进制与库组
再次打开您的目标信息窗口。将配置
设置为所有配置
,然后在头文件搜索路径
属性中,分别添加这些路径(<OF root>
是您检出ObjectiveFlickr的位置)
<OF root>/Source
<OF root>/LFWebAPIKit
在您的项目中使用#import "ObjectiveFlickr.h"
ObjectiveFlickr是一种异步API。由于GUI应用的性质,所有的ObjectiveFlickr请求都是异步的。您发出一个请求,然后ObjectiveFlickr调用您的代理方法,并告诉您请求是否成功或失败。
ObjectiveFlickr 是一个极简主义框架。框架中您需要处理的基本上只有两个类: OFFlickrAPIContext
和 OFFlickrAPIRequest
。与许多其他Flickr API库不同,ObjectiveFlickr 没有 例如FlickrPhoto、FlickrUser、FlickrGroup 等类。您调用一个Flickr方法,例如 flickr.photos.getInfo
,然后返回一个包含结果键值对的字典(在其他语言中是hash或map)。结果是 直接映射自Flickr自己的XML格式响应。因为它们已经是 结构化数据,ObjectiveFlickr 不再转换为其他对象类。
由于设计极简,您还需要对 Flickr API的工作方式 有基本了解。有关详细信息,请参阅 http://www.flickr.com/services/api/。但基本的,您需要知道的是您想要调用的方法以及Flickr将返回哪些XML数据(键值)。
通常,要为Mac或iPhone开发Flickr应用程序,您需要遵循以下步骤
创建一个OFFlickrAPIContext对象
OFFlickrAPIContext *context = [[OFFlickrAPIContext alloc] initWithAPIKey:YOUR_KEY sharedSecret:YOUR_SHARED_SECRET];
在适当的地方创建一个OFFlickrAPIRequest对象,并设置代理
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];
实现代理方法。
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary;
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didFailWithError:(NSError *)inError;
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest imageUploadSentBytes:(NSUInteger)inSentBytes totalBytes:(NSUInteger)inTotalBytes;
这三种方法都是可选的(旧 Objective-C 术语中的“非正式协议”;新术语中的可选协议方法)。 注意:如果您正在使用Mac OS X 10.4 SDK,或者如果您正在使用10.5 SDK但针对10.4,那么代理方法声明为非正式协议。在其他所有情况下(OS X 10.5及以上或iPhone应用程序),您需要指定您正在采用OFFlickrAPIRequestDelegate协议。 例如
@interface MyViewController : UIViewController <OFFlickrAPIRequestDelegate>
调用您想要使用的Flickr API方法。以下是一些示例。
调用 flickr.photos.getRecent
,参数 per_page
= 1
[request callAPIMethodWithGET:@"flickr.photos.getRecent" arguments:[NSDictionary dictionaryWithObjectsAndKeys:@"1", @"per_page", nil]]
相当多的Flickr方法需要您使用HTTP POST调用(因为这些方法写入或修改用户数据)
[request callAPIMethodWithPOST:@"flickr.photos.setMeta" arguments:[NSDictionary dictionaryWithObjectsAndKeys:photoID, @"photo_id", newTitle, @"title", newDescription, @"description", nil]];
在代理方法中处理响应或错误。如果发生错误,则将NSError对象传递给错误处理代理方法。如果错误对象的域是 OFFlickrAPIReturnedErrorDomain
,则它是服务器端错误。您可以参考Flickr的API文档来了解错误的含义。如果域是 OFFlickrAPIRequestErrorDomain
,则是客户端错误,通常是由丢失网络连接或传输超时引起的。
我们将现在讨论响应。
要上传图片,从文件路径或图像数据(NSData)创建一个NSInputStream对象,然后发出请求。在此示例中,我们假设我们已经获得了JPEG格式的图像数据,我们将上传的图片设置为私有
NSInputStream *imageStream = [NSInputStream inputStreamWithData:imageData];
[request uploadImageStream:imageStream suggestedFilename:@"Foobar.jpg" MIMEType:@"image/jpeg" arguments:[NSDictionary dictionaryWithObjectsAndKeys:@"0", @"is_public", nil]];
上传进度将通过代理方法 flickrAPIRequest:imageUploadSentBytes:totalBytes:
进行报告
ObjectiveFlickr要求提供NSInputStream对象作为输入的原因是我们不想将整个图像读入内存以准备上传数据。使用NSInputStream,您可以将内存中的图像数据、文件或甚至来自不同(例如,分区)源虚拟化的图像字节流提供给ObjectiveFlickr。
请确保您已阅读Flickr的上传API文档,以便了解如何获取上传结果。请注意,在上传完成和HTTP POST请求本身完成(您接收响应的瞬间)之间可能有一个 很长 的等待时间。因此,请确保您有一个长超时间隔,特别是在上传大图像时,并且相应地设计您的UI。
已弃用。原始认证过程现在已被Flickr弃用。请参阅我的博客文章,了解新配置所需的步骤。
如果你的应用不仅阅读公开照片,那么你的应用将需要获取用户对其照片的访问权限。你需要使用Flickr的认证/授权机制(以下称为“认证”以涵盖两个步骤)来获取后续访问所需的authToken。
这实际上是使用整个Flickr API中最困难的部分;之后的所有内容都很容易,并且(通常)很顺畅。仅此一项就值得一个完整的教程,但我会尽力解释关键部分。
在此之前,请在此处了解Flickr自己的文档:http://www.flickr.com/services/api/misc.userauth.html。
有两种类型的应用程序认证
实际上有一种“移动应用程序”认证,是为功能手机或不太智能的智能手机(如果相信苹果公司的说法)设计的。但既然我们在谈论Mac和iPhone应用程序,而且它们不是任何古老的移动平台,所以我们将跳过这一部分,直接进入两种主要的应用程序认证类型。
已弃用。原始认证过程现在已被Flickr弃用。请参阅我的博客文章,了解新配置所需的步骤。
在此之前,Mac开发者只对桌面应用程序认证感兴趣。如果你之前使用过任何Mac Flickr应用程序(FlickrExport、HoudahGeo、Posterino等),你知道它是如何工作的
为了将此映射到您的应用程序内部运作中,您需要做以下操作
inResponseDictionary
传递给-[OFFlickrAPIContext loginURLFromFrobDictionary:requestedPermission:]
,并获取返回的NSURL对象。-[OFFlickrAPIContext setAuthToken:]
将认证令牌分配给当前的Flickr API上下文auth_token
参数,你现在可以访问用户授权给你的所有API。已弃用。原始认证过程现在已被Flickr弃用。请参阅我的博客文章,了解新配置所需的步骤。
iPhone和iPod Touch对上面的认证模型提出了挑战:打开Mobile Safari然后要求健忘的用户回来是不好的。
因此,许多iPhone开发者提出了这个绝妙的主意:使用URL方案启动应用程序。结果是Flickr的Web应用程序认证很好地满足了这个想法。下面是如何实现它的
幕后的真相是,提及的iPhone应用在其Info.plist
中注册了一个URL方案,例如someapp://
,并且应用开发者配置了他们的Flickr API密钥,这样当用户授予应用权限时,Flickr会将网页重定向到应用开发者之前指定的那个URL。Mobile Safari打开该URL,然后应用被启动。
事实上,Mac应用也可以这样做!
您需要做的是:
SnapAndRun-Info.plist
和这个CocoaDev文章以获取详细信息。-[OFFlickrAPIContext loginURLFromFrobDictionary:requestedPermission:]
获取登录URL,请注意,您不需要frob字典来获取web应用的登录(认证)URL,所以只需传递nil。当然您仍然需要传递权限参数。-[OFFlickrAPIContext setAuthToken:]
将认证令牌分配给当前的Flickr API上下文auth_token
参数,您现在可以访问所有用户已授予您权限的API。现在您已经完成了使用Flickr API中最困难的部分。
Flickr的默认响应格式是XML。您可以选择JSON。无论您选择哪个格式,其核心思想是它们已经是结构化数据。当我最初设计ObjectiveFlickr时,我发现不需要再创建一层代码来将这些数据映射到“原生”对象。因此,我们没有像OFFlickrPhoto
或OFFlickrGroup
这样的东西。本质上,当请求对象收到响应时,它将XML映射到一个由NSDictionary、NSArray和NSString组成的数据结构。在Apple的术语中,这被称为“属性列表”。我们将使用该术语来描述映射的结果。然后您可以在属性列表中读取您感兴趣的关键值对。
ObjectiveFlickr使用XML格式来最小化依赖性。它使用NSXMLParser解析XML,这是所有Apple平台上都有的。它按照以下三个简单规则映射XML到属性列表:
<photoid>12345</photoid>
)被映射为一个字典,包含键OFXMLTextContentKey
(一个字符串常量)以及其文本内容作为值。例如,下面是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>
然后在您的flickrAPIRequest:didCompleteWithResponse:
代理方法中,如果使用NSLog转储接收到的响应(一个NSDictionary对象),您将看到如下所示的内容(省略了无关部分)
{
auth ={
perms = { "_text" = write };
token = { "_text" = "aaaabbbb123456789-1234567812345678"; };
user = {
fullname = "blah";
nsid = "00000000@N00";
username = foobar;
};
};
stat = ok;
}
所以,如果我们对检索到的认证令牌感兴趣,我们可以这样做
NSString *authToken = [[inResponseDictionary valueForKeyPath:@"auth.token"] textContent];
在这里,我们自己的-[NSDictionary textContent]
只是一个方便的方法,它相当于在我们例子中调用[authToken objectForKey:OFXMLTextContentKey]
。
以下是由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>
映射的属性列表看起来像这样
{
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知道将包围在复数标签内的标记转换为NSArray。因此,如果您想检索数组中的第二张照片,您可以这样做
NSDictionary *photoDict = [[inResponseDictionary valueForKeyPath:@"photos.photo"] objectAtIndex:1];
然后,使用来自 OFFlickrAPIContext
的两个辅助方法,您可以获取静态照片源 URL 和照片原始网页 URL。
NSURL *staticPhotoURL = [flickrContext photoSourceURLFromDictionary:photoDict size:OFFlickrSmallSize];
NSURL *photoSourcePage = [flickrContext photoWebPageURLFromDictionary:photoDict];
请记住,Flickr 要求您在任何显示照片的应用程序中都将链接到照片的网页。因此,请相应地设计您的 UI。
遗憾的是,有一些 Flickr 响应并不严格遵循“复数标签 == 数组”规则。考虑以下片段(已删除标签属性以突出显示相关问题),来自于 API 方法 flickr.activity.userPhotos
<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>
注意 <activity>
标签可以包含一个或多个 <event>
标签。这实际上是 Flickr API 的一个模糊区域,我并不完全确定是否应该将这个异常写入书中(即 OFXMLMapper
中的逻辑,它负责这项工作)。异常列表永远不可能全面。
我们可以利用 Objective-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;
}
另一方面,我们在 OFXMLMapper
中建立复数标签规则的原因是,反复编写常用的标签之上的样板代码非常繁琐。由于 OFXMLMapper
已经处理了复数标签,所以边际案例更容易处理。
OFFlickrAPIRequest
拥有一个 sessionInfo
属性,您可以使用它来提供应用程序的状态信息。但是,在代理方法中编写大量的 if
-else
将很快变得令人厌倦。我的经验是,我设计了一个定制的“会话”对象,具有三个属性:代表(不必是请求的发起者),完成时要调用的选择器,错误时要调用的选择器。然后,OFFlickrAPIRequest
的代理方法简单地根据会话对象派发回调。
如果您的控制器调用了许多 Flickr 方法或涉及到多个阶段/状态,这种设计模式会有所帮助。
每个 OFFlickrAPIRequest 对象可用于创建它的线程。不要跨线程传递它们。代理方法也在请求对象运行的线程上调用。
ObjectiveFlickr 使用 LFHTTPRequest,它只使用 CFNetwork 堆栈。据报道 NSURLConnection 存在其自身的问题。另一方面,LFHTTPRequest 不处理非 HTTP URL(它处理 HTTPS,但有一个例外:在 iPhone 上,您不能使用不受信任的根证书)并且不执行 HTTP 身份验证。它也不管理缓存。但是,对于 Web API 集成,LFHTTPRequest 提供了一种简单的方式来进行和管理工作请求。
一个相关的注释:LFHTTPRequest 将在您的 Mac 或 iPhone 上使用系统的共享代理设置。这就是它需要在 Mac 上单独构建时要求 SystemConfiguration.framework
的原因。
我没有真正仔细研究这个问题,但这里有一些想法
ObjectiveFlickr 首次发布于 2006 年后期。之前版本 0.9.x 经过一次重写,托管在 Google Code 上。还有一个 Ruby 版本,作为 Ruby gem 提供。
此次重写是基于我在开发 Mac 和 iPhone 产品过程中的经验(我经营着自己的公司,Lithoglyph)。这是一个极好的学习过程。
很多人为 ObjectiveFlickr 的发展提供了宝贵的建议和指导。还有很多 Mac 应用使用了它。我想感谢 Mathieu Tozer、Tristan O'Tierney、Christoph Priebe、Yung-Lun Lan 和 Pierre Bernard 提供的反馈,这些反馈最终导致了框架当前的设计和形态。
ObjectiveFlickr 版权 (c) 2006-2009 Lukhnos D. Liu。
LFWebAPIKit 版权 (c) 2007-2009 Lukhnos D. Liu 和 Lithoglyph Inc.
LFWebAPIKit 中的一个测试 (Tests/StreamedBodySendingTest
) 使用了 Google Toolbox for Mac,版权 (c) 2008 Google Inc。有关 Apache License 2.0 的完整文本,请参考目录中的 COPYING.txt
文件。
ObjectiveFlickr 和 LFWebAPIKit 都在 MIT 许可证下发布,许可证的完整文本如下。您也可以在以下网址找到文本:http://www.opensource.org/licenses/mit-license.php
任何人可以在不收费的前提下获得此软件和相关文档的副本(以下简称“软件”),在不受限制的情况下处理该软件,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售软件副本,并允许提供给软件的人这么做,只要符合以下条件
上述版权声明和本许可声明应包含在软件的所有副本或主要部分中。
软件“按原样”提供,不提供任何明示或默示的保证,包括但不限于适用性、针对特定目的的适用性和非侵权性保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任(无论因合同、侵权或其他原因而引起)承担责任,即使作者或版权所有者已被告知此软件或软件的使用可能引起此类损害。