Hi, Ở phần 1 mình đã giới thiệu đến các bạn tổng quan về CP (CarPlay). Phần này mình sẽ chia sẻ cách để xây dựng một ứng dụng hỗ trợ CP, cụ thể ở đây là ứng dụng hỗ trợ một trong 8 CP types mà apple cung cấp, còn type Automaker thì mình chia sẻ mở bài tiếp theo.
Bắt đầu nào!!!
Tạo Project
Ứng dụng CP cũng là một ứng dụng bình thường, các bạn vẫn sử dụng xCode và tạo project như bình thường. Phần code của phone và CP sẽ là hai phần riêng biệt. Vì trên iPhone quá quen thuộc với các bạn rồi nên ở hướng dẫn này mình chỉ thực hiện hiển thị 1 dòng chữ ở giữa màn hình trên iPhone và tập chung vào việc hiển thị trên CP với các template
Tạo entitlement file
Như mình đề cập ở phần 1, với 8 type bình thường của Apple cung cấp thì đi kèm với nó là các entitlement key, thì key này sử dụng ở đâu?. Câu trả lời là key này sử dụng để config trong entitlement file.
Select file -> New file -> choose Property List
Điền tên file và chọn create
Ở đây mình để tên file là CPTemplateSeminar.entitlements
Sau khi tạo entitlement file thì chúng ta cần nhúng đường path dẫn đến entitlement file vào Code Signing Entitlements bằng cách chọn Project Setting -> Build Setting -> Code Signing Entitlements và set giá trị là path dẫn đến entitlement file
Tip: Ngoài cách tạo file và nhúng đường path như ở trên, các bạn có thể enable bất kì một Capabilities nào đó trong xCode để xCode tự động tạo ra entitlement file và tự động nhúng đường path vào Code Signing Entitlements
Sau khi setting Code Signing Entitlements xong, Chúng ta đến bước add entitlement key vào file vừa tạo.
Ở đây mình sử dụng key com.apple.developer.carplay-audio. (Nếu các bạn chưa biết key này là gì và lấy ở đâu thì các bạn xem lại phần 1 của mình nhé!)
Implement Template
Tạo một class kế thừa CPTemplateApplicationSceneDelegate, delegate này có các methods như didConnect, didDisconnectInterfaceController …
import CarPlay
class CPSceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate {
var interfaceController: CPInterfaceController?
// CarPlay connected
func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene,
didConnect interfaceController: CPInterfaceController) {
self.interfaceController = interfaceController
}
// CarPlay disconnected
func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, didDisconnectInterfaceController interfaceController: CPInterfaceController) {
self.interfaceController = nil
}
}
Setting info.plist
Tất cả các CP app đều phải khai báo CP scene để dử dụng CP Framework. Để khai báo CP scene thì chúng ta khai báo ở trong info.plist
Khai báo thêm key CPTemplateApplicationSceneSessionRoleApplication theo như hướng dẫn dưới đây:
Mình set UISceneConfigurationName là CarPlay, UISceneDelegateClassName là class mà mình đã tạo ở bên trên
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>CPTemplateApplicationSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>CarPlay</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).CPSceneDelegate</string>
</dict>
</array>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>iPhone</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
</dict>
</plist>
Setup Root Template
Như một ứng dụng thông thường thì CP app cũng cần setup root view. Ở hướng dẫn này mình xây dựng CP app với root template là 1 tab bar bao gồm tab Home và tab Setting. Tab Home và Setting mình sẽ hiển thị list đơn giản
// CarPlay connected
func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene,
didConnect interfaceController: CPInterfaceController) {
self.interfaceController = interfaceController
interfaceController.setRootTemplate(createTabTemplate(), animated: true, completion: nil)
}
import Foundation
import CarPlay
// MARK: - Tab Template
extension CPSceneDelegate {
func createTabTemplate() -> CPTabBarTemplate {
var tabs: [CPTemplate] = []
let homeItem = CPListItem(text: "Home", detailText: "")
let homeSection = CPListSection(items: [homeItem])
let homeTemplate = CPListTemplate(title: "Home", sections: [homeSection])
homeTemplate.tabImage = UIImage(systemName: "house.fill")
let settingItem = CPListItem(text: "Setting", detailText: "")
let settingSection = CPListSection(items: [settingItem])
let settingTemplate = CPListTemplate(title: "Home", sections: [settingSection])
settingTemplate.tabImage = UIImage(systemName: "gearshape.fill")
tabs.append(homeTemplate)
tabs.append(settingTemplate)
let tabBar = CPTabBarTemplate(templates: tabs)
return tabBar
}
}
Mình tạo một tab bar bằng cách sử dụng CPTabBarTemplate, và mình set tab bar này thành root template khi iPhone và màn hình trên ô tô được kết nối.
Đến step này là đã hoàn thành việc coding một ứng dụng hỗ trợ CP rồi, Chúng ta có thể trải nghiệm ứng dụng bằng cách build lên simulator và sau đó mở cửa sổ xCode Simulator để xem thành quả. Tuy nhiên mình sẽ thêm phần action bấm vào item trên màn home và thực hiện di chuyển đến màn hình phát nhạc.
Tạo Now Playing template
import Foundation
import CarPlay
// MARK: - Playing now Template
extension CPSceneDelegate {
func createNowPlaying() -> CPNowPlayingTemplate {
let playing = CPNowPlayingTemplate.shared
playing.add(self)
playing.isUpNextButtonEnabled = true
playing.isAlbumArtistButtonEnabled = true
return playing
}
}
extension CPSceneDelegate: CPNowPlayingTemplateObserver {
func nowPlayingTemplateUpNextButtonTapped(_ nowPlayingTemplate: CPNowPlayingTemplate) {
}
func nowPlayingTemplateAlbumArtistButtonTapped(_ nowPlayingTemplate: CPNowPlayingTemplate) {
}
}
Thực hiện di chuyển khi bấm vào item trên màn hình Home. Mình sẽ thực hiện pushTemplate đến màn hình Now Playing được tạo bên trên, Các bạn có thể thực hiện push hoặc present tuỳ thích, tuy nhiên ở phần 1 mình cũng có warning là không phải template nào cũng có thể push và present được. Các bạn cứ thử thực hiện để trải nghiệm nhé!
import Foundation
import CarPlay
// MARK: - Tab Template
extension CPSceneDelegate {
func createTabTemplate() -> CPTabBarTemplate {
var tabs: [CPTemplate] = []
let homeItem = CPListItem(text: "Home", detailText: "")
homeItem.handler = { item, completion in
self.interfaceController?.pushTemplate(self.createNowPlaying(), animated: true, completion: nil)
completion()
}
let homeSection = CPListSection(items: [homeItem])
let homeTemplate = CPListTemplate(title: "Home", sections: [homeSection])
homeTemplate.tabImage = UIImage(systemName: "house.fill")
let settingItem = CPListItem(text: "Setting", detailText: "")
let settingSection = CPListSection(items: [settingItem])
let settingTemplate = CPListTemplate(title: "Home", sections: [settingSection])
settingTemplate.tabImage = UIImage(systemName: "gearshape.fill")
tabs.append(homeTemplate)
tabs.append(settingTemplate)
let tabBar = CPTabBarTemplate(templates: tabs)
return tabBar
}
}
Ở đây mình chỉ sử dụng các template mà Audio type hỗ trợ, các bạn cũng có thể sử dụng các template mà Audio không hỗ trợ để trải nghiệm nhé, ví dụ như sử dụng Action Sheet template. (Lưu ý là app sẽ crash nhé!!!)
Ngoài ra các bạn cũng có thể xây dựng ứng dụng của mình với các type còn lại để trải nghiệm nhé!
Kết quả
Sau khi build trên xCode Simulator mình được kết quả như sau:
Trên đây là phần mình chia sẻ về cách tạo một ứng dụng hỗ trợ CP sử dụng type là template được cung cấp bởi Apple. Bài tiếp theo mình sẽ chia sẻ về cách tạo một ứng dụng hỗ tợ CP với type Automaker
Mình hi vọng bài viết có thể giúp ích được cho các bạn. Chúc các bạn thành công!