使用YouTube API 打造影音搜尋App

文章推薦指數: 80 %
投票人數:10人

Google 關於YouTube API 的文件數量龐大,所以本文的目標是將收集自不同網頁的資訊集合在一起,並協助你快速且容易地使用這些API ... Available on iOS and Android. Swift程式語言 使用YouTubeAPI打造影音搜尋App GabrielTheodoropoulos Nov12,2015 42分鐘閱讀時間 In:Swift程式語言 分享 因為GoogleAPI和服務也能夠運用於行動平台,所以我們當然不能放過這個好好研究一番的機會,而且事實上,我們在過去也曾處理過Google技術。

舉例而言,這篇文章講的是GoogleMapsSDK。

而在本文中,我們將首度揭露YouTubeAPI這項全然不同的服務。

YouTubeAPI非常容易使用,不過有些事情你最好先知道一下,否則使用起來可能會感覺卡卡的。

請容我給你一點提示:我們將不會使用任何SDK或者iOS本身的程式庫。

相反地,我們只會送出簡單的HTTP要求(其實就是GET要求),向Google擷取資料。

結果會是JSON格式,所以你最好先知道一下JSON格式的資料如何編碼。

你不必是JSON專家;只需要了解其格式即可。

如果需要協助的話,可以在這裡找到一些有用的資訊。

Google關於YouTubeAPI的文件數量龐大,所以本文的目標是將收集自不同網頁的資訊集合在一起,並協助你快速且容易地使用這些API。

雖然我們只會介紹一小部分的YouTubeAPI,但是你學到的知識將可以幫助你理解其餘的部分。

不過在繼續往下閱讀之前,我必須先提醒你幾件事。

如前所述,我們會向YouTubeAPI送出HTTP要求以便取得資料。

這些要求會觸發執行特定的函式,實際上代表的是特定的API任務。

舉例而言,channels.list函式可以取得頻道(Channel)的資料。

當然了,針對本文所要探討的每個函式(HTTP要求),我都會提供相關文件的連結。

每個函式都需要一些參數和篩選器。

在介紹這類函式用法的小節當中,我會說明即將使用哪些參數和篩選器,至於細節的部分,可以點擊文章中的連結以便閱讀更多的資訊。

在此就不贅述Google文件的內容了。

針對向GoogleAPI發出的要求,其中有些會需要使用者授權,有些則不會。

針對需要授權的那些API,使用的是OAuth2.0協定,好讓使用者能夠安全地以Google帳號登入。

至於不需要授權的那些API,只需要簡單的API金鑰就可以用來應付不需要授權的要求了。

在本文中,我們只會處理第2種情況,也就是說我們並不會要求使用者登入。

但是請相信我,這些議題要用單篇文章講完並不容易。

我強烈建議你可以在閱讀本文的同時,搭配一些特定的Google文件。

如果你想要先嘗鮮一下,可以參考下列這些連結: YouTube資料API參考手冊 YouTube資料API簡介 實作與遷移指南 內容搜尋 除了上述這些連結之外,也歡迎隨意瀏覽並且閱讀任何你感興趣的內容。

先說明一下,我們即將使用的是YouTubeAPIv3,這是Google目前建議採用的版本。

最後容我再提醒一點,我們實在無法提供所有的連結(不只上述這些連結,還有接下來將會看到的那些連結),因為Google文件太過廣泛了。

因此我有必要將本文的內容限縮在合理的範圍之內。

好的,就讓我們開始動手吧,在本文的最後,我們將會完成一支可以用來下載頻道和影片(Video)、搜尋以及播放影片的App。

且聽我們娓娓道來。

範例App簡介 我將從下載Starter專案開始介紹我們要在本文中開發的範例App。

在此壓縮檔中,你可以看到介面和所需的IBOutlet屬性以及IBAction函式,還有其他必要的程式碼,所以我們只需要專注於實作App的邏輯就可以了。

注意:你需要使用Xcode7以上版本來執行此範例程式。

在開始撰寫程式碼之前,我們必須從Google取得API金鑰,好讓我們能夠向YouTubeAPI發出要求。

我們需要遵循一道特定的程序,不過我現在還不打算詳細介紹;在下一節中我們才會一步一步說明。

現在讓我們先來看一下範例App的模樣: 此範例App具備多重目標: 下載(並顯示)YouTube頻道的詳情。

下載(並顯示)頻道中的影片播放清單。

能夠搜尋頻道和影片。

播放影片。

關於使用者介面的部分,此App是由2個視圖控制器所組成。

第1個視圖控制器擁有3個子視圖:表格視圖、文字欄位、分段元件。

這些子視圖的用途分述如下: 在表格視圖中,將會顯示頻道或影片的資訊。

這些影片可以是頻道的播放清單,或是搜尋的結果。

文字欄位是搜尋的欄位。

在此輸入的關鍵字隨後會向YouTube發出搜尋要求。

分段元件非常重要。

當選取的是第1個索引時,表格視圖將會顯示頻道。

此外搜尋結果也只會侷限於頻道。

另一方面,當選取的是第2個索引(影片索引)時,表格視圖將會顯示現有的影片播放清單,而搜尋結果也只會與影片有關。

第2個視圖控制器是我們的播放器視圖控制器;用來播放選取的影片的串流。

我們會在最後一個小節介紹這個部分。

此外我們將會使用Google的輔助程式庫來「建立」播放器並且實現串流。

我們對於範例App的規劃如上所述,現在就讓我們開始來實作吧。

建立API金鑰 探索YouTubeAPI的第一步,就從這裡開始:在GoogleDevelopersConsole中替我們的App建立一筆新的記錄(新的專案),以便能夠存取YouTubeAPI。

事實上,在Console中建立完新的專案之後,還需要做2件事: 啟用(Enable)YouTubeAPI,以便在我們的App中使用。

建立API金鑰,以便向YouTubeAPI發出要求。

請留意(再度提醒),我們在本文中向YouTubeAPI發出的要求並未經過授權程序,這意味著我們不會要求使用者以他們的Google帳號來登入我們的App。

當然了,只是透過運用API金鑰,的確也限制了能夠發出的要求,如果想要完整的存取權限,則需要完整的授權。

不過就本文的內容而言,我們只需要使用API金鑰就夠了。

現在請連結到DevelopersConsole並且為我們的專案建立新的記錄。

假使你目前並未登入Google的話,請即刻登入。

必須要有Google帳號才能夠繼續遵循本文的步驟。

登入之後看到的會是如下所示的畫面: 點擊左上角的藍色CreateProject(建立專案)按鈕,在彈出視窗中輸入新專案的名稱。

我命名為YTDemo,不過你可以選擇任何你喜歡的名稱。

請留意,如果你覺得已經不再需要此專案的話,最後也可以從Console刪除這筆記錄。

下一步是啟用此專案的YouTubeAPI功能。

記住,你可以為同一個專案啟用多組API,不過此刻你還不需要使用到其他的API。

啟用YouTubeAPI的方法是,從視窗左側的功能表中點擊APIs&auth>APIs選項。

在主視窗區域中找到YouTubeDataAPI(YouTube資料API)並且點擊其連結。

瀏覽器會將你帶領到新視窗,你可以(也必須)在那裡啟用YouTubeAPI。

只要點擊上方的EnableAPI按鈕,一切就準備就緒了。

現在我們已經啟用完YouTubeAPI了,接著必須建立我們想要使用的API金鑰。

來到APIs&auth>Credentials功能表,點擊CreatenewKey(建立新的金鑰)按鈕之後將會出現新視窗。

在彈出視窗中,必須選取我們的專案所適用的平台。

這時候我打賭你會毫不猶豫地點擊iOSkey(iOS金鑰)按鈕。

但是請別這麼做。

根據Google的文件,iOS金鑰無法適用於所有的API,而YouTubeAPI正好就是其中一組。

我可以證明這點,因為基於好奇我曾嘗試著建立這樣一把金鑰,並且套用於範例App中,但是完全無法使用。

你應該做的,其實是點擊Browserkey(瀏覽器金鑰)按鈕。

在彈出視窗中,只要點擊Create即可,不要在欄位中輸入任何文字。

接著回到帳號密碼的憑證視窗,你將會發現新的金鑰已經建立完成。

如你所見,你可以套用各種動作,甚至可以刪除,但是我們現在當然還不會這麼做。

花了一些時間,我們在DevelopersConsole中的設定工作總算大功告成,但是還不要登出。

讓瀏覽器繼續開著,因為你很快就會需要將產生的金鑰複製到iOSApp當中。

擷取資料的機制 在產生完API金鑰之後,我們便可以返回Xcode開始處理Starter專案。

如果仔細回想我們想要實作的目標和功能(取得頻道和影片、實現搜尋),將會意識到存在著一個共同的元素。

亦即我們都必須向Google的伺服器發出要求(HTTP要求),接著傳回JSON格式的資料。

牢記這項事實,如果我們可以撰寫通用的函式來實現實際的要求以及取回資料,一定會相當方便。

沒錯,你猜對了,本節我們要做的正是這件事。

更精確地說,在我們的ViewController類別中將會實作一個可以用來進行簡單的GET要求的函式。

在我們的範例App中,我們使用的是NSURLSession和NSURLSessionDataTask類別。

請留意,這些動作都是非同步的,因此只要一收到任何結果(或錯誤訊息),我們就必須使用主執行緒來加以剖析然後更新UI。

相關步驟詳述如下。

首先,定義此函式: funcperformGetRequest(targetURL:NSURL!,completion:(data:NSData?,HTTPStatusCode:Int,error:NSError?)->Void){ } 如你所見,此函式預期我們將會提供要求的URL網址(NSURL物件)。

第2個參數是閉包(Closure),或稱完成處理常式區塊(CompletionHandlerBlock)。

我在前面提過,實際的資料擷取動作是非同步的,當資料擷取的動作做完時,此完成處理常式便會在主執行緒中被喚起,以便執行剖析的任務。

請留意,此完成處理常式需要3個參數: data:擷取到的資料,格式為JSON。

HTTPStatusCode:HTTP狀態代碼。

我們希望收到的代碼是200(代表一切正常)。

error:發生錯誤時,則會傳回錯誤訊息。

現在,讓我們進入到實作的部分: funcperformGetRequest(targetURL:NSURL!,completion:(data:NSData?,HTTPStatusCode:Int,error:NSError?)->Void){ letrequest=NSMutableURLRequest(URL:targetURL) request.HTTPMethod="GET" letsessionConfiguration=NSURLSessionConfiguration.defaultSessionConfiguration() letsession=NSURLSession(configuration:sessionConfiguration) lettask=session.dataTaskWithRequest(request,completionHandler:{(data:NSData?,response:NSURLResponse?,error:NSError?)->Voidin dispatch_async(dispatch_get_main_queue(),{()->Voidin completion(data:data,HTTPStatusCode:(responseas!NSHTTPURLResponse).statusCode,error:error) }) }) task.resume() } 讓我們來探討一下上述的程式碼。

首先,我們利用參數裡的URL物件來建立NSMutableURLRequest物件。

我們必須設定GET作為偏好的HTTP方法。

這樣就組成了稍後要送出的要求。

接著,初始化NSURLSessionConfiguration物件,並將之傳入作為初始化NSURLSession的參數。

然後利用此session(工作階段)實體來建立資料任務物件,此時我們提供request(要求)作為參數。

在資料任務的完成處理常式中,我們呼叫了dispatch_async()函式,好讓我們能夠在主執行緒中執行我們的完成處理常式。

請留意我們如何從這些參數當中取得回應(NSURLResponse)的HTTP狀態代碼。

最後,我們使用資料任務的resume()函式來開始擷取作業。

現在,就只差資料的剖析了,不過這件事我們稍後才會做。

現在可以確定的是我們不需要擔心資料的擷取了,因為每當我們需要的時候,都可以利用上述函式來達成任務。

取得頻道資訊 我們「要求」YouTubeAPI做的第一件事,就是提供一份頻道清單。

說得更清楚些,我們會要求標題(Title)、簡介(Description),以及Apple、Google和Microsoft的YouTube頻道的縮圖(Thumbnail)資料。

除了這些資料之外,我們還需要一個額外的數值,也就是每個頻道上傳的影片的播放清單ID,好讓我們在稍後能夠向這些影片發出要求。

所有這些任務都會寫在同一個新函式裡。

在此函式中,我們不只會取得單一頻道的詳情,我們的實作會按照此邏輯以遞迴方式取得所有頻道的詳情。

在開始撰寫函式之前,讓我們先來宣告一些將會使用到的變數。

請來到ViewController類別的開頭,並且加入下列這幾行: varapiKey="YOUR_API_KEY" vardesiredChannelsArray=["Apple","Google","Microsoft"] varchannelIndex=0 varchannelsDataArray:Array>=[] 請確定在apiKey變數中設定了你自己的API金鑰數值。

在複製和貼上的時候請務必留意,因為如果有誤的話,稍後送出的要求將會失敗。

除了API金鑰變數之外,我們還需要另外3個變數: desiredChannelsArray:此陣列包含了我們想要取得其資訊的頻道的名稱。

channelIndex:此變數用來指向App應該在稍後即將實作的函式中向哪個頻道發出要求。

channelsDataArray:此陣列將會擁有取得的頻道的資料。

更精確地說,就是我們從JSON回應中擷取出來的資料,每個元素都是一筆頻道字典記錄。

關於我們要使用的YouTubeAPI函式,說明如下。

此函式是channels.list,相關資訊可以在這裡找到。

此要求的URL網址如下: https://www.googleapis.com/youtube/v3/channels 此URL還需要一些參數,以便指定或篩選要傳回的資料。

在介紹這些參數之前,容我先說明一下,針對頻道詳情的取得,總共有3種呼叫API的方式: 指出你是頻道的擁有者。

指定擁有者的使用者名稱。

提供頻道ID(每個頻道有各自的唯一數值)。

在上述這3種方法當中,我們將會使用到後面2種。

針對這2種方法,我們都會使用part作為必要參數,用來指定要求的結果要包含哪些部分。

在本例中,我們針對此參數的設定是snippet,contentDetails(以逗號分隔數值),其中snippet可以為我們提供頻道的詳情(我們會在稍後加以顯示),而contentDetails則會傳回該頻道所擁有的上傳影片的播放清單ID。

在基於使用者名稱來擷取頻道的情況當中,我們還會使用到forUsername參數,並且使用id參數來取得該ID所代表的頻道的資訊。

此刻請容我提醒一點,雖然就目前而言我們並不需要使用該ID來取得頻道詳情,不過稍後在搜尋頻道時還是會需要用到。

此刻我只是先將這項資訊讓你知道而已。

除了要指定的參數之外,我們每次都需要使用API金鑰,否則要求將會失敗。

請牢記上述的說明,現在就讓我們開始來實作新函式吧。

你將會看到,參數的數值指出了發出要求的偏好作法: funcgetChannelDetails(useChannelIDParam:Bool){ varurlString:String! if!useChannelIDParam{ urlString="https://www.googleapis.com/youtube/v3/channels?part=contentDetails,snippet&forUsername=\(desiredChannelsArray[channelIndex])&key=\(apiKey)" } else{ } lettargetURL=NSURL(string:urlString) } 請留意,上述程式碼中的else區塊是空白的。

我們稍後才會補上,此處的示範可以看到根據頻道ID數值所組合出來的URL網址: urlString="https://www.googleapis.com/youtube/v3/channels?part=contentDetails,snippet&id=SOME_ID_VALUE&key=\(apiKey)" 另外也請留意,在組合完最終的URL字串之後,必須將之轉換成NSURL物件。

此物件會緊接著在稍早實作過的performGetRequest(...)函式之後被呼叫到。

在繼續往下進行之前,請容我先為你展示使用username參數從Apple頻道傳回的JSON資料(若要實際測試此API呼叫,可以按這裡,你也可以試試看其他的要求類型): { "kind":"youtube#channelListResponse", "etag":"\"Y3xTLFF3RLtHXX85JBgzzgp2Enw/vTcBxrLzIZMaCeqgOzHFbBhLTUs\"", "pageInfo":{ "totalResults":1, "resultsPerPage":5 }, "items":[ { "kind":"youtube#channel", "etag":"\"Y3xTLFF3RLtHXX85JBgzzgp2Enw/Wd_D4NnOqPTiOtqOKvlNDEMuMxY\"", "id":"UCE_M8A5yxnLfW0KghEeajjw", "snippet":{ "title":"Apple", "description":"AppledesignstheMac,alongwithOSX,iLife,andiWork.ItleadsthedigitalmusicrevolutionwithiPodsandiTunes.ItreinventedthemobilephonewithiPhoneandAppStore.Andit'sdefiningthefutureofmobilemediaandcomputingwithiPad.", "publishedAt":"2005-06-22T05:12:23.000Z", "thumbnails":{ "default":{ "url":"https://yt3.ggpht.com/-KdgJnz1HIdQ/AAAAAAAAAAI/AAAAAAAAAAA/4vVN7slJqj4/s88-c-k-no/photo.jpg" }, "medium":{ "url":"https://yt3.ggpht.com/-KdgJnz1HIdQ/AAAAAAAAAAI/AAAAAAAAAAA/4vVN7slJqj4/s240-c-k-no/photo.jpg" }, "high":{ "url":"https://yt3.ggpht.com/-KdgJnz1HIdQ/AAAAAAAAAAI/AAAAAAAAAAA/4vVN7slJqj4/s240-c-k-no/photo.jpg" } }, "localized":{ "title":"Apple", "description":"AppledesignstheMac,alongwithOSX,iLife,andiWork.ItleadsthedigitalmusicrevolutionwithiPodsandiTunes.ItreinventedthemobilephonewithiPhoneandAppStore.Andit'sdefiningthefutureofmobilemediaandcomputingwithiPad." } }, "contentDetails":{ "relatedPlaylists":{ "uploads":"UUE_M8A5yxnLfW0KghEeajjw" }, "googlePlusUserId":"115967756576401290385" } } ] } 針對上述的結果,我們關心的只有title、description和snippet字典中的defaultthumbnail,而針對uploads數值,我們感興趣的只有contentDetails>relatedPlaylists字典。

根據上述的結果,我們可以「解碼」出每個我們想要的資料的路徑,以便完成後續的實作。

為了簡化起見,我針對上述的每一行結果都在程式碼中加入了註解,方便你理解每個步驟的用途: funcgetChannelDetails(useChannelIDParam:Bool){ ... performGetRequest(targetURL,completion:{(data,HTTPStatusCode,error)->Voidin ifHTTPStatusCode==200&&error==nil{ do{ //將JSON資料轉換成字典 letresultsDict=tryNSJSONSerialization.JSONObjectWithData(data!,options:[])as!Dictionary //從傳回的資料中取得第一筆字典記錄(通常也只會有一筆記錄) letitems:AnyObject!=resultsDict["items"]asAnyObject! letfirstItemDict=(itemsas!Array)[0]as!Dictionary //取得包含所需資料的snippet字典 letsnippetDict=firstItemDict["snippet"]as!Dictionary //建立新的字典,只儲存我們想要知道的數值 vardesiredValuesDict:Dictionary=Dictionary() desiredValuesDict["title"]=snippetDict["title"] desiredValuesDict["description"]=snippetDict["description"] desiredValuesDict["thumbnail"]=((snippetDict["thumbnails"]as!Dictionary)["default"]as!Dictionary)["url"] //儲存頻道的上傳影片的播放清單ID desiredValuesDict["playlistID"]=((firstItemDict["contentDetails"]as!Dictionary)["relatedPlaylists"]as!Dictionary)["uploads"] //將desiredValuesDict字典新增到陣列中 self.channelsDataArray.append(desiredValuesDict) }catch{ print(error) } } }) } 讓我們來看幾個重點。

首先,我們檢查HTTP狀態代碼的數值和錯誤物件,以便確認真的有JSON資料存在。

如果一切正常的話,我們會開始將擷取到的資料(NSData物件)轉換成字典。

接著,我們使用此字典來存取傳回來的所有項目(與頻道資料有關的字典)。

通常在要求頻道的詳情資料時,只會傳回一個項目。

在本例中,此項目是以firstItemDict表示的物件。

這樣就可以很方便地取得snippet字典。

在存取完snippet字典之後,為了保存目的,我們建立了一個新的字典,因為我們需要保留那些數值。

在desiredValuesDict字典中,我們保存了頻道的標題、簡介和縮圖網址。

請留意,我選擇使用預設的縮圖(尺寸最小的那個)。

必須稍微「挖掘」一下,才能夠找到此數值。

在處理完頻道的詳情(包括標題等)之後,我們存取uploads數值以便取得包含所有頻道影片的播放清單ID。

如你所見,此數值也是存放在desiredValuesDict字典當中。

最後,將我們自訂的字典附加到channelsDataArray陣列中,以便稍後能夠顯示和管理這些頻道。

上述的函式尚未實作完成,不過在繼續往下討論之前,容我先介紹即將用來擷取所需數值的結構順序(我們稱之為「路徑」): 要存取snippet字典,使用: 從JSON轉換而來的字典>“items”陣列>第一個項目字典>“snippet”字典 要存取頻道的縮圖網址,使用: 從JSON轉換而來的字典>“items”陣列>第一個項目字典>“snippet”字典>“thumbnails”字典>“default”字典>“url”數值 要存取播放清單ID,使用: 從JSON轉換而來的字典>“items”陣列>第一個項目字典>“contentDetails”字典>“relatedPlaylists”字典>“uploads”數值 如你所見,必須深入到JSON結果底層的字典和陣列當中,不過這一點也不困難,而且Google提供的範例結果也很清楚易懂,對於學習非常有幫助。

現在讓我們開始來實作缺漏的部分吧。

接下來要做的,是在頻道資料新增到channelsDataArray陣列之後,重新載入表格視圖(雖然我們要到下一節才會實作顯示資料的功能),並且繼續擷取下一個頻道(當然是指如果還有的話)。

funcgetChannelDetails(useChannelIDParam:Bool){ ... performGetRequest(targetURL,completion:{(data,HTTPStatusCode,error)->Voidin ifHTTPStatusCode==200&&error==nil{ ... //重新載入表格視圖 self.tblVideos.reloadData() //載入下一個頻道資料(如果有的話) ++self.channelIndex ifself.channelIndexVoidin ifHTTPStatusCode==200&&error==nil{ ... }else{ print("HTTPStatusCode=\(HTTPStatusCode)") print("Errorwhileloadingchanneldetails:\(error)") } }) } 我們的新函式已經準備就緒了。

只需要加以呼叫即可,我們打算在viewDidLoad函式中這麼做: overridefuncviewDidLoad(){ ... getChannelDetails(false) } 透過檢視getChannelDetails(...)函式,最終你將發現,我們做的事情其實一點也不複雜。

我們只是處理傳回的資料,並且將我們感興趣的部分儲存起來。

在接下來的小節中,我們還會實作幾個也很類似的函式;當我們要取得頻道的影片,以及當我們在實作搜尋功能的時候。

不過那一點也不麻煩,因為你已經受過剛剛那陣呼叫YouTubeAPI的「洗禮」了。

顯示頻道 如同前面說明過的,在ViewController類別中的分段元件扮演了很重要的角色,實際上我們就是透過它來決定要在表格視圖中顯示哪種資料。

當選取的是名為Channels(頻道)的第1個索引時,顯示的應該會是對應的頻道詳情,相反地,如果選取的是影片索引,則會在表格視圖中顯示影片的詳情。

此刻就應該考慮這個部分,因為我們現在就必須依照條件在表格視圖中顯示擷取的頻道資料。

好了,首先我們要指定的是表格視圖的資料列數目: functableView(tableView:UITableView,numberOfRowsInSectionsection:Int)->Int{ ifsegDisplayedContent.selectedSegmentIndex==0{ returnchannelsDataArray.count } else{ } return0 } 如你所見,要決定搭配的資料來源非常簡單,只需要根據segDisplayedContent分段元件的selectedSegmentIndex屬性。

我們便可以據此在表格視圖中顯示對應的儲存格資料: functableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{ varcell:UITableViewCell! ifsegDisplayedContent.selectedSegmentIndex==0{ cell=tableView.dequeueReusableCellWithIdentifier("idCellChannel",forIndexPath:indexPath) } else{ cell=tableView.dequeueReusableCellWithIdentifier("idCellVideo",forIndexPath:indexPath) } returncell } 如果檢視InterfaceBuilder中的ViewController場景,將會注意到頻道的原型儲存格包含了3個子視圖,並且分別被指派了特定的tag(標記)數值: 頻道標題是UILabel,其tag數值為10 頻道簡介是UILabel,其tag數值為11 頻道縮圖是UIImageView,其tag數值為12 我們首先要做的是從每個儲存格中將這些子視圖「抽取」出來,以便將擷取到的資料填進去。

示範如下: functableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{ varcell:UITableViewCell! ifsegDisplayedContent.selectedSegmentIndex==0{ cell=tableView.dequeueReusableCellWithIdentifier("idCellChannel",forIndexPath:indexPath) letchannelTitleLabel=cell.viewWithTag(10)as!UILabel letchannelDescriptionLabel=cell.viewWithTag(11)as!UILabel letthumbnailImageView=cell.viewWithTag(12)as!UIImageView } else{ cell=tableView.dequeueReusableCellWithIdentifier("idCellVideo",forIndexPath:indexPath) } returncell } 接著,我們取得每個頻道的詳情,並且指派給這些子視圖: functableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{ varcell:UITableViewCell! ifsegDisplayedContent.selectedSegmentIndex==0{ ... letchannelDetails=channelsDataArray[indexPath.row] channelTitleLabel.text=channelDetails["title"]as?String channelDescriptionLabel.text=channelDetails["description"]as?String thumbnailImageView.image=UIImage(data:NSData(contentsOfURL:NSURL(string:(channelDetails["thumbnail"]as?String)!)!)!) } else{ cell=tableView.dequeueReusableCellWithIdentifier("idCellVideo",forIndexPath:indexPath) } returncell } 現在你可以嘗試首次執行此App。

如果你有遵循剛才所介紹的每個步驟,也設定了自己的API金鑰,那麼應該可以在表格視圖中看到3個頻道。

擷取影片 範例App的第一項功能現在運作正常,接下來繼續實作第二項。

這次我們想要擷取所選頻道的影片,並且條列在表格視圖中(我們將在下一個小節中實作此部分)。

整個程序顯然會在頻道被點擊時才展開,這也正是我們實作的起點。

一如往常,我們需要在類別的開頭宣告新的變數: varvideosArray:Array>=[] 如同你所猜測的,我們會在剖析之後,在videosArray陣列中存放影片的詳情。

請留意,陣列在宣告的同時也一併進行了初始化。

現在,讓我們來處理頻道儲存格的點擊事件: functableView(tableView:UITableView,didSelectRowAtIndexPathindexPath:NSIndexPath){ ifsegDisplayedContent.selectedSegmentIndex==0{ //在本例中,頻道就是要顯示的內容 //所選頻道的影片將被擷取並顯示出來 //將分段元件切換成「Videos」 segDisplayedContent.selectedSegmentIndex=1 //顯示活動指示器 viewWait.hidden=false //移除videosArray陣列中全部舊有的影片詳情 videosArray.removeAll(keepCapacity:false) //針對點擊的頻道,擷取其影片詳情 getVideosForChannelAtIndex(indexPath.row) } else{ } } 上述的註解可以幫助你理解整個流程。

因為我們要顯示的是影片,所以首先將選取的分段元件索引變更為適當的數值。

接著,我們顯示包含活動指示器的viewWait視圖。

不容忽視的一個重要步驟就是要將videosArray陣列中的舊內容清除乾淨。

當然了,此App首次執行時此陣列一定是空白的,但是後續的影片要求就不能夠缺少這行程式碼了。

最後,我們呼叫了getVideosForChannelAtIndex(...)函式,也就是我們在此想要實現的功能。

但是此函式尚不存在,所以我們稍後將會實作。

請留意,我們會將選取的儲存格的索引以參數形式傳遞進去。

這回我們將會使用到YouTubeAPI的playlistItem.list函式,相關的文件可以參考這裡。

我們會在此GET要求中輸入3個參數;其中一個顯然就是API金鑰。

除此之外,我們還需要設定必要的part參數搭配snippet數值,以便取得所需的影片詳情,以及包含這些影片的播放清單的ID。

此參數叫做playlistID。

針對所擷取的頻道,我們已經取得其播放清單的ID數值,接著便可以加以使用。

假使你想要觀看實際使用該要求的範例,可以造訪這個網頁(確定part參數只有設定snippet數值)。

我們將開始實作此新函式,首先我們會取得所選頻道的播放清單ID。

接著我們會指定剛才提過的參數,以便產生正確的URL字串,接著我們會建立NSURL物件,以便發出要求: funcgetVideosForChannelAtIndex(index:Int!){ //從channelsDataArray陣列取得所選頻道的playlistID數值,並用來擷取正確的影片播放清單 letplaylistID=channelsDataArray[index]["playlistID"]as!String //產生要求URL的字串 leturlString="https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=\(playlistID)&key=\(apiKey)" //根據上述字串產生NSURL物件 lettargetURL=NSURL(string:urlString) } 跟你想像的一樣,頻道的播放清單應該會包含許多部影片,不過在本文中,我們並不需要取得所有的影片。

我們只需要取得一部影片就夠了。

不過在本例中,我們並不需要明確地限制結果,因為YouTubeAPI預設一次只會傳回5筆結果。

下列是傳回的JSON結果的範例。

跟之前看過的一樣,這份結果有助於明確地指引我們應該存取哪些數值: { "kind":"youtube#playlistItemListResponse", "etag":"\"Y3xTLFF3RLtHXX85JBgzzgp2Enw/ep-DtNxjJwMQbpCO1Lk3_ggMScU\"", "nextPageToken":"CAUQAA", "pageInfo":{ "totalResults":1636, "resultsPerPage":5 }, "items":[ { "kind":"youtube#playlistItem", "etag":"\"Y3xTLFF3RLtHXX85JBgzzgp2Enw/SYrDBZ2Ywgpf3zgCreEdB4PIf1o\"", "id":"UUZwDRPIG5DD2lxeCjap51NdbKiDO_M62c", "snippet":{ "publishedAt":"2015-06-25T01:50:54.000Z", "channelId":"UCK8sQmJBp8GCxrOtXWBpyEA", "title":"TheGoogleapp:Summer", "description":"\"OKGoogle,whenisSummerover?\"\n\nTalktoGoogletogetanswers,findstuffnearby,andgetthingsdone.TheGoogleapp.AvailableoniOSandAndroid.\n\nDownloadtheapphere:http://www.google.com/search/about/download/", "thumbnails":{ "default":{ "url":"https://i.ytimg.com/vi/BVGKskYZrw8/default.jpg", "width":120, "height":90 }, "medium":{ "url":"https://i.ytimg.com/vi/BVGKskYZrw8/mqdefault.jpg", "width":320, "height":180 }, "high":{ "url":"https://i.ytimg.com/vi/BVGKskYZrw8/hqdefault.jpg", "width":480, "height":360 }, "standard":{ "url":"https://i.ytimg.com/vi/BVGKskYZrw8/sddefault.jpg", "width":640, "height":480 }, "maxres":{ "url":"https://i.ytimg.com/vi/BVGKskYZrw8/maxresdefault.jpg", "width":1280, "height":720 } }, "channelTitle":"Google", "playlistId":"UUK8sQmJBp8GCxrOtXWBpyEA", "position":0, "resourceId":{ "kind":"youtube#video", "videoId":"BVGKskYZrw8" } } }, ...更多項目... ] } 針對影片項目,在傳回的所有數值中,我們只需要保留title、defaultthumbnail和videoID數值。

我們稍後將會使用videoID來播放影片串流。

程式碼如下所示: funcgetVideosForChannelAtIndex(index:Int!){ ... //從Google取得播放清單 performGetRequest(targetURL,completion:{(data,HTTPStatusCode,error)->Voidin ifHTTPStatusCode==200&&error==nil{ do{ //將JSON資料轉換成字典 letresultsDict=tryNSJSONSerialization.JSONObjectWithData(data!,options:[])as!Dictionary //取得所有的播放清單項目(形成items陣列) letitems:Array>=resultsDict["items"]as!Array> //利用迴圈來處理所有的影片項目 forvari=0;i)["snippet"]as!Dictionary //初始化新的字典,並且儲存感興趣的資料 vardesiredPlaylistItemDataDict=Dictionary() desiredPlaylistItemDataDict["title"]=playlistSnippetDict["title"] desiredPlaylistItemDataDict["thumbnail"]=((playlistSnippetDict["thumbnails"]as!Dictionary)["default"]as!Dictionary)["url"] desiredPlaylistItemDataDict["videoID"]=(playlistSnippetDict["resourceId"]as!Dictionary)["videoId"] //將desiredPlaylistItemDataDict字典附加到影片陣列中 self.videosArray.append(desiredPlaylistItemDataDict) //重新載入表格視圖 self.tblVideos.reloadData() } }catch{ print(error) } } }) } 我們用來「抽取」所需數值的邏輯,跟之前剖析頻道時的很類似,所以在此就不贅述了。

背後的概念非常簡單;我們根據的是範例結果,並據此修改我們的程式碼。

請留意,一旦關於影片的資料被新增到陣列之後,我們便必須重新載入表格視圖以便更新UI。

最後,讓我們幫此函式收尾: funcgetVideosForChannelAtIndex(index:Int!){ ... //從Google擷取播放清單 performGetRequest(targetURL,completion:{(data,HTTPStatusCode,error)->Voidin ifHTTPStatusCode==200&&error==nil{ ... } else{ println("HTTPStatusCode=\(HTTPStatusCode)") println("Errorwhileloadingchannelvideos:\(error)") } //隱藏活動指示器 self.viewWait.hidden=true }) } 我們只會顯示HTTP狀態代碼,以及任何遭遇的錯誤(例如沒有可以剖析的資料),並再次隱藏viewWait視圖。

顯示影片 現在要做是將擷取到的影片清單顯示出來,動作同樣非常簡單,就跟我們在顯示頻道時做過的一樣,只要把對應的項目呈現出來就可以了。

那麼,首先我們必須指定表格視圖的資料列數目: functableView(tableView:UITableView,numberOfRowsInSectionsection:Int)->Int{ ifsegDisplayedContent.selectedSegmentIndex==0{ returnchannelsDataArray.count } else{ returnvideosArray.count } } 資料列數目顯然必須與videosArray陣列中存在的影片字典數目相符。

接著,讓我們來處理儲存格的部分,我們將首次使用tag數值來存取儲存格的個別子視圖,然後替它們設定影片的標題和縮圖。

相較之下,頻道只有一個用來顯示影片標題的標籤物件,而沒有簡介標籤。

此外還有用來顯示影片縮圖的影像視圖;同樣地,我們會使用剖析出來的URL數值從網際網路即時取得縮圖。

functableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{ varcell:UITableViewCell! ifsegDisplayedContent.selectedSegmentIndex==0{ cell=tableView.dequeueReusableCellWithIdentifier("idCellChannel",forIndexPath:indexPath) ... } else{ cell=tableView.dequeueReusableCellWithIdentifier("idCellVideo",forIndexPath:indexPath) letvideoTitle=cell.viewWithTag(10)as!UILabel letvideoThumbnail=cell.viewWithTag(11)as!UIImageView letvideoDetails=videosArray[indexPath.row] videoTitle.text=videoDetails["title"]as?String videoThumbnail.image=UIImage(data:NSData(contentsOfURL:NSURL(string:(videoDetails["thumbnail"]as?String)!)!)!) } returncell } 現在範例App已經能夠下載和顯示所選頻道的影片清單,如果有興趣的話,你也可以實際嘗試看看。

不過現在最應該做的,是再幫App完成另外一項功能:可以透過分段元件來切換頻道和影片。

在ViewController類別中,原本就定義了一個名為changeContent(...)的IBAction函式。

每次當你選取不同的區段時都會觸發此函式,其程式碼如下所示: @IBActionfuncchangeContent(sender:AnyObject){ tblVideos.reloadSections(NSIndexSet(index:0),withRowAnimation:UITableViewRowAnimation.Fade) } 上述這行將會以淡入淡出的方式來重新載入表格視圖的資料。

當分段元件的索引改變時,將會自動選取正確的資料來源,並將正確的內容顯示到表格視圖中。

下圖是Apple頻道的影片清單截圖: 搜尋頻道與影片 使用者最常使用到的關鍵功能,就是內容的搜尋,無論是Google的哪一款產品或API,皆致力於實現搜尋的功能。

基於這項事實,我深深感覺到如果不能夠加入利用YouTubeAPI來進行搜尋的功能,我們的範例App似乎就少了點什麼,所以接下來就讓我們來補齊這個部分吧。

一般而言,在YouTube中,使用者可以搜尋影片、頻道和播放清單。

在本文中,我們只會啟用前2項的搜尋功能。

說得更明白一些,我們不會同時搜尋頻道與影片。

搜尋的類型(Type)將視所選的分段元件索引而定。

也就是說,如果選取的是頻道,那麼我們的「搜尋引擎」將會預設傳回頻道結果。

相反地,如果選取的是影片,則會傳回影片結果。

在下載的Starter專案中,我已經將ViewController類別設定為文字欄位(我們的搜尋欄位)的委派,並且定義了textFieldShouldReturn(...)委派函式。

此刻我們將著手實作此函式,並從2件事情開始:隱藏鍵盤,以及根據分段元件的選取索引來決定搜尋類型。

程式碼示範如下: functextFieldShouldReturn(textField:UITextField)->Bool{ textField.resignFirstResponder() viewWait.hidden=false //指定搜尋類型(頻道或影片) vartype="channel" ifsegDisplayedContent.selectedSegmentIndex==1{ type="video" videosArray.removeAll(keepCapacity:false) } returntrue } 我們要使用的YouTubeAPI函式是search.list,相關文件在這裡。

在我們送出的GET要求中,除了API金鑰之外,還有3個額外參數。

第1個必要的參數是part,其值必須指派為snippet(以便取得結果項目的詳情)。

第2個參數是type,我們將指派為前面所指定的頻道或影片數值,以便傳回正確的結果。

第3個、同時也是最重要的參數是q,也就是實際的搜尋字串(換言之,也就是文字欄位的內容)。

缺少了第3個參數,便無法實現搜尋功能。

現在讓我們來準備要求的URL網址。

這回我們增加了一些新的東西,也是我們頭一次實際將要求進行URL編碼。

這麼做是必要的,好讓所有的特殊字元或空格都被正確置換成百分比逸出字元(例如以%20來取代空格字元)。

functextFieldShouldReturn(textField:UITextField)->Bool{ ... //產生要求的URL字串 varurlString="https://www.googleapis.com/youtube/v3/search?part=snippet&q=\(textField.text)&type=\(type)&key=\(apiKey)" urlString=urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)! //根據上述字串建立NSURL物件 lettargetURL=NSURL(string:urlString) returntrue } 下一步便是發出要求。

初始的步驟都非常簡單,亦即取得JSON資料並且轉換成字典。

接著取得所有的搜尋結果項目,最後建立迴圈逐一處理。

functextFieldShouldReturn(textField:UITextField)->Bool{ ... //取得結果 performGetRequest(targetURL,completion:{(data,HTTPStatusCode,error)->Voidin ifHTTPStatusCode==200&&error==nil{ //將JSON資料轉換成字典物件 do{ letresultsDict=tryNSJSONSerialization.JSONObjectWithData(data!,options:[])as!Dictionary //取得所有的搜尋結果項目(items陣列) letitems:Array>=resultsDict["items"]as!Array> //以迴圈迭代處理所有的搜尋結果,並且只保留所需的資料 forvari=0;i //根據搜尋的目標是頻道或影片來收集正確的資料 ifself.segDisplayedContent.selectedSegmentIndex==0{ } else{ } } }catch{ print(error) } } }) returntrue } 上述程式碼並無特別困難之處,所以我們只要先關注頻道結果即可。

在本例中,對於結果的處理非常單純,因為我們只需要取得個別頻道的ID並將之附加到desiredChannelsArray陣列當中。

如果你還有印象的話,我們一開始便在此陣列中加入過Apple、Google和Microsoft等頻道名稱了。

此外,我們建立了getChannelDetails(...)函式,負責存取API並且擷取關於頻道的詳細資料。

在實作的時候,我們曾經提到,要求可以基於頻道擁有者的使用者名稱,或是頻道本身的ID;除此之外,我們也會加上實現此功能的邏輯,並且留待稍後(也就是現在)在使用頻道ID來建立要求URL字串時再做說明。

所以現在就讓我們將getChannelDetails(...)函式缺漏的部分補齊吧。

只需要加入下列的else分支: funcgetChannelDetails(useChannelIDParam:Bool){ varurlString:String! if!useChannelIDParam{ ... } else{ urlString="https://www.googleapis.com/youtube/v3/channels?part=contentDetails,snippet&id=\(desiredChannelsArray[channelIndex])&key=\(apiKey)" } ... } 現在讓我們來探討搜尋結果的剖析,並且在desiredChannelsArray陣列中加入擷取到的頻道ID。

請留意,除了這些動作之外,在for迴圈之後立即呼叫了getChannelDetails(...)函式,並且傳入true數值作為參數,指示必須根據頻道ID來產生該要求: functextFieldShouldReturn(textField:UITextField)->Bool{ ... //取得結果 performGetRequest(targetURL,completion:{(data,HTTPStatusCode,error)->Voidin ifHTTPStatusCode==200&&error==nil{ ... //以迴圈迭代處理所有的搜尋結果,並且只保留所需的資料 forvari=0;i //根據搜尋的目標是頻道或影片來收集正確的資料 ifself.segDisplayedContent.selectedSegmentIndex==0{ //記住頻道ID self.desiredChannelsArray.append(snippetDict["channelId"]as!String) } else{ } } //呼叫getChannelDetails(...)函式以便擷取頻道 ifself.segDisplayedContent.selectedSegmentIndex==0{ self.getChannelDetails(true) } } }) returntrue } 在完成上述的步驟之後,便準備好可以搜尋頻道了。

搜尋所傳回的任何新頻道,都將被新增到表格視圖中既有的項目之後。

請留意,在預設的情況下,每次要求只會傳回5筆結果,這樣對我們而言就夠了,我們也省得設定任何限制或篩選器。

現在讓我們來處理影片搜尋的結果。

在本例中,我們將會建立新的字典,用來存放標題、縮圖URL和影片的ID。

接著,此字典會被附加到videosArray陣列當中,並且重新整理表格視圖: functextFieldShouldReturn(textField:UITextField)->Bool{ ... //取得結果 performGetRequest(targetURL,completion:{(data,HTTPStatusCode,error)->Voidin ifHTTPStatusCode==200&&error==nil{ ... //以迴圈迭代處理所有的搜尋結果,並且只保留所需的資料 forvari=0;i //根據搜尋的目標是頻道或影片來收集正確的資料 ifself.segDisplayedContent.selectedSegmentIndex==0{ ... } else{ //建立新的字典,用來儲存影片詳情 varvideoDetailsDict=Dictionary() videoDetailsDict["title"]=snippetDict["title"] videoDetailsDict["thumbnail"]=((snippetDict["thumbnails"]as!Dictionary)["default"]as!Dictionary)["url"] videoDetailsDict["videoID"]=(items[i]["id"]as!Dictionary)["videoId"] //將desiredPlaylistItemDataDict字典附加到影片陣列當中 self.videosArray.append(videoDetailsDict) //重新載入表格視圖 self.tblVideos.reloadData() } } ... } }) returntrue } 現在,我們只差還沒有加入錯誤處理以及隱藏viewWait視圖的程式碼而已了。

functextFieldShouldReturn(textField:UITextField)->Bool{ ... //取得結果 performGetRequest(targetURL,completion:{(data,HTTPStatusCode,error)->Voidin ifHTTPStatusCode==200&&error==nil{ ... } else{ println("HTTPStatusCode=\(HTTPStatusCode)") println("Errorwhileloadingchannelvideos:\(error)") } //隱藏活動指示器 self.viewWait.hidden=true }) returntrue } 讓我們再度嘗試執行此App,這回要測試的是影片搜尋的功能。

播放影片 在iOSApp中播放YouTube影片非常容易,因為Google提供了能夠用來嵌入播放器並且接收影片內容串流的輔助(Helper)程式庫。

此輔助類別會建立內嵌iframe的特殊網頁視圖,使得整合變得非常容易。

你可以在這裡找到對應的文件。

針對iOS,此程式庫是以Objective-C撰寫而成,所以要在SwiftApp中使用的話,必須建立所需的表頭橋接檔案。

為了方便說明並且節省時間起見,我已經在下載的Starter專案建立好此檔案,同時也加入了程式庫的必要檔案。

說得更清楚一些,針對專案中的影片輔助程式庫,你會看到有下列這些東西: 名為Assets的群組 YTPlayerView.h檔案 YTPlayerView.m檔案 除此之外,在InterfaceBuilder的PlayerViewController場景中,我加入了一個視圖物件作為子視圖。

此視圖的類別已設定為YTPlayerView,至此所有的初始步驟便大功告成。

現在,我們只需要撰寫少許的程式碼,讓播放器能夠顯示YouTube影片的串流即可。

假使你還記得的話,在擷取和剖析影片資料期間,我們只儲存了特定的數值。

其中之一便是videoID,而現在正是拿來使用的時刻。

我們會將此數值提供給YTPlayerView,然後我們的專案也就差不多完成了。

就讓我們從在ViewController類別中點擊影片儲存格開始。

當此動作發生時,將會完成2項任務:首先,從videosArray陣列中取得正確的影片ID,接著載入PlayerViewController視圖控制器。

與此同時,其Segue也蓄勢待發,我們會將影片ID傳遞給下一個視圖控制器。

再度回到動作函式,首先來到ViewController類別的開頭,並且宣告如下的變數(最後一個了): varselectedVideoIndex:Int! 接著,更新下列的表格視圖委派函式: functableView(tableView:UITableView,didSelectRowAtIndexPathindexPath:NSIndexPath){ ifsegDisplayedContent.selectedSegmentIndex==0{ ... } else{ selectedVideoIndex=indexPath.row performSegueWithIdentifier("idSeguePlayer",sender:self) } } 看吧,一點都不難,我們只需要記住點擊資料列的索引,然後執行Segue。

現在讓我們來檢視Segue的準備程式碼,實際上我們是存取影片ID並將之傳遞給PlayerViewController類別。

overridefuncprepareForSegue(segue:UIStoryboardSegue,sender:AnyObject?){ ifsegue.identifier=="idSeguePlayer"{ letplayerViewController=segue.destinationViewControlleras!PlayerViewController playerViewController.videoID=videosArray[selectedVideoIndex]["videoID"]as!String } } 如果Xcode顯示錯誤的話請勿驚慌。

那是因為videoID屬性尚未存在於PlayerViewController類別當中,不過我們現在就會立刻來修正此問題。

開啟PlayerViewController類別,並且加入下列的宣告: varvideoID:String! 最後,在viewDidLoad中呼叫YTPlayerView類別的函式: overridefuncviewDidLoad(){ super.viewDidLoad() playerView.loadWithVideoId(videoID) } 我們準備好了。

現在可以執行此App,選擇並觀賞任何你喜歡的影片。

你應該也注意到了,播放器允許全螢幕,此外也可以執行一些其他動作。

結語 要使用YouTubeAPI一點也不難,只需要找到Google所提供的正確資源和文件。

針對每個要求,都有詳細的文件和範例能夠引導你完成實作,讓你能夠按照最正確的方式來取得和處理資料。

如同我在引言當中所說的,在本文中我們所發出的要求只會需要用到簡單的API金鑰,無須執行使用者授權程序。

但是有很多情況是需要使用者在執行特定動作(例如上傳影片)之前先完成登入。

如你所知,我們無法只用單篇文章來涵蓋所有這些主題,不過你在本文的所學已經足以讓你開始使用YouTubeAPI了。

你當然也可以運用類似於前面幾個小節所介紹的方法來使用其他的API,所以請你挑一組喜歡的API盡情嘗試吧! 供你參考,請從Github下載本文的完整Xcode專案。

譯者簡介:陳佳新–奇步應用共同創辦人,開發自有App和網站之外,也承包各式案件。

譯有多本電腦書籍,包括O'Reilly出版的iOS、Android、Agile和GoogleCloud等主題,也在報紙上寫過小說。

現與妻兒居住在故鄉彰化。

歡迎造訪https://chibuapp.com,來信請寄到[email protected]。

原文:BuildingaVideoSearchAppwithYouTubeAPI 作者 GabrielTheodoropoulos 資深軟體開發員,從事相關工作超過二十年,專門在不同的平台和各種程式語言去解決軟體開發問題。

自2010年中,Gabriel專注在iOS程式的開發,利用教程與世界上每個角落的人分享知識。

可以在Google+或推特關注Gabriel。

查看全部文章 評論 載入評論 精選文章 在SwiftUI使用Markdown輕鬆格式化文本 30Mar2022 透過SwiftPlaygrounds4直接在iPad構建一個文字轉語音App 12Jan2022 在SwiftUI中設置漸變動畫(animatedgradient)的3個方法 29Sep2021 最新文章 利用ConfettiSwiftUI 簡單打造屬於自己的彩色紙屑動畫 01Jun2022 有了SwiftUI 在iOS進行網頁抓取更輕鬆! 25May2022 一步步編寫模組化程式碼 在SwiftUI套用SwiftPackage 18May2022 更多來自AppCoda中文版 Swift程式語言 Swift5.7新功能縮短Unwrap的程式碼到底是好是壞? 在這篇文章中,我會介紹一個在Swift5.7中為UnwrapOptional而設的小「語法糖(syntacticsugar)」。

這個改變並不是這篇文章的重點,更重要的是這個題目的討論。

AppCoda編輯團隊 May4,2022 4分鐘閱讀時間 Blockchain Solidity簡介:讓Swift開發者快速掌握Solidity的語言結構和函式 Solidity是一種物件導向語言,用來撰寫適用於區塊鏈的智能合約,比如說Ethereum。

Solidity的語法與Javascript類似,但語義就比較接近C++。

在這篇文章中,Kristaps會帶大家從Swift開發者的角度,深入探究Solidity的語言結構和功能。

AppCoda編輯團隊 Jan19,2022 4分鐘閱讀時間 Swift程式語言 Swift5.5的新語法和機制讓我們用最直觀的方式撰寫非同步程式 Swift5.5導入了新的非同步任務機制,包括了async/await、Actor、TaskGroup等等好用的工具,還有針對既有的SwiftUI、CoreData、跟許多Foundation下的API所做的async/await封裝。

有了這些新的語法跟機制,我們就可以用非常直觀的方式來撰寫非同步的程式,提升程式碼的可讀性,同時也大幅降低出錯的機率。

HuangShihTing Dec22,2021 29分鐘閱讀時間 很好!你已成功註冊。

歡迎回來!你已成功登入。

你已成功訂閱AppCoda中文版電子報。

你的連結已失效。

成功!請檢查你的電子郵件以獲取用於登入的連結。

好!你的付費資料已更新。

你的付費方式並未更新。



請為這篇文章評分?