The Old Blog Archive (Traditional Chinese), 2004-2009

Archive for the 'tekhnologia 技術或者藝術' Category

ObjectiveFlickr新進度:PhotoSearch Demo

這個週末又繼續把ObjectiveFlickr往前推進。整個framework已經重寫過了,現在只剩下兩個class是需要的,一個叫OFFlickrContext,一個叫OFFlickrInvocation。大幅改名是為了讓class name更能反映實際的功能,但也因此讓過去的舊程式要大改才能使用ObjectiveFlickr了。

這一週新寫的demo是這樣子的。

Ruby on Rails是一套很強大的框架,人們為了推廣RoR,錄製了不少短片用以證明RoR有多容易寫web app。其中一個demo是利用RoR加上Flickr API來寫照片搜尋功能。

PhotoSearch.app模仿了那個RoR範例(事實上我連他們的CSS都抄來用了),用來證明,只要使用Objective-C、WebKit、ObjectiveFlickr,一樣可以很快寫出具有Flickr照片搜尋功能的… 嗯… desktop app。

我現在的blog theme不適合用來說明程式碼,我也還不會錄製說明短片。這些都再再說明,我該好好努力在presentation skill上精進了。

總之,有圖有真相:

Public Photo Search with ObjectiveFlickr

以上的 demo 可以這個網址取得。ObjectiveFlickr使用New BSD License公開,程式碼均存放在Google Code上。

附錄:如何用Objective-C寫ObjectiveFlickr的應用程式(草稿)

Warning: because my blog theme isn’t good for showing code, this is going to be an aesthetically unacceptable entry.

在ObjectiveFlickr的Demos group底下,有PhotoSearch這個示範程式。這個程式的構成相當簡單,首先,用Interface Builder拉出三個元件:搜尋欄位、進度風火輪、WebView,然後透過繼承NSWindowController創建出PhotoSearchController,把該連接delegation連接起來。

接著,生成程式碼,然後在 PhotoSearchController.h 中的開頭 #import 這兩個標頭檔:

#import <WebKit/WebKit.h>
#import <ObjectiveFlickr/ObjectiveFlickr.h>

然後在 PhotoSearchController class 中加入以下成員:

OFFlickrContext *context;
OFFlickrInvocation *invoc;

接著修改 PhotoSearchController.m。

首先,我們要加入 awakeFromNib 方法。沒錯,Objective-C 的特色之一是,增加method是不需要於標頭檔宣告的。我們在 awakeFromNib 裡,建立 context 和 invoc 物件:

context = [[OFFlickrContext contextWithAPIKey:OFDemoAPIKey sharedSecret:nil] retain];
invoc = [[OFFlickrInvocation invocationWithContext:context delegate:self] retain];

然後把包在 Resource/ 目錄下的 index.html 給呼叫出來:

[[webView mainFrame] loadRequest:
    [NSURLRequest requestWithURL:
        [NSURL fileURLWithPath:
            [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@”index.html”]
        ]
    ]
];

(呼,有夠長!)

然後在開始搜尋的那一段 control action 中(method startSearch:),增加實際搜尋的程式碼。我們首先要把搜尋文字轉換成有 percent escape 的字串:

NSString *srchstr = [[sender stringValue] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

(還是一樣有夠長!)

然後把風火輪轉起來:

[progressIndicator startAnimation:self];

接著就可以呼叫 Flickr method 啦。因為 OFFlickrInvocation 都幫你做好了,因此呼叫 flickr.photos.search 只要一行:

[invoc flickr_photos_search:nil text:srchstr selector:@selector(handleSearch:errorCode:data:)];

這句話的意思是說呢,呼叫flickr.photos.search方法,userinfo參數是nil,搜尋字串是srchstr,而callback selector則是 handleSearch:errorCode:data: 這個方法。

然後我們要建立一個同名的方法:

- (void)handleSearch:(id)userinfo errorCode:(int)errorcode data:(id)data
{
    // …
}

然後在裡面填上程式碼。我們直接取用 callback 收到的 data 物件,先將 XML 轉成 NSDictionary ,然後直接取出 photo array:

NSArray *photos = [[data flickrDictionaryFromDocument] valueForKeyPath:@”photos.photo”];

我們打算使用 WebKit 來顯示這些照片,因此準備一個字串來插入 HTML 程式碼:

NSMutableString *code = [NSMutableString string];

然後把照片通通串起來:

unsigned i, c = [photos count];
for (i = 0; i < c; i++) {
    NSString *url=[context photoURLFromDictionary:[photos objectAtIndex:i] size:@"s" type:nil];
    [code appendString:[NSString stringWithFormat:@"<img src=\"%@\" />", url]];
}

其中第三行,我們呼叫 OFFlickrContext 的 photoURLFromDictionary: 方法,將 photo object 轉成 URL string。

最後我們呼叫 WebKit 的 DOM 操作介面,把 photos element 取出來,然後改變裡面的 inner HTML,再呼叫script.aculo.us著名的Effect.BlindDown(為什麼著名?因為上述 RoR 範例也這麼用),為我們的範例勾芡:

DOMHTMLElement *e = (DOMHTMLElement*)[[[webView mainFrame] DOMDocument] getElementById:@”photos”];
[e setInnerHTML:code];
[webView stringByEvaluatingJavaScriptFromString:@"new Effect.BlindDown('photos')"];

大體如此。  

ObjectiveFlickr 框架完成

ObjectiveFlickr 的架構初步完成了,目前簡單寫了一篇英文 blog 記錄最近的變化。還有一大堆要做的documentation work,但至少已經可以check out出來用了。

另外就是真的得好好重整我的兩個 blog 所使用的 theme 了,not really suitable for showing code snippets…

詳情請參見本 blog 的英文版

ObjectiveFlickr 進度更新

這幾天繼續在改寫 ObjectiveFlickr 。幾個重要的變化:

  1. Source trunk大地震。許多檔案搬了家。新的 Xcode project file 可以一次 build 完 framework (yes! ObjectiveFlickr.framework is there!) 和 demo apps。編出來的 ObjectiveFlickr.framework 可以獨立放在 /Library/Frameworks 也可以嵌入你所寫的 app 中使用。
  2. Source code大地震。三個 class 全部重寫了一遍,主要是把 application context (API key, shared secret 及 auth token) 分成一類(連同 URL 生成),遞送 HTTP request 的工作再歸成一類(這次加入了 HTTP POST 支援了),另外 uploader 也寫得再清楚一點。
  3. 抽取 XML payload 資訊的功能,從這一版中被拿掉了。主要希望讓 request wrapper 能專心做 payload delivery,因此甚至連 Flickr error block 的解讀工作都不做了。研究了一下 BadgerFish 以及其他 XML-JSON 互轉的慣例,可能也要依樣畫葫蘆。
  4. 考慮了一圈,在目前這個層次上(「低階」的 API),一切都還是以 async 的方式撰寫。可能要到再往上一層的 API ,才會有 blocking call ,也就是做完一個 HTTP request 才做下一件事。大家都知道原因:在 GUI 程式裡寫 sync/blocking call 反而是比較困難的。
  5. 捏指一算,目前 Flickr API 竟然有 95 個 method !
  6. API method 多,傳回來的 XML payload 也是種類繁多。

總之,目前還需要再往上架一層,才可能看到更多簡單的 demo app 出現。目前是希望把 Flickr authentication 也做成一個 sheet package ,只要把 sheet window (註)調出來用就可以處理掉 Flickr 複雜的 authentication work 。不過這個部份的 abstraction and wrapping 遠比寫框架困難就是了。UI 真不是件容易的工作。

註:OS X 的 sheet window ,最近研究才知,並不是嚴格意義上的 modal window。因為 sheet 在 on focus 時,並不真的 block 住 parent window 的 run loop (event loop) …… OS X 的 run loop 架構,其實也挺亂七八糟的 -_-。

另一個相關的問題是

我們願意花多少錢來取得這種虛擬的實體感(註一),以及有多少人會願意誠實地付錢?

有時候會很希望看到有人利用Freakonomics的方法來考察iTunes Music Store或者就是paid legal music download in general。以至於推廣到e.g. Shareware buying, online software purchase等行為。

問題是一樣的:在有不投錢而取得東西,因此而被逮機會又微乎其微的情況下(如同Freakonomics裡那個靠榮譽制販賣bag的人),到底有多少人願意付錢取得東西?

不過,Freakonomics的案例,簡化掉了其中一個因素:交易摩擦。或者說是交易過程所需經過的「層層險阻」。販賣bagel的過程,只有投錢入箱和拿走bagel而已。但是買音樂就不是那麼簡單,要開帳號(還得在能開帳號的國家開)、要有信用卡、要能值得信任地付錢……

最近遇到的悽慘例子是,在台北的計程車上聽到1976的《耳機裡的新浪潮》。結果找了幾家台北主流的唱片行,都說「我們不進他們的CD」。orz之一。然後上網查,發現要上網訂購。要上網訂購還要很多天後取貨,這也就算了,還要些註冊會員、回覆確認、blah blah blah……

在交易摩擦這麼大的情況下,我相信很多人會寧願選擇:1. 打開 P2P 軟體開始找,2. 花一點時間等待下載下來不知伊於胡底的音樂檔。或者願意忍受的話,3. 也可以去某些國家不知伊於胡底的音樂網站「試聽」……

In the end, 我既沒有買CD(因為我那時已離開台北),也沒有選擇 1. 2. 3. (因為我懶)。

我真正希望的是,點選 PayPal ,購買單單這一首歌,收到下載通知信下載(或者就直接下載),匯入 iTunes,結案。

所以問題在於,有行無市(註二)。

註一:其實在英文裡,virtual的確實意義是「實際上被當為真實的」(effective),所以virtual reality其實也是一種reality,是「被視為的」真實。不過再查字典,驚覺New Oxfoard American Dictionary竟然用這麼淺顯的話來解釋先前被英英字典解釋得很玄的virtual: “adjective; almost or nearly as described, but not completely or according to strict definition”…

註二:一直在想,有沒有可能用小額付款機制付錢給喜歡的artist,然後他們可以直接把打上了浮水印的作品(例如 mp3 檔?浮水印可以突顯這是personalized item/transaction)直接 mail 到信箱裡啊……

當CD封面可以在電腦裡翻的時候

我知道,我從此不會想再買任何一張實體CD了。

前不久,Apple發表新的iTunes 7。Steve Jobs說「這會改變你聽音樂的方式」。誠哉斯言。事實上iTunes 7的CD封面檢閱模式,可能是在為了OS X 10.5的CoreAnimation做準備(OmniGroup的OmniDazzle搶先學去)──當然,根據討論,很可能這只是個非常簡單的OpenGL view就能做出的效果,其中絕無任何高深之處。

但是,這麼簡單的一個UI上的變化,到底為何能發生這麼大的效用?在此之前,MP3 對我一直是很虛無的存在。不愛去大量下載、去「嘿嘿嘿」音樂,因為那樣弄下來的音樂,都很不真實、很不連貫。

用album art可能是有幫助的。事實上有些「嘿嘿嘿」網站,幫人把整套CD封面封底小冊,通通掃描起來,供人自行印製或存檔留念。但是那樣好像還是沒辦法解決MP3的不真實感。

iTunes 7的那個介面,卻補足了一個實體CD盒存在的理由:它們是拿來翻的。並且,這樣一個實體的存在,把CD中的音樂牽在一起,做為一種記憶的單元,畢竟我們還是熟悉「唱片」這種原型吧,即便是單曲也還是用一張兩張在量數的啊。

每每我拿到CD,首先要先剝去外頭紙盒,再來剝去側標。以往還會翻閱小冊內文,如今往往也省去此儀式。CD的存在就是為了在一堆CD中被人翻找。觸覺和視覺的聯覺如此重要。

而如今透過電腦繪圖,一張張的CD也可以被翻找。用視覺補償了觸感的不足──事實上,如果不久有人把Smackbook hack成可以拿來「翻」iTunes的CD櫃,那就更像可以摸的東西了。

CD如此,同樣的事情會不會發生在書本上?雖然閱讀的觸感與視覺聯覺更為重要,但如今的現實是有越來越多東西得在線上閱讀,紙本變成一種奢侈。而且不可否認,電子書架真的也越做越棒了。也許將來只會剩下長時間移動時,還會想讀紙本的東西。還有文學作品(雖然像詩這樣的文類,其實是非常、非常、非常適合拿來當RSS feed來讀的)。

所以,請不要用李伯伯式的口吻說,現在的年輕人都不看書。他們也許螢幕上已經看得太多(更何況,要知道什麼是《悲慘世界》,google不就好了嗎?),想要在坐捷運的時候,享受城市的風景與空氣,去感受那些不會也不想被OpenGL取代的實體世界。

至少我是這麼覺得的。

(update: 結果發現一年前寫過這篇,orz)

來函轉載:OV Podcast, semantic web, 按tab賺錢

單中杰指出上一次OV/ilyastorming podcast中一個討論的盲點:

現場錄音中好像沒有提到 Semantic Web, 讓我有點驚訝。

討論 Semantic Web 常提到的一個問題是,如何讓大眾能夠且願意把自己的創作標上機器可讀的標籤,讓搜尋引擎把資訊整合起來,以便我找「位於捷運站一公里以內、有寫過輸入法的人推薦的、並且有賣兩百元以下素食主餐的聚會場所」等。有沒有辦法把輸入法改裝成為標籤的通路呢?

也就是說,以「加快打字速度」為號召,引誘使用者多跟電腦說明一下自己在做什麼事。最簡單的例子,就是把注音輸入法的詞彙與通訊錄的姓名整合起來,輸入人名加詞的時候順便輸入地址電話,以後打「某人的地址是」之後按 Tab 就跳出地址來,或是打「某人家從捷運」之後按 Tab 就跳出「某站二號出口右轉……即到」。有人 IM 給我「請趕快來這裡」,我回電「好,騎車預計」按 Tab 跳出「12 分鐘抵達」。有人 email 問我白菜怎麼賣,我回答「一把」按 Tab……。最終的目的當然是一個人坐在電腦前面只要一直按 Tab 就有錢賺。

另有相關:Remembrance Agent(中譯建議:「記念杯」)

My footnote: 其實我們那天後來有聊到「一直按 tab 就可以寫完整篇論文」(這是貫串 OV agent 的主題之一,經常被提到)──如同我們聊到聊天機器人可以幫你解決食衣住行育樂的各種問題那般。我是比較喜歡這組杯子啦……

Thanks, Ken.

ObjectiveFlickr增加上傳功能



ObjectiveFlickr with Uploading Feature

Originally uploaded by lukhnos.

在試了一個週末後,終於發現先前沒好好研讀HTTP spec,是會得到報應的。+_+

總之,ObjectiveFlickr 現在有了 FlickrUploader 這個 class ,也多了 FlickrUploaderDelegate 這個 informal protocol ,只要叫某個 uploader 的 instance 開始上傳,上傳狀態就會源源不斷地傳回 delegate ,直到完成為止。

進一步的 demo ,請見最新版的 ObjectiveFlickrDemo 。Once again, the source can be found at Google code hosting. :)

ObjectiveFlickr in Progress

週末繼續幫 ObjectiveFlickr 增添了一些功能,但是 Flickr upload API 好像這陣子有些問題。觀察中。

ObjectiveFlickr,一套簡單的Flickr API框架(中文)

做了一套很簡單的Objective-C Flickr API,取名做ObjectiveFlickr。原始程式碼放在Google Code上面,可以從這裡取得

程式碼是用BSD授權公開的,請自由取用。其中還用到了Andreas Meingast的CocoaCryptoHasing來為NSString增加md5計算功能(Objective-C的category真好用),特此致謝。

程式碼裡面有附一個簡單的demo,如果你打算從原始碼自己建,你得先在ObjectiveFlickrDemoDelegate.m裡填進你自己的Flickr API key跟所謂的”shared secret”。萬一你忘了填,反正編譯也不會給你過。:)

我也預先建好了可直接使用的demo,放在這裡。當你按下Authenticate按鈕時,瀏覽器會打開,帶你到Flickr的授權頁面,授權的應用程式名稱叫FlickrVanilla…… 請原諒我最近越來越沒有命名的創意……

Demo展示的是Flickr API使用的必經程序。首先,你得先取得一個所謂的”frob”,再從frob合成Flickr授權頁面的URL,進到Flickr取得授權,然後回到demo app中取得所謂的authorization token。之後就可以憑此token取用各種Flickr API的服務(使用者訊息也在token裡面)。照片顯示是直接用Interface Builder拉一拉WebKit做出來的,還需要再美化就是了。

這個demo把Flickr API的呼叫拆成幾個部份,事實上在大多數Flickr桌面應用程式中,frob取得與進入授權頁面,是做成同一件事的。通常程式就直接打開瀏覽器,並用一個modal dialog box告訴使用者說,請授權完畢後再回到應用程式。

Flickr這樣做有個很棒的地方是,應用程式完全不用去管login logic。只要把之後取得的auth token先預存起來,下次打開應用程式時詢問token還有效就可以了,如果token失效了,只要再重跑一次frob取得以及授權的程序就可以。

當然,初見之下,這種作法有點不直觀。需要從API key及”shared secret”中計算出API call的簽章,自然也是為了防API abuse。

目前我還沒有把ObjectiveFlickr變成一個真正的OS X framework。目前程式庫的部份其實只有一個.h跟一個.m檔。Class也只有兩個。FlickrRESTURL是負責生出各種Flickr REST API的奇怪URL(有些還挺長的)。至於FlickrRESTRequest則包裝了NSURLConnection,並且使用Cocoa常見的非同步delegate模式,你先把request丟出去,再等著接收FlickrRESTRequest instance送回的訊息,訊息包括:網路傳輸中、資料傳輸取消、發生錯誤,或是正確載回了Flickr API的XML payload等等。我做了一些基礎的Flickr error code處理。

這兩個class構成一個相當「低階」的框架。Flickr API有一大部份是要處理那些傳回來的XML data block。目前我只做了token跟photos兩個data block的拆封動作。其他還有一大堆類似的資料得處理。

我們可以從這兩個「低階」的class出發,建立更「高階」的Flickr API framework,好比說,同步化的資料傳輸(亦即,要等API call完成才繼續下一個動作,而不是現在的async model),或是直接能寫像[flickr blogs_list]這種更直觀的API call,還有就是把送回的XML payload做更完整的拆解(例如轉換成我愛的NSDictionary資料結構等等)。

差不多就是這樣了。是有打算繼續開發下去,頭一步應該是把它變成真正的 .framework 吧。還有bug要修、做單元測試等等(async model真難debug…)。還有文件得寫。不過就先把它放出來了。如果有patch、有任何意見,或者想直接要svn commit權限,都非常歡迎的喔。

ObjectiveFlickr, a Simple Flickr API Wrapper

I’ve made a very basic Flickr API framework written in Objective-C. I called it ObjectiveFlickr and the project, including the source code (released under the New BSD License), can be found at its Google Code hosting site.

To build the demo app from the source code, you have to first check out the code (of course), and fill in your own Flickr API key and “shared secret” code in ObjectiveFlickrDemoDelegate.m ; if you don’t, there will be compiling stage errors anyway. :)

A pre-built demo app can be found here. In the Flickr authentication page, you’ll be asked to give read permission to an application called FlickrVanilla. And there you go.

The demo app is quite rudimentary but shows the basic steps: obtaining a “frob”, finishing the authentication process by visiting Flickr, then obtaining the token (with it comes the user information). After that we can do what we want. I use WebKit to display the photos. Aesthetically it still needs some brush-up.

Note that this is a step-by-step demo. OS X Flickr apps usually combine frob-getting and authentication into one step using a modal dialog box, asking users to come back as the browser window is being opened.

I actually haven’t built the framework into an OS X .framework–yet. Right now it only consists of one .h and one .m and two classes. FlickrRESTURL is the “URL factory” that packages the Flickr API URL scheme together with API signing. Whereas FlickrRESTRequest packages NSURLConnection and some XML unpackaging utilities. It follows the delegate design pattern and uses informal protocol to tell the delegate if an XML document is being received, canceled, successfully received and unpackaged, or if there’s anything wrong (Flickr error codes are processed here too).

These two classes make very basic building blocks. As for the XML unpackaging, so far I’ve only done the one for authentication token and photos. There are other Flickr data blocks that we have to take care of.

Using these building blocks–”low-level” in a way–we can go steps further, and come up with, say, “higher-level” encapsulations, such as synchronous calls, API calls as Objective-C methods, or simply returning every Flickr response as pre-unpackaged Foundation data structures (I love NSDictionaries).

So much for now. I plan to build more things with it and improve the framework along the way (building a real .framework will be an immediate to-do). There will be bugs to fix and some more testings. Oh, and the documentation work (currently none). But here I will give it a go, releasing it into the wild. Patches, comments, or even commit rights requests are all welcome.

Thanks!

(ObjectiveFlickr uses Andreas Meingast’s CocoaCryptoHasing to do the md5 signing, Danke Andreas!)

« Prev - Next »