1 Launch Keychain Access from your local Mac and from the login keychain, filter by the Certificates category. You will see an expandable option called “Apple Development Push Services”
2 Right click on “Apple Development Push Services” > Export “Apple Development Push Services ID123″. Save this as apns-dev-cert.p12 file somewhere you can access it. There is no need to enter a password.
3 The next command generates the cert in Mac’s Terminal for PEM format (Privacy Enhanced Mail Security Certificate):
openssl pkcs12 -in apns-dev-cert.p12 -out apns-dev-cert.pem -nodes -clcerts
posted @
2012-05-29 11:07 lincode 閱讀(732) |
評論 (0) |
編輯 收藏
這個 bug 在 xcode 4.3 以下會出現,4.3 以后已經修正了。
解決方法為:找到 target 的圖標,更改其 Other Linker Flags 為: -all_load 或 -force_load
-force_load,后跟隨一個文件位置,可以更精確地加載所需文件。
蘋果的解釋為 :
http://developer.apple.com/library/mac/#qa/qa1490/_index.html簡單點說就是,Objective-C 的動態特性使得需要,為鏈接器添加一個標簽(設置 Other Linker Flags 為 -ObjC)來解決通過 Category 向類添加方法的問題。
但這個標簽 -ObjC 在 64 位 和 iOS 中有問題,需要使用 -all_load 或 -force_load。
總結如下:
如果,第三庫中沒有 category,Other Linker Flags 無需設置
如果,第三方庫中有 category,需要設置為 -ObjC
如果,某些 Xcode 版本中,出現問題,修改設置為 -all_load
posted @
2012-04-23 14:56 lincode 閱讀(1814) |
評論 (0) |
編輯 收藏
獲得 Crash Report:1 itunesConnect 的后臺會提供一個 Crash report 表;
2 把一臺打開了開發模式的機器接入 Mac,Xcode 的 Organizer 中能查看這臺設備的 Crash Report;
3 若使用了 Umeng.com, Bugsense.com 之類的工具。
閱讀 Crash Report:這之前需要一個名為 AppName.app.dSYM 的文件。Xcode 中,Archive 一個項目之后,可以在 Organizer 的 Archives 分頁中,找到所有項目的 Archvie 文件。
右鍵點擊一個, Show Package Content,就能看到一個類似 AppName-3-19-12.app.PM.xcarchive 的文件,show in finder 這個文件,就能找到 .dSYM 文件。
在 Ternimal 中執行,若是 來自于 iphone 3G 的機器,就需要使用 armv6 代替 armv7.
atos -o AppName.app.dSYM/Contents/Resources/DWARF/AppName -arch armv7 0x0000b82
這樣就能看到,地址對應的類,函數,代碼行數。這個命令只能解析出客戶代碼的位置。若是錯誤堆棧中的系統調用,是無法翻譯出來的。
posted @
2012-03-18 13:56 lincode 閱讀(1001) |
評論 (0) |
編輯 收藏
Apple 提供了一個地址方向解析的服務 MKReverseGeocoder,上傳一個經緯度,返回一個詳細的地理位置信息。但這個服務在中國不太穩定,時常不可用。
Google map 也提供了一個類似的服務,是訪問一個 google map 的 api,這里試圖封裝了 google map 的服務。使使用 apple 和 google 的服務的接口基本一致,替換起來很容易。Google map 的這個服務在中國的狀態比 apple 稍微好一些,但也有不穩定的時候。我猜想,apple 也許是使用 google 的服務封裝了自己的 MKReverseGeocoder。若是如此,這里的嘗試也就沒有什么意義了。
DOUHttpRequest 是對 ASIHTTPRequest 的一個簡單封裝。這些代碼可以 繼承自 MKReverseGeocoder。這樣,使用方法就和 MKReverseGeocoder 一樣了。
static NSString* kGeoServerUrl = @"http://maps.google.com/maps/api/geocode/json?latlng=%f,%f&sensor=true&language=en";
static NSString* kLatitudeUserInfoKey = @"latitudeUserInfoKey";
static NSString* kLongitudeUserInfoKey = @"longitudeUserInfoKey";
//
// It's tje solution for replacing MKReverseGeocoder that has problem in China.
//
- (void)startedReverseGeoderWithLatitude:(double)latitude longitude:(double)longitude {
NSString *url = [NSString stringWithFormat:kGeoServerUrl, latitude, longitude];
DOUHttpRequest *req = [DOUHttpRequest requestWithURL:[NSURL URLWithString:url] target:self];
NSNumber *lat = [NSNumber numberWithDouble:latitude];
NSNumber *lon = [NSNumber numberWithDouble:longitude];
req.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:lat, kLatitudeUserInfoKey, lon, kLongitudeUserInfoKey, nil];
DOUService *service = [DOUService sharedInstance];
[service addRequest:req];
}
- (NSDictionary *)addressDictionary:(NSObject *)obj {
NSArray* ary = nil;
if (IS_INSTANCE_OF(obj, NSDictionary)) {
NSObject* data = [(NSDictionary*)obj objectForKey:@"results"];
if (IS_INSTANCE_OF(data, NSArray)) {
ary = (NSArray*)data;
NSDictionary *dic = [ary objectAtIndex:0];
NSArray *addressComps = [dic objectForKey:@"address_components"];
//NSString *streetNumber = @"";
NSString *route = @"";
NSString *locality = @"";
NSString *country = @"";
for (NSDictionary *comp in addressComps) {
NSArray *types = [comp objectForKey:@"types"];
NSString *type = [types objectAtIndex:0];
// if ([type isEqualToString:@"street_number"]) {
// streetNumber = [comp objectForKey:@"long_name"];
// }
if ([type isEqualToString:@"route"]) {
route = [comp objectForKey:@"long_name"];
}
if ([type isEqualToString:@"locality"]) {
locality = [comp objectForKey:@"long_name"];
}
if ([type isEqualToString:@"country"]) {
country = [comp objectForKey:@"long_name"];
}
}
NSDictionary *addressDic = [NSDictionary dictionaryWithObjectsAndKeys:route, kABPersonAddressStreetKey,
locality, kABPersonAddressCityKey,
country, kABPersonAddressCountryKey, nil];
return addressDic;
}
}
return nil;
}
- (void)requestFinished:(DOUHttpRequest *)req {
NSError *error = [req error];
if (!error) {
DebugLog(@"str:%@", [req responseString]);
NSObject *obj = [[req responseString] JSONValue];
NSDictionary *addressDic = [self addressDictionary:obj];
CLLocationCoordinate2D coordinate;
coordinate.latitude = [[req.userInfo objectForKey:kLatitudeUserInfoKey] doubleValue];
coordinate.longitude = [[req.userInfo objectForKey:kLongitudeUserInfoKey] doubleValue];
MKPlacemark *placemark = [[[MKPlacemark alloc] initWithCoordinate:coordinate
addressDictionary:addressDic] autorelease];
[self reverseGeocoder:nil didFindPlacemark:placemark];
}
}
- (void)requestFailed:(DOUHttpRequest *)req {
[self reverseGeocoder:nil didFailWithError:[req error]];
}
#pragma mark - MKReverseGeocoderDelegate
static NSString * const AppleLanguagesKey = @"AppleLanguages";
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark {
NSArray *array = [[NSUserDefaults standardUserDefaults] objectForKey:AppleLanguagesKey];
NSString *currentLanguage = [array objectAtIndex:0];
// set current language as english
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"zh-Hans", nil]
forKey:AppleLanguagesKey];
NSString *local = [placemark.locality lowercaseString];
[AppContext sharedInstance].currentCityUid = local;
// reset current language
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:currentLanguage, nil]
forKey:AppleLanguagesKey];
}
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error {
TraceLog(@"reverseGeocoder :%@", [error localizedDescription]);
}
posted @
2012-01-12 21:27 lincode 閱讀(1367) |
評論 (0) |
編輯 收藏
定義OAuth(開放授權)是一個開放標準,允許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密的資源(如照片,視頻,聯系人列表),而無需將用戶名和密碼提供給第三方應用。
OAuth允許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據。每一個令牌授權一個特定的網站(例如,視頻編輯網站)在特定的時段(例如,接下來的2小時內)內訪問特定的資源(例如僅僅是某一相冊中的視頻)。這樣,OAuth允許用戶授權第三方網站訪問他們存儲在另外的服務提供者上的信息,而不需要分享他們的訪問許可或他們數據的所有內容。
認證和授權過程
在認證和授權的過程中涉及的三方包括:- 服務提供方,用戶使用服務提供方來存儲受保護的資源,如照片,視頻,聯系人列表。
- 用戶,存放在服務提供方的受保護的資源的擁有者。
- 客戶端,要訪問服務提供方資源的第三方應用,通常是網站,如提供照片打印服務的網站。在認證過程之前,客戶端要向服務提供者申請客戶端標識。
使用OAuth進行認證和授權的過程如下所示:
- 用戶訪問客戶端的網站,想操作用戶存放在服務提供方的資源。
- 客戶端向服務提供方請求一個臨時令牌。
- 服務提供方驗證客戶端的身份后,授予一個臨時令牌。
- 客戶端獲得臨時令牌后,將用戶引導至服務提供方的授權頁面請求用戶授權。在這個過程中將臨時令牌和客戶端的回調連接發送給服務提供方。
- 用戶在服務提供方的網頁上輸入用戶名和密碼,然后授權該客戶端訪問所請求的資源。
- 授權成功后,服務提供方引導用戶返回客戶端的網頁。
- 客戶端根據臨時令牌從服務提供方那里獲取訪問令牌。
- 服務提供方根據臨時令牌和用戶的授權情況授予客戶端訪問令牌。
- 客戶端使用獲取的訪問令牌訪問存放在服務提供方上的受保護的資源。
OAuth 2.0
OAuth 2.0是OAuth協議的下一版本,但不向后兼容OAuth 1.0。 OAuth 2.0關注客戶端開發者的簡易性,同時為Web應用,桌面應用和手機,和起居室設備提供專門的認證流程。規范還在IETF OAuth工作組的開發中 ,按照Eran Hammer-Lahav的說法,OAuth將于2010年末完成。
Facebook的新的Graph API只支持OAuth 2.0,Google在2011年3月亦宣佈Google API對OAuth 2.0的支援。
posted @
2011-10-27 18:15 lincode 閱讀(323) |
評論 (0) |
編輯 收藏
名稱
REST,即Representational State Transfer的縮寫。我對這個詞組的翻譯是"表現層狀態轉化"。
如果一個架構符合REST原則,就稱它為RESTful架構。
要理解RESTful架構,最好的方法就是去理解Representational State Transfer這個詞組到底是什么意思,它的每一個詞代表了什么涵義。如果你把這個名稱搞懂了,也就不難體會REST是一種什么樣的設計。
資源(Resources)
REST的名稱"表現層狀態轉化"中,省略了主語。"表現層"其實指的是"資源"(Resources)的"表現層"。
所謂"資源",就是網絡上的一個實體,或者說是網絡上的一個具體信息。它可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的實在。你可以用一個URI(統一資源定位符)指向它,每種資源對應一個特定的URI。要獲取這個資源,訪問它的URI就可以,因此URI就成了每一個資源的地址或獨一無二的識別符。
所謂"上網",就是與互聯網上一系列的"資源"互動,調用它的URI。
表現層(Representation)
"資源"是一種信息實體,它可以有多種外在表現形式。我們把"資源"具體呈現出來的形式,叫做它的"表現層"(Representation)。
比如,文本可以用txt格式表現,也可以用HTML格式、XML格式、JSON格式表現,甚至可以采用二進制格式;圖片可以用JPG格式表現,也可以用PNG格式表現。
URI只代表資源的實體,不代表它的形式。嚴格地說,有些網址最后的".html"后綴名是不必要的,因為這個后綴名表示格式,屬于"表現層"范疇,而URI應該只代表"資源"的位置。它的具體表現形式,應該在HTTP請求的頭信息中用Accept和Content-Type字段指定,這兩個字段才是對"表現層"的描述。
狀態轉化(State Transfer)
訪問一個網站,就代表了客戶端和服務器的一個互動過程。在這個過程中,勢必涉及到數據和狀態的變化。
互聯網通信協議HTTP協議,是一個無狀態協議。這意味著,所有的狀態都保存在服務器端。因此,如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生"狀態轉化"(State Transfer)。而這種轉化是建立在表現層之上的,所以就是"表現層狀態轉化"。
客戶端用到的手段,只能是HTTP協議。具體來說,就是HTTP協議里面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源(也可以用于更新資源),PUT用來更新資源,DELETE用來刪除資源。
綜述
綜合上面的解釋,我們總結一下什么是RESTful架構:
?。?)每一個URI代表一種資源;
(2)客戶端和服務器之間,傳遞這種資源的某種表現層;
?。?)客戶端通過四個HTTP動詞,對服務器端資源進行操作,實現"表現層狀態轉化"。
誤區
RESTful架構有一些典型的設計誤區。
最常見的一種設計錯誤,就是URI包含動詞。因為"資源"表示一種實體,所以應該是名詞,URI不應該有動詞,動詞應該放在HTTP協議中。
舉例來說,某個URI是/posts/show/1,其中show是動詞,這個URI就設計錯了,正確的寫法應該是/posts/1,然后用GET方法表示show。
如果某些動作是HTTP動詞表示不了的,你就應該把動作做成一種資源。比如網上匯款,從賬戶1向賬戶2匯款500元,錯誤的URI是:
POST /accounts/1/transfer/500/to/2
正確的寫法是把動詞transfer改成名詞transaction,資源不能是動詞,但是可以是一種服務:
POST /transaction HTTP/1.1
Host: 127.0.0.1
from=1&to=2&amount=500.00
另一個設計誤區,就是在URI中加入版本號:
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo
因為不同的版本,可以理解成同一種資源的不同表現形式,所以應該采用同一個URI。版本號可以在HTTP請求頭信息的Accept字段中進行區分(參見Versioning REST Services):
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
原帖:http://www.ruanyifeng.com/blog/2011/09/restful.html
posted @
2011-10-27 18:02 lincode 閱讀(302) |
評論 (0) |
編輯 收藏
PhoneGap 是一個移動開發框架。通過 PhoneGap,開發者可以使用 JavaScript 調用手機的原生功能,例如,獲取經緯度,讓手機振動等。
主頁 http://www.phonegap.com/ 。
源碼 https://github.com/phonegap/phonegap-android 。
PhoneGap 在早期,應該是使用 WebView 的 addJavaScriptInterface 方法,來為 JS 提供調用原生功能可能。addJavaScriptInterface ,可以將一個 Java 對象綁定到一個 JS 對象。是的,JS對象可以調用 Java方法。但在 PhoneGap 1.0.0 這個版本中,PhoneGap 改變了方法。
以振動功能為例,我們可以看一下程序調用的流程:
1 在 JS 中,啟動命令 main.js / navigator.notification.vibrate(0);
notification.js / Notification.vibrate.vibrate 中執行了 PhoneGap.exec(null, null, "Notification", "vibrate", [mills]);
phonegap.js / PhoneGap.exc 中執行了 var r = prompt(PhoneGap.stringify(args), "gap:"+PhoneGap.stringify([service, action, callbackId, true]));
這時,WebView 就會企圖彈出一個窗口。這時使用 android 提供的 WebChromeClient 的 API 就可以截獲 WebView 的這個動作 。
2 JAVA 中,處理命令
WebView 的 WebChromClient 實現了下面這個函數:
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)
在 onJsPrompt 中執行了 String r = pluginManager.exec(service, action, callbackId, message, async);
PlugManager 會根據收到參數,將命令分發給特定的 Plugin。這個例子中,接收的 plugin 是:Notification。
落實到 Notification 的 exec 函數:會執行這一行: this.vibrate(args.getLong(0));
振動的實現為:
public void vibrate(long time){
// Start the vibration, 0 defaults to half a second.
if (time == 0) {
time = 500;
}
Vibrator vibrator = (Vibrator) this.ctx.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(time);
}
3 Java 處理完后的數據,需要給 JS 一個反饋:這里 PhoneGap 使用了一個在客戶端本地實現的 XHRServer,具體到代碼中就是一個JAVA 類 CallbackServer。
分兩個部分介紹其行為:
本地 XHRServer,
思想是,后臺每執行完一個命令,都會將結果存在 CallbackServer 中的一個鏈表中,具體為CallbackServr的 private LinkedList<String> javascript;
這個結果其實是一段字符串表示的 JS 函數調用。例如檢測網絡調用的結果為:PhoneGap.callbackSuccess('Network Status1',{status:1,message:"wifi",keepCallback:true});
XHRServer 的行為很簡單,只要有請求來,就把鏈表中的最先進來的提出來,返回給客戶端。沒有請求來,則 10秒鐘返回一個空的回復,以維持XHRServer。
Webview 作為客戶端:
在 WebView 中,會有一個輪詢機制,這可以參考 PhoneGap.JSCallack 和 PhoneGap.JSCallbackPolling 兩個函數來訪問 XHRServer。XHRServer,返回的結果就是 WebView 需要調用的 JS 函數。 在 JS 中,eval() 函數,將返回的結果變為一個可以執行的對象,在 Webview 中執行,可以認為這即是回調函數 Callback。這也是為什么 PhoneGap 為何命名 XHRServer 為 CallbackServer 的原因。
posted @
2011-09-20 10:20 lincode 閱讀(3742) |
評論 (1) |
編輯 收藏