Apple推送通知服務教程

Apple推送通知服務教程 

生成APP ID和SSL證書

登陸iOS Provisioning Portal頁面php

首先,咱們將要新建一個App ID. 每個推送APP都須要一個惟一的對應的App ID,推送的消息將被送達到這個ID對應的APP應用中(這裏不能使用通配ID)。html

iOS Provisioning Portal頁面左側選擇 App IDs,而後點擊 New App ID 的按鈕。mysql

 

在例子中,對應的表單項填的值以下:ios

· Description: PushChatweb

· Bundle Seed ID: Generate New (this is the default option)(這是默認值)sql

· Bundle Identifier: com.hollance.PushChat數據庫

個人例子APP中對應的Bundle ID值爲 – com.yoursite.PushChat – 這裏最好替換成你本身的。一樣,你須要在 XCode中對應的工程的Bundle ID配置裏指定爲同一個值。apache

等一會,咱們將生成一個SSL證書,讓你的推送服務器能夠建立一個安全鏈接至APNS。這個證書會連接到你的這個App ID,你的推送服務器只能推送通知到特定的APP,而不是其它APP。:編程

 

在製做App ID後,應該顯示成像下面這樣:api

在「Apple Push Notification service」這一欄中,有兩個橙燈,分別對應配置APP推送功能的「開發版」和「產品版」,這也就是說,這個App ID能接收推送消息,可是咱們仍然須要設置好才能夠。點擊Configure 連接打開對應的配置頁面。

勾選「Enable for Apple Push Notification service 」的複選框,接下來點擊Development Push SSL Certificate右邊的Configure 按鈕,會彈出「Apple推送通知服務SSL證書助手」的頁面。

第一件事讓你生成一個證書籤名的請求,這一步咱們已經作過了,因此點擊Continue。下一步是上傳CSR文件。選擇在前面步驟中咱們已生成好的CSR文件,而後點擊Generate按鈕。

等待數秒鐘後便會生成SSL證書。當證書生成完成後,點擊Continue按鈕。

如今點擊Download 按鈕下載這個生成好的文件名爲「aps_developer_identity.cer」的證書。點擊Done 關閉證書助手,回到配置App ID的界面。

如今能夠看到,咱們已經生成了一個可用的證書,如今推送通知功能已經爲接下來的開發準備好了(開發版)。你能夠在須要的時候重複下載這個證書文件。須要注意的是,這個開發證書只有3個月的有效期

當你準備好發佈正式版APP時,須要在產品版的Configure裏重複上述操做,步驟是同樣的。

注:產品版的證書有效期是一年,但若是你爲了在失效期以前想從新生成這個證書,請事先確認這個app自己沒有過時。

雖然你能夠經過雙擊下載下來的aps_developer_identity.cer文件,將這個證書加到你的鑰匙串中,但你並不必定須要這麼作。固然若是你這麼作了,你會發現這個證書關聯了那個私有密匙。

生成一個PEM文件

如今咱們有3個相關文件

· CSR文件

· P12私有密鑰文件,PushChatKey.p12

· SSL證書文件,aps_developer_identity.cer

把這三個文件存到一個安全的地方。你能夠扔掉CSR但個人意見是保留它更容易。當你證書過時時,你能夠用相同的CSR產生一個新的。若是你想產生一個新的CSR,你也要產生一個新的私鑰。當你重複使用CSR時你繼續使用存在的私鑰,僅僅.cer文件將會改變。

咱們必須轉換證書和私鑰成一個對咱們更有用的格式。由於咱們服務器的推送部分是用php寫的,咱們將要把證書和私鑰整合到一個單獨PEM格式的文件裏。

PEM的內容並非很重要(事實上,我也不知道),可是它讓PHP使用咱們的證書變得很是方便。若是你想用其餘語言寫你的推送服務,如下的步驟對你來講也許會沒有做用

咱們將要使用命令行OpenSSL工具來作這些,打開命令行而後執行如下的步驟。

前往你下載好文件的文件夾,我是放在Desktop上

$ cd /Users/matthijs/Desktop

把.cer文件轉換成.pem文件:

$ openssl x509 -in aps_developer_identity.cer -inform der

-out PushChatCert.pem

把私鑰.p12文件轉換成.pem文件:

$ openssl pkcs12 -nocerts -out PushChatKey.pem -in PushChatKey.p12

Enter Import Password:

MAC verified OK

Enter PEM pass phrase:

Verifying – Enter PEM pass phrase:

你首先須要爲.p12文件輸入passphrase密碼短語,這樣OpenSSL能夠讀它。而後你須要鍵入一個新的密碼短語來加密PEM文件。仍是使用」pushchat」來做爲PEM的密碼短語。你須要選擇一些更安全的密碼短語。

注意:若是你沒有鍵入一個PEM passphrase,OpenSSL將不會返回一個錯誤信息,可是產生的.pem文件裏面將不會含有私鑰。

最後。把私鑰和證書整合到一個.pem文件裏:

$ cat PushChatCert.pem PushChatKey.pem > ck.pem

爲了測試證書是否工做,執行下面的命令

$ telnet gateway.sandbox.push.apple.com 2195

Trying 17.172.232.226…

Connected to gateway.sandbox.push-apple.com.akadns.net.

Escape character is ‘^]’.

它將嘗試發送一個規則的,不加密的鏈接到APNS服務。若是你看到上面的反饋,那說明你的MAC可以到達APNS。按下Ctrl+C 關閉鏈接。若是獲得一個錯誤信息,那麼你須要確保你的防火牆容許2195端口。

而後再次鏈接,此次用咱們的SSL證書和私鑰來設置一個安全的鏈接:

$ openssl s_client -connect gateway.sandbox.push.apple.com:2195

-cert PushChatCert.pem -key PushChatKey.pem

Enter pass phrase for PushChatKey.pem:

你會看到一個完整的輸出,讓你明白OpenSSL在後臺作什麼。若是鏈接是成功的,你能夠鍵入一些字符。當你按下回車後,服務就會斷開鏈接。若是在創建鏈接時有問題,OpenSSL將會給你一個錯誤消息,可是你不得不向上翻輸出LOG,來找到它。

注意有2個不一樣的APNS服務器:沙盒服務器是用來作測試,而live服務器是爲產品模式使用的。因爲以上緣由,咱們用沙盒服務器,由於咱們的證書是用來作開發的,不是產品使用的。

製做Provisioning Profile

在Provisioning Porta尚未完成,點擊側邊欄Provisioning,點擊New Profile。

在上面的區域裏我是這麼填寫的:

Profile Name:PushChat Development

Certificates:勾選你的證書

App ID:PushChat

Devices:勾選你的設備

這和你之前作的任何provisioning profile都不一樣。咱們須要作一個新的profile,由於每一個推送app必須有它本身的profile來鏈接正確的App ID。

點擊提交而後profile就會產生。新的profile將會是Pending狀態。刷新Development Provisioning Profiles頁面,狀態就會變成Active而後你就能夠下載文件了(名字是PushChat_Development.mobileprovision)。

經過雙擊它或者拖到Xcode的圖標添加provisioning profile到Xcode裏。若是發佈你的app,你將不得不重複這個過程來產生一個AdHoc或者App Store distribution profile。

 

在教程的中,你已經能讓你的iPhone應用接收推送消息,而且知道了怎樣使用一個PHP的腳原本推送一個測試消息。在這個第二部分和最後一個部分中,你將會學到如何用APNS來作一個簡單的應用,以及一個簡單的PHP Web服務來加強你的應用功能。

注意:這篇教程相對比較長,你須要作好一個較長的時間準備去看完它。但它是很值的,一旦你看完它,你就擁有了一個更強大的應用和使用推送消息的Web服務。

 

介紹PushChat

在這個教程中,咱們將要作一個叫作PushChat的簡單的發消息應用,它採用推送消息來發信息。如下是它的樣子:

用戶看到的第一個屏幕是登錄界面。在這裏用戶輸入他們的暱稱和一個secret code。用戶與他們的朋友們分享這個secret code。

每個使用相同的secret code的人能夠看到相互的消息。那麼實際上,這個secret code至關於一個聊天室的名字。固然,若是你能猜到別人的code,那麼你也能看到別人消息,因此它叫作secret code。

當用戶按了Start!按鈕,應用就給咱們的服務器發送一條註冊聊天室的消息。而後登錄界面就跳轉到聊天界面:

Secret code 顯示在navigation bar上。本地用戶發送的消息顯示在屏幕的右邊,收到的消息則顯示在左邊。那麼在這樣例子中,這個用戶和一個叫作SteveJ的人同時登錄到了這個叫作「TopSecretRoom123」的聊天室中。

第三個也是最後一個界面是輸入界面。

 

這裏沒有什麼特別的地方。它只是一個帶有鍵盤的text view。消息大小被限制在190個字節,屏幕的上方顯示了剩餘字節數。

我添加這個限制的目的是由於一條推送消息的最大長度是256個字節,它包括了開頭的JSON內容。

當用戶按下保存按鈕的時候,消息將會被髮送到咱們的服務器,而後推送到其餘登錄相同聊天室的用戶。

The Server API 服務器API

在以上闡述PushChat如何工做的過程當中,我提到了幾回「咱們的服務器」。咱們須要使用一臺服務器是由於咱們要使不一樣的設備相互協做。當只有你一我的在說的時候,這也算不上什麼聊天。

我用PHP和MySQL寫了一個簡單的Web服務。這個iPhone應用將會給咱們的服務器發送如下命令:

加入當用戶在登錄界面登錄的時候,咱們會給服務器發送他的暱稱、它的secret code和它的device token。服務器在活躍用戶列表中添加一條關於他的記錄。這之後,同一個聊天室成員發送的任何消息將會被髮送到這個新用戶上。

LEAVE這與加入相反。用戶在聊天界面中經過按下Exit按鈕退出聊天。咱們給服務器發送一條LEVEL的命令讓它從活躍用戶列表中刪除這個用戶。

MESSAGE.當一個用戶在輸入界面按下Sava按鈕的時候,咱們會給咱們的服務器發送一條新的文本消息。而後服務器將爲每個聊天室成員將這些消息轉換成推送消息,而後傳給APNS。過一下子,ANPS就會將這些消息推送給那些用戶的設備。

UPDATE. 這是讓服務器知道,這個用戶擁有了新的device token。 Token也許會常常更新,若是發現更新了,就必須讓服務器知道.關於這個以後會詳細討論

它的工做流程如圖所示

 

要頻繁的向服務器發送數據,要讓服務器知道用戶加入或離開一個聊天室或者她正在發送一條新的消息。服務器輪流建立推送消息而後發給APNS.蘋果服務器負責將這些消息發送給app。當app收到一條新的推送消息,它會在message.app的Chat View上顯示一條含有文字內容的聊天氣泡

啓用服務器

Setting up the server

若是你是WEB新手,你能夠先看一下Ray’s tutorial on PHP and MySQL,咱們將使用MAMP在mac上安裝服務器和數據庫,若是你的APP正在開發階段,這是個不錯的方案,MAMP容易安裝而且你也沒必要爲一個分離的服務器付錢。

惟一的要求是你的MAC和你的iPhone共享一個本地網絡,不然APP將無法和SERVER交互,不少人家裏有WIFI,因此這不是個大問題。

固然,一旦你的APP上線了,你要安裝真正的和INTERNET鏈接的服務器。

你能夠免費下載MAMP(也有付費版,可是免費版夠用了)

PDO,PDO_MYSQL,MBSTRING,OPENSSL.

MAMP 包含APACHE服務器,PHP腳本,MySQL數據庫,咱們將使用這全部的三樣,若是你想在安裝了PHP設備上而不是MAMP上使用這個教程的服務器代碼,你要確保你額外安裝並啓動了如下擴展:

安裝MAMP很容易,將下載的文件解壓,打開DMG文件,接受License許可,將MAMP文件夾拖拽到你的應用的目錄,DONE!

啓動MAMP,打開APPLICATION/MAMP點擊MAMP圖標(有大象的那個),打開了MAMP窗口。

點擊OPENSTARTPAGE 按鍵,打開了默認瀏覽器,進入welcome界面。

Great!如今下載PUSHCHART SERVER代碼,解壓,確保在你的桌面上解壓了這玩意兒,我之因此說這話,由於咱們須要爲APACHE web服務器 配置將這些文件的路徑。

打開APPLICATIONS/MAMP/CONF/APACHE/HTTPD.CONF 添加以下代碼行:

複製代碼
Listen 44447
 
<VirtualHost *:44447>
    DocumentRoot "/Users/matthijs/Desktop/PushChatServer/api"
    ServerName 192.168.2.244:44447
    ServerAlias pushchat.local
    CustomLog "/Users/matthijs/Desktop/PushChatServer/log/apache_access.log" combined
    ErrorLog "/Users/matthijs/Desktop/PushChatServer/log/apache_error.log"
 
    SetEnv APPLICATION_ENV development
    php_flag magic_quotes_gpc off
 
    <Directory "/Users/matthijs/Desktop/PushChatServer/api">
        Options Indexes MultiViews FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>
複製代碼

有些須要修改的,個人PUSH CHAT SERVER 的安裝目錄是「/Users/matthijs/Desktop」.把它改爲

你解壓的文件的位置。

在SERVERNAME 行裏修改IP,改爲你的MAC的IP,若是你不知道如何獲取IP,打開系統偏好設置,進入網絡面板.

我使用個人MACBOOK的WIFI的IP地址,不過 以太網的IP也能夠. 注意你在SERVERNAME處將 端口號改成44447.

ServerName <your IP address>:44447

這裏的WEB服務使用44447端口,這是 隨便 設定的值, 網頁服務 使用了80端口,默認的MAMP網頁服務在8888端口,這裏只是選擇一個不會與其它服務衝突的端口.

「pushchat.local」做爲服務器的別名,這就像一個域名,但它僅僅工做在本地網絡上,咱們要把名字改爲IP地址,完成這個的捷徑就是,編輯文件 「/ETC/HOSTS」在文件末尾添加以下代碼行:

127.0.0.1 pushchat.local

在MAMP窗口,點擊STOP SERVERS,等燈變紅,點擊START SERVERS,若是你 對HTTPD.CONF的修改沒有錯的話,兩個服務器的燈都應該再次變綠。

打開默認瀏覽器,到 http://pushchat.local:44447.能看到信息:

If you can see this, it works!

太棒了!這意味着服務器API的APACHE和PHP代碼成功安裝,如今要配置服務器。

啓動數據庫

設置數據庫

返回MAMP起始頁(你能夠經過點擊MAMP窗口的OPEN THE START PAGE).點擊頂部的PHPMYADMIN按鍵,屏幕以下:

在CREATE DATABASE 區域鍵入 pushchat,選擇 「utf8_general_ci」覈對,而後點擊 CREATE建立數據庫,能夠看到數據建立成功的提示。

點擊 屏幕頂端的Privileges按鍵,而後添加一個新用戶。

以下填寫:

· User name: pushchat

· Host: localhost

· Password: d]682\#%yI1nb3

· Privileges: Check 「Grant all privileges on database 「pushchat」」

點擊GO,添加用戶,你能夠修改密碼,但你同時要記住在不一樣的PHP腳本中更改密碼。

這是 數據庫和用戶,如今咱們須要向數據庫中添加表,點擊屏幕頂部的SQL按鍵,將以下代碼貼進TEXT BOX.

複製代碼
USE pushchat;
 
SET NAMES utf8;
 
DROP TABLE IF EXISTS active_users;
 
CREATE TABLE active_users
(
    udid varchar(40) NOT NULL PRIMARY KEY,
    device_token varchar(64) NOT NULL,
    nickname varchar(255) NOT NULL,
    secret_code varchar(255) NOT NULL,
    ip_address varchar(32) NOT NULL
)
ENGINE=InnoDB DEFAULT CHARSET=utf8;
複製代碼

你也能夠在 PushChatServer/database/api.sql中找到這些語句,

點擊GO,執行腳本,建立一個新表——active_users

當服務器API收到JOIN命令,咱們將爲用戶在表中添加一個新的記錄,相反的,當LEAVE命令發送,將從表中移除用戶的紀錄。

這是另外一個要添加的表,重複以上動做添加:

複製代碼
USE pushchat;
 
SET NAMES utf8;
 
DROP TABLE IF EXISTS push_queue;
 
CREATE TABLE push_queue
(
    message_id integer NOT NULL AUTO_INCREMENT,
    device_token varchar(64) NOT NULL,
    payload varchar(256) NOT NULL,
    time_queued datetime NOT NULL,
    time_sent datetime,
    PRIMARY KEY (message_id)
)
ENGINE=InnoDB DEFAULT CHARSET=latin1;
複製代碼

能夠在PushChatServer/database/push.sql找到這些語句。

去論什麼時候 服務器接收 到MESSAGE命令,他將 推送消息添加進這個表。

配置服務器API

 

 

在這裏我就不大篇幅描敘服務器的API是如何工做的,可是我已經說了不少關於API.PHP,即便你不知道PHP你應該也能跟上。

與咱們息息相關的是 api_config.php 文件,這個文件包含了服務器API的配置選項。

這裏有兩組 配置選項,一個是development模式,一個是production模式,作兩組設置 你能夠很容易的在這二者之間切換。

可是怎樣告訴API他運行DEVELPMENT模式仍是PRODUCTION模式?答案在 APACHE VITRUAL HOST 配置,以前咱們添加了 一個<VirtualHost>模塊在HTTP.CONF文件裏:

複製代碼
&lt;VirtualHost *:44447&gt;
    …
 
    SetEnv APPLICATION_ENV development
 
    ...
&lt;/VirtualHost&gt;
複製代碼

「SetEnv APPLICATION_ENV development」指令建立了一個 環境變量,它決定了腳本在那種模式下運行。若是你想切換到PRODUCTION CONFIGURATION 模式,把這行去掉,或者將DEVELOPMENT 換成 PRODUCTION。

若是 你用了和我相同的名字作 數據庫名字,在API.CONFIG.PHP裏你就不用修改任何值了,可是 若是你用了其餘的值,你要在這裏 輸入它們,不然 API就連不上數據庫。

瀏覽 http://pushchat.local:44447/test/database.php,你獲得以下消息:

Database connection successful!

建議:若是你獲得了 不理解的錯誤。在PushChatServer/log/apache_error.log檢查PHP錯誤紀錄和在PushChatServer/log/apache_error.log.檢查Apache 的錯誤記錄,這些log文件可能告訴你 哪裏錯了。

到這裏完成 SERVER API的安裝。

讓咱們開始編程吧!

 

下載PushChatStarter代碼並解壓,這是沒有網絡和推送代碼的PUSHCHAT應用,我會快速解釋這個應用是怎麼工做的,而後我會向它裏面添加代碼,讓他可以和服務器對話,並能接受推送通知。

總之,這是你想了解的東西!

你能夠在模擬器運行 PUSHCHAT,由於如今他沒有任何推送功能,首先在XCODE中打開工程,到目標設置(Target Setting)。你必須將綁定ID(bundle ID——我這項目是「com.hollance.PushChat」 )改換成你本身的bundle ID,這樣你就能夠在iOS Provisioning Portal(APPLE開發者中心)中選擇各類你須要的消息方式……這很重要由於APP須要用配置文件(provisioning profile)簽名。

 

編譯運行,好好把玩一下,由於新的APP使用的BUNDLE ID 和最開始是的TEST APP相同,

因此在編譯運行以前最好先 卸載那個 舊的APP,以確保你的IPHONE不會報錯。

打開 MAINWINDOW.XIB,看一下 APP是如何構成的:

MAIN WINDOW包含一個 Navigation Controller, ChatViewController是它的根控制器,那個視圖控制器展現收到的和發送的消息,你能夠在NIB裏看到,是一個普通的UITableView 控制器。

speech bubbles 由 MessageTableViewCell繪製,MESSAGETABLEVIEW是UITABLEVIEWCELL SpeechBubbleView是UIView的子類,完成普通繪製功能,這些是簡單的TableView編程,你確定早就知道了,我就不花時間解釋了。

兩個SCREEN, Login screen and 和 Compose screen,

都是在 CHAT VIEW CONTORLLER頂部展現的view controller模型,可是 Long screen在LoginViewController裏實現,Compose Screen在ComposeViewController裏實現,它們都有各自的NIB。這些是很簡單的SCREEN,查看源碼詳細看一下。

還剩下兩個數據模型類(data model class),DataModel和Message,message描敘了一條單獨消息內容,它多是由當前用戶發送的,或者從另外一個用戶收到的,每個消息有一個發送者的姓名(sender name),一個日期,還有消息實體。

DataModel 類負責這些消息對象,當一個新的消息被髮送或者收到,DataModel將其加到表中,而後將表保存到 APP的文檔目錄的一個文件裏、

當APP啓動時,DataModel從文件中加載信息,這個是對應用的一個簡單描敘,我建議在繼續下一步以前看看代碼,代碼有大量註解解釋每一部分如何工做的。

經過服務器聊天

咱們講解完了app的每個view並開始向其中添加代碼,使得它能和服務器通信。咱們的服務器須要app發送HTTP Post請求,因此我打算用ASIFormDataRequest來發送數據給服務器。全部的請求類已經被加入PushChat的初始代碼中,因此這裏不須要再次安裝。

若是你第一次在iOS apps上使用web服務,我建議你先去看一下Ray’s previous tutorial 中有關這些的教程

添加以下代碼到defs.h中

#define ServerApiURL @」http://192.168.2.244:44447/api.php」

咱們將把HTTP POST請求發送到這個URL。你須要修改IP地址來指向你的服務器。同時要確認MAMP正在運行,也就是說服務器是可訪問的,而且你的iPhone和服務器在同一局域網下。

咱們最好先從登錄界面開始。當用戶按下Start!按鈕,咱們須要發送一條JOIN命令到服務器。這讓服務器知道這裏有一個新的用戶。服務器將會把他添加到活躍用戶列表中

添加以下代碼到LoginViewController的頂部:

#import "ASIFormDataRequest.h"
#import "MBProgressHUD.h"

把下面一大段代碼添加到userDidJoin和loginAction方法之間:

複製代碼
- (void)postJoinRequest
{
    MBProgressHUD* hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.labelText = NSLocalizedString(@"Connecting", nil);
 
    NSURL* url = [NSURL URLWithString:ServerApiURL];
    __block ASIFormDataRequest* request = [ASIFormDataRequest requestWithURL:url];
    [request setDelegate:self];
 
    [request setPostValue:@"join" forKey:@"cmd"];
    [request setPostValue:[dataModel udid] forKey:@"udid"];
    [request setPostValue:[dataModel deviceToken] forKey:@"token"];
    [request setPostValue:[dataModel nickname] forKey:@"name"];
    [request setPostValue:[dataModel secretCode] forKey:@"code"];
 
    [request setCompletionBlock:^
    {
        if ([self isViewLoaded])
        {
            [MBProgressHUD hideHUDForView:self.view animated:YES];
 
            if ([request responseStatusCode] != 200)
            {
                ShowErrorAlert(NSLocalizedString(@"There was an error communicating with the server", nil));
            }
            else
            {
                [self userDidJoin];
            }
        }
    }];
 
    [request setFailedBlock:^
    {
        if ([self isViewLoaded])
        {
            [MBProgressHUD hideHUDForView:self.view animated:YES];
            ShowErrorAlert([[request error] localizedDescription]);
        }
    }];
 
    [request startAsynchronous];
}
複製代碼

呀,介都是嘛啊,讓咱們一行行的來解釋下。

MBProgressHUD* hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.labelText = NSLocalizedString(@"Connecting", nil);

這將顯示一個轉菊花並阻止整個屏幕的活動,你能夠在Ray’s article中瞭解關於MBProgressHUD更多的內容

NSURL* url = [NSURL URLWithString:ServerApiURL];
    __block ASIFormDataRequest* request = [ASIFormDataRequest requestWithURL:url];
    [request setDelegate:self];

這是爲咱們的服務器URL建立一個ASIFormDataRequest對象。ASIFormDataRequest在向服務器發送POST請求時作得很出色。你告訴他URL,給他POST的內容 而後他就發出去了,我將要花一分鐘來解釋下__block的意思

[request setPostValue:@"join" forKey:@"cmd"];
    [request setPostValue:[dataModel udid] forKey:@"udid"];
    [request setPostValue:[dataModel deviceToken] forKey:@"token"];
    [request setPostValue:[dataModel nickname] forKey:@"name"];
    [request setPostValue:[dataModel secretCode] forKey:@"code"];

在這裏,咱們向request中添加了POST的內容。我設計的服務器API將會在POST的數據中查找cmd的字符串。這個值決定API將會執行什麼命令。這段代碼是執行Join命令。

Join有四個參數。用戶的暱稱、secret code是顯而易見的,這些事用戶在login 頁面所填入的信息。可是「udid」 和 「token」呢?也許你猜到了,token是用於推送的device token,這個是讓服務器知道他要給設備推送信息時的地址。

UDID是設備的惟一ID,在數據庫中標記用戶我更青睞於用設備ID。我能夠用nickname代替,可是這樣就不能存在兩個相同暱稱的用戶,咱們就須要在app中告訴用戶他的暱稱已經被使用了。

我也能夠用device token來當作用戶的惟一id,可是device token時常更新。可是udid歷來不會改變。我沒有說這種方法適用全部web服務,可是對於咱們來講已經足夠了。

好的,如今開始變得有趣了

[request setCompletionBlock:^
    {
        ...
    }];

咱們用了塊函數(blocks)!這裏有兩種方法做爲ASIFormDataRequest的通知結果。老方式是實現requestFinished 和 requestedFailed delegate。這是Ray在他的web服務文章中使用的方式。

這種方式能夠很好的工做,可是必須是iOS4.0以上。Blocks被介紹爲一種代替delegate的方式。但只是爲了歡樂,我決定在這篇教程中使用塊函數。這裏咱們設置了完成時的塊函數。^{}之間的代碼,如今是不會被執行的,直到請求成功的完成。

還記得在我建立ASIFormDataRequest對象時的__block標記嗎?那是用來阻止block中保持對request引用的。由於request已經在block中保持了引用,這會致使retain循環,並致使內存泄露。

若是你接受不了,那就不要再糾結他了。只要記得當你用塊函數替代delegate方法之時,把__block放到ASIFormDataRequest以前。

在塊函數中,咱們這樣:

if ([self isViewLoaded])
        {
            [MBProgressHUD hideHUDForView:self.view animated:YES];
}

首先,咱們判斷是否還在加載。由於請求是異步在後臺的,他會過一段時間纔會完成。理論上說,在此期間咱們的view是有可能被移除的(unload),例如當收到低內存警告。在那時候,咱們僅僅無視請求曾經開始過的事實。

這種狀況可能有點極端,可是這只是我程序的一些防禦措施。會考慮這種奇怪的狀況的下意識是很是好的,特別是你在後臺作事情。

剩下的塊函數是這樣的:

複製代碼
if ([request responseStatusCode] != 200)
{
    howErrorAlert(NSLocalizedString(@"There was an error communicating with the server", nil));
}
else
{
    [self userDidJoin];
}
複製代碼

咱們檢查了相應的狀態碼(status code)。這是服務器API返回的HTTP狀態碼。若是一切正常,狀態碼是「200 OK」然而,計算機和internet是變化無常的,有時會出錯。例如若是MySQL數據庫掉線了,server API會返回 「500 Server Error」 出現那種狀況時,咱們會顯示一個alert view。

在創造UIAlertView而且顯示出來,使用ShowErrorAlert函數是很方便的。而且注意我在建立用於顯示的字符串的時候使用了NSLocalizedString。這是一個讓你本地化變得更容易的好習慣。請參看 Sean Berry’s tutorial on localization.

若是這裏沒有任何錯誤,咱們調用userDidJoin方法。這個方法告訴DataModel,用戶成功加入聊天室,而且關閉當前view,回到主屏幕。

由於沒人知道internet會發生什麼,咱們一樣爲請求由於某些緣由失敗,設置一個塊函數。常常是由於服務器不可訪問,或者是請求時間過長(time out)致使失敗。

複製代碼
[request setFailedBlock:^
    {
        if ([self isViewLoaded])
        {
            [MBProgressHUD hideHUDForView:self.view animated:YES];
            ShowErrorAlert([[request error] localizedDescription]);
        }
    }];
複製代碼

這裏隱藏了轉菊花,並用alert view顯示錯誤。

複製代碼
- (IBAction)loginAction
{
    ...
 
    // REPLACE THIS LINE:
    [self userDidJoin];
 
    // WITH:
    [self postJoinRequest];
}
複製代碼

最後,咱們告訴請求去作他該作的事情。你將要一直使用異步請求來和服務器通信。請求會須要若干秒來完成,若是使用同步請求,你的程序會在請求的全過程當中徹底失去響應。而且若是無響應的時間過長,OS會終止掉你的程序。

仍是在LoginViewController.m中,在loginAction中進行以下改變: – (NSString*)udid; – (NSString*)deviceToken; – (void)setDeviceToken:(NSString*)token;

事先,咱們在用戶點擊Start!以後直接調用userDidJoin。如今不能這樣了,咱們只能在服務器請求完成以後纔可調用。

若是你足夠仔細,你會發現咱們調用了兩個DataModel並沒定義的方法(udid 和 deviceToken)。讓咱們添加他們並擺脫編譯器的警告。

在DataModel.h中添加:

- (NSString*)udid;
- (NSString*)deviceToken;
- (void)setDeviceToken:(NSString*)token;

在DataModel.m @implementation中添加以下代碼:

- (NSString*)udid
{
    UIDevice* device = [UIDevice currentDevice];
    return [device.uniqueIdentifier stringByReplacingOccurrencesOfString:@"-" withString:@""];
}

這是獲取設備的UDID。可是默認的UDID中間有破折號,可是咱們只想讓它的長度固定在40個字符。因此咱們剝去破折號(僅供參考:咱們的模擬器的device ID 只有32個字符)。

還在DataModel.m中@implementation 之上 添加以下代碼:

static NSString* const DeviceTokenKey = @"DeviceToken";

添加下面的代碼到@implementation 中

複製代碼
- (NSString*)deviceToken
{
    return [[NSUserDefaults standardUserDefaults] stringForKey:DeviceTokenKey];
}
 
- (void)setDeviceToken:(NSString*)token
{
    [[NSUserDefaults standardUserDefaults] setObject:token forKey:DeviceTokenKey];
}
複製代碼

這裏是initialize方法的小改動。

複製代碼
+ (void)initialize
{
    if (self == [DataModel class])
    {
        [[NSUserDefaults standardUserDefaults] registerDefaults:
            [NSDictionary dictionaryWithObjectsAndKeys:
                @"", NicknameKey,
                @"", SecretCodeKey,
                [NSNumber numberWithInt:0], JoinedChatKey,
 
                // 添加了這一行
                @"0", DeviceTokenKey,
 
                nil]];
    }
}
複製代碼

咱們這裏作了什麼?在setDeviceToken,咱們將token存儲在NSUserDefaults字典中。在deviceToken中咱們從NSUserDefaults讀取出來。若是你對NSUserDefautls不熟悉,這是一個很簡單但頗有用的類,能讓你存儲你app的設置。

initialize方法是用來Objective-C第一次運行DataModel類時調用的方法。咱們將初始化的默認值存入NSUserDefaults

咱們向默認值中添加了一行,device token 爲字符串 0,我一下子會解釋這爲何是必須的。目前,它表明咱們發送JOIN命令到咱們的服務器,它使用0爲device token代替真實的token

哎,咱們花了這麼久來說述這些。下一篇教程讓咱們編譯運行它,並看它是否正確工做。

相關文章
相關標籤/搜索