Shit happens, always.

國外一些網路媒體如 Tech Crunch、還有 John Gruber 的網站等,前兩天報導,Joe Hewitt (Facebook 的 iPhone 應用程式的作者)說,他個人打算中止 Facebook 的 iPhone 應用程式開發,轉往進行其他的 Facebook 的計畫,原因是他對於蘋果的 App Store 上架審核機制非常不滿。

剛看到這則新聞的時候,還搞不清楚是怎麼一回事,第一個想法是-別人對上架審核不滿也就罷了,Facebook 有什麼好不滿的?其他人將軟體送進去之後快則到七天之後才能夠等到審核結果,就 App Store 開張以來,唯一看到能夠一個星期推出兩個新版本的軟體,也就只有一個,而這個軟體還不是別的,就是 Facebook。

昨天上班一打開電腦,才終於搞清楚發生在 Joe Hewitt 身上是什麼狀況。打開收信程式,看到蘋果送來的退件信件,說,我們寫的程式不能夠在 App Store 上架,原因是程式裡頭呼叫了 iPhone 的 private API;而我從來就不記得我什麼時候用到過這些東西,查了一下,呼叫 private API 的,不是自己的程式,而是因為程式用到了一個 external library,這個 library 呼叫了,而這個 library 就是 Joe Hewitt 所撰寫的 Three20

Three20 Library

Three20 大概是目前最有名的第三方 iPhone 的 UI library,因為 iPhone 的螢幕大小是 320×480 pixel,所以取了這麼一個名字。Three20 計畫是 Facebook 的 iPhone 應用程式的延伸產物,他們把 Facebook 的 iPhone 程式在架構上拆成了兩塊,一塊處理如何登入、讀取 server 資料的網路 API ,另外一部分就是如何在 iPhone 上做 user interface 的呈現,而這部份 Facebook 將程式碼開放出來讓其他人都能使用,之前是放在 Joe Hewitt 的個人名下(以及 github 的個人帳號下),前兩天轉到了 Facebook 的帳號下。

Three20 這個 library 裡頭,個人覺得厲害的有幾個地方:

一、自己做了一套在 iPhone 上的 Rich Text 的文字排版引擎:

在 iPhone SDK 原本所提供的 UI 元件中,想要在畫面上放一段文字的話,每個 UILabel 元件一次只能夠使用一種字體、一種顏色、一種大小,如果想要比較複雜的排版,就必須使用瀏覽器元件 UIWebView。

這樣要做一個介面老實說頂痛苦的-當你在做一個輸入資料的介面,在一堆文字輸入框以及按鈕當中,想要有一段排版比較複雜的說明文字時,你不太可能就這樣在畫面中挖一塊,放個瀏覽器元件進去;而且 UIWebView 本身也不太能夠作為 iPhone 應用程式的主要介面元件,在 Mac OS X 上你還可以透過比較多的 delegate method 以及 Javascript-Objective C bridging,讓 Web view 裡頭發生的事件傳回你的 controller class 裡頭,在 iPhone 上也沒有這方面的 API。

Three20 自己做了一套排版引擎,語法是 HTML,只要你把想要填入的內容加上 HTML tag,就可以直接在 UILabel 上呈現排版效果;同時你也可以使用像是 CSS 的樣式指令,雖然每個樣式都還是一個 Objective C selector,但是可以很方便的就加上了字體、顏色、背景、縮排…等原本很難做的排版。這部份放在 TTStyle 部分的程式中。

二、提供大量現成而且簡化過的 UI 元件

如果你想要做一個像 iPhone 內建的相簿程式那樣的介面,想要有可以全螢幕瀏覽圖片,用手指滑一下就跳到下一張,然後可以回到縮圖瀏覽模式…這些功能,Three20 已經有現成的元件,你的圖片資料可以放在本機、也可以放在網路上,只要提供一個內容是 URL 的 Array 就好了,Three20 會自己幫你處理圖片下載與 cache 等工作。

另外像是 Table View,原本你寫一個 UITableViewController subclass 可能要實作五六個 data source 與 delegate method,他也幫你處理掉很多工作,有時候就只需要一個 method、準備一個 Array 就夠了,也幫你處理什麼想在表格裡頭放置在網路上的圖片這類的工作。

裡頭有一些 UI 元件我會拿來用(正所謂人活著好好的幹嘛重新發明輪子),不過我自己是比較不贊同把抓取網路資料的行為、跟 iPhone 的 UI code 混在一起,因為這樣會將網路資料交換的程式散亂在很多地方,很容易搞亂架構,而且會變得不容易測試。

三、URL Based Navigation

Three20 是一個在想法上非常 Web 導向的 library,像是前一點自己弄一個 HTML 引擎,或是覺得網路上的圖片應該直接在 UI code 部分處理,不過,這種 Web 思考的導向,還是在 URL Based Navigation 最明顯。

iPhone 的 UI 設計的一大特色,就是相較其他平台,應用程式有很清楚的導覽路徑-如果你眼前的畫面的左上方有一個向左的箭頭,就代表可以回到目前瀏覽路徑的上一層,然後點到表格中的某個項目或某個其他元件,就會從右方跑出一個新的畫面,透過動畫效果把原本的畫面擠到左方,告訴你進入了下一層。在 Palm Pre 系統沒有視覺元件告訴你現在你在哪一層,唯一的一個實體按鈕則有時是回到上一層、有時拿來當做切換應用程式,Android 有實體的 Back 與 Menu 按鈕,但往往讓人看不出來什麼時候可以回上一層、什麼時候有 Menu 可以用。

iPhone SDK 提供 UINavigationController 這個 class,處理這種一層一層瀏覽行為,要進入下一層,就是產生一個新的 UIViewController subclass,呼叫 UINavigationController 的 -pushViewController:animated:。而這個過程有一個麻煩的地方-這些物件在跳出應用程式後就消失了,那,我在跳出應用程式,再重新進入應用程式後,我要怎麼記得上次瀏覽到什麼地方?我要怎麼可以恢復最後一次使用的瀏覽狀態?

Three20 就在 UINavigationController 的基礎上,架了一層用 URL 處理事情的想法:雖然我們現在在做一套 iPhone 應用程式,但是卻用 Web App 的想法設計-每一個 iPhone 的畫面,都想像成是一個 Web App 的 URL,每個 UIController 在初始化時需要的參數,都像是我們對一個 Web App 的 URL 傳入的 Get 參數一樣。

如果你有一個通訊錄程式,首頁是通訊錄群組列表,點下去是群組中的人名列表,人名點上去可以看通訊資料內容,再點下去可以編輯,可能就會像這樣:

通訊群組列表(主畫面) addressbook://root
顯示某一個群組 addressbook://group/1
顯示某一個人的資料 addressbook://person/1
編輯某一個人的資料 addressbook://person/edit/1

而當 UINavigationController 在推入這些 view controller 的時候,就同時把對應到這些 view controller 的 URL,像是瀏覽器歷史紀錄一樣的記下來,下一次進入程式時,就可以按照最後一次記下的路徑,逐一重新產生 view controller,呈現最後一次使用的狀態。不過個人不是很喜歡 Three20 裡頭所有叫做 URL 的變數,都是 NSString,而不是 NSURL。

目前在 github 上,Three20 是 Objective-C 語言類中,在使用者追蹤開發進度排行榜、以及分支數量的排行榜上,都是累計第一名。github 的 clone 數報表目前故障,沒辦法知道有多少人 checkout Three20 拿來用的紀錄,但是有兩百多個分支,至少代表有兩百多個人還需要客製化 Three20,在這邊先不講求什麼精確的數字,總之在使用 Three20 的開發者有一大堆,而 Three20 的用途就是放在 iPhone 應用程式裡頭,所以,有用到 Three20 的應用程式,一大堆。

於是 Apple 開始 reject 用到 Three20 的程式之後,這兩天你又是可以看到有人寫 blog、有人在 Stack Overflow 上發問、有人把問題丟到 issue tracker,當然在 Three20 的討論區上更是免不了,一串討論就是一百多篇,因為整個討論串很長,所以又必須有人做出一份摘要,其中對你來說比較重要的資訊是-目前如果想要一份沒有用到 private API 的 Three20,請先服用 Prime 31 的 fork

Private API

蘋果所稱 Three20 所使用的 private API,是 Three20 直接存取了 UITouch 這個 class 裡頭的幾個成員變數,像是 _locationInWindow、_view、_window 等。UITouch 就是用來記錄、處理使用者用手指摸過 iPhone 螢幕時所產生的事件的 class,裡頭儲存的資料包括-什麼時候發生、手指在螢幕上的座標位置、點到的是哪個畫面等。

一般來說,好像不太需要自己產生 UITouch 物件,而是在使用者按了螢幕之後,系統就會把 UITouch 物件傳遞給你的程式。而 Three20 用到 UITouch 成員變數的地方,在於自己產生 UITouch 物件,初始化時自然會將各種資料儲存在這些成員變數裡,需要自己產生 UITouch 物件的主要用途,則是用來作 UI 的自動化測試。

根據 commit log,這段程式在今年二月十八日就有了,在這段時間內 Facebook 出了多少版本,多少用了 Three20 library、用了這段程式的軟體在 App Store 上架,到了十一月,突然統統 reject。

而這些 UITouch 的成員變數有多「private」呢?你也不需要用什麼逆向工程工具就可以知道,因為都直接寫在 UITouch 的 header 裡頭,如果你裝了 iPhone SDK,請打開 Xcode,隨便開一個文字檔,打上「UITouch」幾個字,按著鍵盤的 command key,在剛剛打的字上面點兩下,Xcode 就幫你打開了 UITouch.h,另外一種更快的方法是直接用 spotlight 搜尋「UITouch.h」。雖然這些變數是 class 裡頭的內部狀態,但是就是寫在這麼容易就可以看到的檔案裡頭,然後說這是 private API…。

如果真的不希望別人使用,Xcode 也可以設計成,在編譯的時候,同時檢查哪些是你不希望別人使用的東西,提出警告。Xcode 現在連哪裡有 potential leak 哪裡有 over release 都可以找到,這種事情也不是做不到。

不過,這個時候突然針對取用 UITouch 成員變數有大動作,總覺得是想要排除很多現在的應用程式日後不相容的問題,UITouch 大概會有大改寫。而 UITouch 還可以改寫什麼呢?新的觸控介面的資料?在雙螢幕上同時出現 touch 事件?(隨便亂猜)

這邊加一段我同事的意見:

『Objective-C 確實沒有像 C++ 那個嚴格的成員變數保護,這些底線開頭的變數也確實被 Apple 視為 private ivar,但是在沒有任何溝通的情況下,直接以「這是使用private API」的行為來 reject 掉一些拿來測試用的 code ??』

總之,狀況簡述如下-

你製作、維護一套花了很大力氣做出來的 library,說服公司將資產透過網路分享給別人,自己寫這套 library 只領公司薪水,其他人的軟體卻因此在 UI 精緻度上提昇並因此獲利。有一天,蘋果 reject 了(想來絕對不下)上千套軟體,原因是一段就算處在灰色地帶,但是長久以來(以 iPhone 的發展速度,九個月算很久了)沒什麼問題,但突然被認為有問題的十行程式。其他開發者的詢問如雪片般飛來,一片哀鴻遍野,一群人為了各自利益的軟體產品圍著你一個人看你寫程式,做這件事情對自己與公司看來也都沒有什麼好處,而問題追根究柢是蘋果的政策。你會怎麼做?

於是看到某些討論就實在讓人覺得很歡樂,比方說看到這樣的留言

滾吧,app store有超過十萬個軟體,全世界到處都有開發者,不差你一個!!!
臉書算啥,等等就有人開發手書,腳書,屁股書,比你臉書好十倍!!!

好像總有人不知道一個很簡單的道理-不管是什麼人,都總是站在別人的肩膀上。

9 thoughts on “Shit happens, always.

  1. 好像總有人不知道一個很簡單的道理-不管是什麼人,都總是站在別人的肩膀上。

    的確,不過這位 Joe Hewitt 大概覺得他的熱臉貼在 夾不死 的冷屁股上面吧?

  2. 写的很好,但是总觉得 Joe Hewitt 的离开是有很多原因的.
    在大批程序reject之前, 他已经很久没有更新了 three20 项目,那时候已经积累了很多issues在github上了.

    现在一个好处是在Joe Hewitt 后,Three20项目又变得非常活跃了.

  3. 鳥事老是發生阿~~
    老是有哪種看了別人寫的書,然後用自己的嘴巴說出來,就硬要說是自己想的人,
    明明是別人的知識,尊重一下又會怎樣呢?

    如果真是有料有才華超級鬼才,自然會紅阿~~
    酸別人,勉強不來吧~

  4. 當兵前看了一本書叫”驗證精實案”,讓我很嚮往金門當兵的生活,不論多苦多操學長學弟制多重,只要能夠飽覽國家公園風光,體會驗證精實案作者當年,一腳踏上料羅灣碼頭的那那一剎那
    台灣島住久了如果能在當兵時期離家也是一種學習獨立的方式,所以並不排斥到外島當兵,如果是爽兵更好.. 我暗自在心得寫作簿寫下想當空特和金門工兵營的生活
    精實案過後,金門戰地任務解除,部隊不若以往的精實
    06年成功嶺新訓,就打算抽到金門籤,那時義務役期從1年6個月減到一年四個月,外島出現大退潮,我用專科所學的統計學盤算著,大抽籤機率可能有30%..後來只有10%是外島籤..
    在新訓第三天憲兵來選兵,小選傘兵之前就傳出我硬被選上憲兵
    雖然並不在意結果,在心中卻有隱隱的難過,或許是離驗證精實案發生地又遙遠了一步..
    因為憲兵抽單位籤是在陸軍新訓時就抽好了,而在憲校會有一個福利叫”媒合互調”,在受憲訓時會將要調單位的人統一調查,然後在抽籤一次,如果兩個人想抽一個名額,就會有兩支籤

  5. 我是台中人,聽到下部隊的單位是在中部憲兵隊,當兵不因該是如此的安逸的,要浪跡天涯(當時我的卻是著麼想的) 因此更激起到金門當兵的衝動,在調查媒合互調時選擇到金門,那時候有一個金門名額,在舉手時同梯都以為我瘋了
    [甘林娘! 肖ㄟ] ㄧ個同梯說
    [在新訓時我就覺得他怪怪的] ㄧ個同梯又說
    連上的人議論紛紛..
    班長將參加媒合互調人員集合帶往一間教室,準備抽籤
    [你們要調到哪?] 一個分隊長問
    [金門!!] 我說
    他用很驚訝的眼神看著我
    [你是金門人?] 分隊長問
    [不是,我是台中人!!]
    這時大家都看著我..說不出話來
    等其它單位籤抽完,換到外島這次只有一支外島籤,只有金門
    有一個人跟我一起抽,所以機率只有1/2..
    不知道是聽誰說先抽籤的機率大,我認為抽中的機率大,金門一定是我的
    [班長,我先抽籤啦!] 我很積極的表示
    [你先] 班長說
    舉起手伸進箱子裡抽起,往左往右摸到了一支………空白籤…..
    [空白籤就是未中籤!] 班長說
    居然沒有抽中,那我這一年四個月都要在戶籍地無聊的度過軍旅生涯,
    當時心情非常的沮喪….默默的接受上天的安排
    事後回想起來,都覺得不可思議.
    我將中籤率從陸軍10%提升到憲兵的50%,扎扎實實的50%
    外島..金門.居然離我是如此的近,一不小心就抽中了金門..
    未中籤反而掉入了另一個地獄…….待續…..

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.