- 投稿日:2022-01-25T23:39:56+09:00
iOSエンジニアの秘伝の巻物作り開始 = 初投稿(自己紹介)
初投稿ということでまず自己紹介させて頂きます。 現在関西の自社開発企業でiOSエンジニア(一年目)をしている20代前半の男です。 趣味 :ダウンタウンを見ること、筋トレ、個人開発、ビートボックス、キャンプなど 僕自身以前は全くの異業種でして、ホテルマンとして働きながらiOSエンジニアになるために1年以上勉強していました。 僕は正直エンジニアになるのに普通の人よりすごく時間がかかった方だと思います。ですが毎日アホみたいにプログラムを書いたおかげでなんとか第一志望の企業に就職できました。 今後はまずiOSエンジニアを極めてiOSアプリならなんでも開発できるエンジニアになるためにiOSエンジニア専用の秘伝の巻物(要するにリファレンスです)を作っていこうと思います。 まずiOSアプリ、その後は両OSのアプリもできるようにしていきます。 まだまだ未熟者ですが、数年後にはバケモンエンジニアになるので先輩方よろしくお願いします!
- 投稿日:2022-01-25T17:36:24+09:00
iOSアプリにApple Music機能を追加(音楽の検索と再生) (MusicKit)
これまでは、楽曲を検索するにはApple Music APIを使用する必要がありました。新たに発表されたiOSのMusicKitフレームワークによって、楽曲を検索したり、アルバム内の曲をすべてリストアップしたり、楽曲を再生したりするためのリクエストをより簡単に行えるようになりました。 このフレームワークは、Androidシステムでも利用可能です。この記事では、これについては触れません。 MusicKitの新たなフレームワークには、新しくなったSwiftの並行処理も採用されています。データのリクエストに対するレスポンスを待つには、awaitコマンドを使用することができます。 この記事では、Apple Musicのカタログを検索し、音楽を再生するアプリの構築について説明します。 使用事例 Appleによると、可能となる使用事例にはゲーム内のBGM、トレーニング中の音楽、ソーシャルメディアなどが含まれています(しかしながら、ご使用の際にはライセンスや著作権などに関して、常にご確認をいただくようお願いします)。 最初にEntitlementを追加する 最初に https://developer.apple.com/account/resources/identifiers/list に移動し、アプリケーションを開くことを選択します。クリックしてApp Servicesタブを開き、MusicKitのトグルをオンにします。 リスト内にアプリケーションが表示されない場合は、XcodeのSigning & Capabilitiesビューで任意の機能を追加できます。これによりXcodeが自動的でに署名プロファイルを生成します。 トークンを生成する必要はありません。それはMusicKitによって自動的に生成されます。 Apple Music ライブラリ へのアクセス申請 ユーザーの視聴履歴や個人ライブラリにアクセスしたい場合は、許可を申請する必要があります。 まず、Info.plistファイル内に使用説明文字列NSAppleMusicUsageDescriptionを追加します。 次に、ユーザーの認可を要求します Button("Request authorization") { Task { await MusicAuthorization.request() } } 次のコードで、ユーザーがアクセスを許可しているか確認します。 Button("Reload authorization status") { self.currentAuthStatus = MusicAuthorization.currentStatus } ユーザーのApple Musicサブスク契約状況を確認 画面が表示されたら、ユーザーのApple Musicサブスク契約状況の問い合わせができます。 まず、ユーザーのサブスク契約状況を格納する変数musicSubscriptionを作成します。表示開始時には、これはnilとなっています。 @State private var musicSubscription: MusicSubscription? ビューが表示されたら、forループでawaitを利用して読み込みを続け、ユーザーの登録ステータスにおける変化を待ちます。 .task { for await subscription in MusicSubscription.subscriptionUpdates { self.musicSubscription = subscription } } 登録に関する更新情報があれば、ユーザーの登録ステータスを読み込むことができます。 ユーザーがApple Musicに登録できるかどうかを確認 musicSubscription.canBecomeSubscriberを利用して、ユーザーがApple Musicの登録者になれるかどうかを確認します。 この値がtrueの場合、ユーザーはApple Musicにまだ登録しておらず、登録可能であることを意味します。 MusicSubscriptionOfferオブジェクトを利用すれば、ユーザーにApple Musicへの登録を勧めることができます。SwiftUI内では、musicSubscriptionOfferビューモディファイアが使用可能です。 @State private var showAppleMusicSubscriptionOfferView: Bool = false if musicSubscription.canBecomeSubscriber { Button("Promopt the user to subscribe to Apple Music") { self.showAppleMusicSubscriptionOfferView = true } .musicSubscriptionOffer(isPresented: $showAppleMusicSubscriptionOfferView) } 変数musicSubscription.canBecomeSubscriberがfalseの場合は以下のいずれかが当てはまります: ユーザーのデバイスが制限されているか、もしくはApple Musicにサブスクライブできません。 ユーザーが既にApple Musicにサブスクライブしています。どちらに当てはまるのか確認したい場合は、別の変数を使ってユーザーがApple Musicカタログにアクセスできるかチェックする必要があります。 ユーザーがApple Musicのカタログにアクセスできるかどうかの確認 変数musicSubscription.canPlayCatalogContentを使用します。この変数がtrueであれば、ユーザーはApple Music内のコンテンツを再生できることを意味し、ほとんどの場合、ユーザーはApple Musicに加入していることになります。 ユーザーのiCloudミュージックライブラリへのアクセス確認 変数musicSubscription.hasCloudLibraryEnabledを使って、ユーザーがクラウドミュージックライブラリを有効にしているかどうかを確認することができます。 音楽を検索する Apple Musicの音楽カタログを検索するにはMusicCatalogSearchRequestオブジェクトを使用します。 public struct MusicCatalogSearchRequest { public init(term: String, types: [MusicCatalogSearchable.Type]) } term変数には検索語を指定してください。types変数には検索するデータの種類を指定します(例:曲、アルバム、プレイリストなど)。検索する種類は複数定義することができます。 たとえば、次のコードを使用すると検索語がBGMの曲を検索することができます。 let request = MusicCatalogSearchRequest(term: "BGM", types: [Song.self]) 次に、let response = try await request.response()を利用して、検索リクエストを実行します。awaitを使用しているので、検索結果が返ってくるまでコードはこの行で停止します。 Button("Perform search") { Task { do { let request = MusicCatalogSearchRequest(term: searchTerm, types: [Song.self]) self.isPerformingSearch = true let response = try await request.response() self.isPerformingSearch = false self.searchResultSongs = response.songs } catch { fatalError("Error") } } } この例では、応答をリクエストする直前に、isPerformingSearchをtrueに設定しています。応答の準備が整うと、isPerformingSearchはfalseに、searchResultSongsは検索によって取得された曲に設定されます。 特定のアイテムをリクエストする アイテム(曲またはアルバム)をリクエストするためには、そのIDを使用します。 let request = MusicCatalogResourceRequest<Album>(matching: \.id, equalTo: "1594311281") let response = try await request.response() let items: MusicItemCollection<Album> = response.items オブジェクトのさらなる情報を読み込む MusicKitは、すべての情報で応答するわけではありません。例えば、アルバムを検索する場合、アルバム内の曲の情報は含まれません。 MusicKitから受け取ったオブジェクトは、with関数を呼び出すことで更新できます。 例えば、アルバムオブジェクトがあり、曲を含むアルバムを取得したい場合、関数album.with([.artists, .tracks, .relationAlbums])を呼び出します。 このSwiftUIビューの例では、検索結果からアルバム変数を入力し、曲情報でオブジェクトを更新します。 struct AlbumDetailsView: View { @State private var updatedAlbumObject: Album? var album: Album var body: some View { AnyView() .task { self.updatedAlbumObject = try? await album.with([.tracks]) } } } self.updatedAlbumObject.tracks変数を使用して、このアルバム内のすべての曲を取得します。 if let tracks = self.updatedAlbumObject?.tracks { ForEach(tracks) { track in switch track { case .song(let songItem): SongInfoView(songItem: songItem) case .musicVideo(_): EmptyView() @unknown default: EmptyView() } } } else { ProgressView("Loading tracks...") } 音楽アイテムのアートワークを入手 .artworkプロパティを呼び出すと、音楽アイテム(曲やアルバムなど)のアートワークを取得できます。 if let artwork = songItem.artwork { ArtworkImage(artwork, height: 100) } SwiftUI内では、ArtworkImage変数を直接使用して、アートワーク画像を表示できます。 音楽を再生 システムプレーヤーまたはアプリプレーヤーを使用して音楽を再生できます。1曲または1枚のアルバムを再生できます。 システムプレーヤーを使用すると、アプリを閉じても音楽が再生され続けます。 システムプレーヤーの使用 Button("Play using iOS system player") { Task { SystemMusicPlayer.shared.queue = .init(for: [album]) do { try await SystemMusicPlayer.shared.play() } catch { print(error.localizedDescription) } } } album変数は、MusicItemプロトコルに準拠した他のオブジェクトで変えることができます。 アプリ内プレーヤーの使用 アプリ内プレーヤーを使えば、より多くのカスタマイズを行うことができます。 Button("Play using in-app player") { Task { ApplicationMusicPlayer.shared.queue = .init(for: [album]) do { try await ApplicationMusicPlayer.shared.play() } catch { print(error.localizedDescription) } } } 他の種類のAPIリクエストの実行 その他のApple Music用APIの中には、MusicKit内では利用できないものがある場合があります。そのような場合は、MusicDataRequestを使ってリクエストを行ってください。レスポンスは手動で解析する必要があります。 let url = URL(string: "https://api.music.apple.com/v1/me/recent/played")! let request = MusicDataRequest(urlRequest: URLRequest(url: url)) let response = try await request.response() 例えば、最近再生した音楽を取得するには、このウェブページを参照してください: MusicKit - Access user’s recently … | Apple Developer Forums Twitter @MszPro カテゴリー別に整理された、自分の公開記事とニュースレターを表示します。iOSデバイスのカメラアプリを使用して、このコードをスキャンするだけです。 または、Webで表示
- 投稿日:2022-01-25T01:07:30+09:00
【Swift】知っておきたいKeyPathの基本と使い方
この投稿は何? SwiftにおけるKeyPathについて、基本から解説します。 実行環境 macOS 12.1 Xcode 13.2.1 Swift 5.5 KeyPathとは 要は、「あるデータ型に定義されたプロパティまでの参照(パス)」です。 Swift3時代のString KeyPath KeyPathは、Objective-Cの時代から利用されていました。 Swiftでもそれを受け継いでおり、クラスに`objcMembers'属性をマークすることで利用できました。 参照型データの場合 @objcMembers class Kid: NSObject { dynamic var nickname: String = "" dynamic var age: Double = 0.0 dynamic var bestFriend: Kid? = nil dynamic var friends: [Kid] = [] init(nickname: String, age: Double) { self.nickname = nickname self.age = age } } // ベン君 let ben = Kid(nickname: "Benji", age: 5.5) // Kid型のnicknameプロパティへのキーパスを作成 let kidsNameKeyPath = #keyPath(Kid.nickname) // String // キーパスを使って取得した「ベン君のニックネーム」はAny型になってしまう let name = ben.value(forKeyPath: kidsNameKeyPath) // value(forKeyPath: String) -> Any print(name as! String) // Benji この方法で取得したキーパスは、単純なString型として保持されます。 そして、String KeyPathを利用して取得したプロパティ値は、型情報が失われてしまう性質がありました。 Swiftに最適化されたKeyPath Swift4では、全く新しい方法でKeyPathを利用できるようになりました。 値型データの場合 struct BirthdayParty { let celebrant: Kid var theme: String var attending: [Kid] } var bensParty = BirthdayParty(celebrant: ben, theme: "Construction", attending: []) let birthdayKid = bensParty[keyPath: \BirthdayParty.celebrant] bensParty[keyPath: \BirthdayParty.theme] = "Ninja" let birthdayKidsAgeKeyPath = \BirthdayParty.celebrant.age // キーパスを作成 let birthdayboysAge = bensParty[keyPath: birthdayKidsAgeKeyPath] // 5.5 値型データのKeyPathでは型情報が維持され、よりシンプルに記述されます。 基点型が推論可能な場合、記述を省略できます。 benはKid型インスタンスなので、基点型はKidであることが推論可能です。 従って、Kidを省略して以下のように記述できます。 let age = ben[KeyPath: \.age] 同様に、bensPartyはBirthdayParty型インスタンスなので、以下のコードも略記できます。 bensParty[keyPath: \BirthdayParty.theme] = "Ninja" bensParty[keyPath: \.theme] = "Ninja" // 型推論による基点型の省略記法 この方法で取得したキーパスは、ReferenceWritableKeyPath型として保持されます。 そして、取得した値の型情報も維持されます。 クラスのキーパス クラスでも同じようにして、キーパスを利用できます。 class C { var p = "" } let c = C() let cpKeyPaty = \C.p // ReferenceWritableKeyPath<C, p> let cp = c[keyPath: \C.p] // String