使用YouTube API 打造影音搜尋App
文章推薦指數: 80 %
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.channelIndex
只需要加以呼叫即可,我們打算在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中文版電子報。
你的連結已失效。
成功!請檢查你的電子郵件以獲取用於登入的連結。
好!你的付費資料已更新。
你的付費方式並未更新。
延伸文章資訊
- 1在Android應用程式使用YouTube API來嵌入視訊
在Android版YouTube播放器API使您可以將視訊播放功能到你的Android應用程式。該API允許您載入和播放YouTube視訊(和播放列表),並自定義和控制視訊播放 ...
- 2101 使用YouTube Data API 抓取有趣的Youtuber 影片& MV
YouTube 上有那麼多精彩的影片,有沒有可能我們自己寫個App,串接它豐富的資料呢? 當然可以,透過YouTube Data API,我們不只能從YouTube 回傳的JSON 取得影片的相...
- 3third-party元件(Youtube API) Youtube API 簡易使用 - Blog for ...
3、回到Android Studio內,右上方File >> Project Structure... 按下去之後,找到你自己專案,案ok. 我們就可以使用Youtube ...
- 4Day 21 --YouTube API(2) - iT 邦幫忙::一起幫忙解決難題
工具和播放器API: 因為YouTube應用程式可以在兩個系統裡使用,兩個系統為Android和IOS。 Android— 播放器API 播放器API顯示應用播放器API參考YouTube ...
- 5YouTube 行動應用程式開發資源
圖1:YouTube Direct Android 應用程式. YouTube 資料API 也為影片分享作業提供豐富功能。YouTube API 跟其他Google 資料API 一樣,採用以RE...