Open19

Swiftの勉強

hzuikahzuika

Swiftについて

https://developer.apple.com/jp/swift/

Swiftのリソース(開発ツールやドキュメントへのリンク)

https://developer.apple.com/jp/swift/resources/

標準ライブラリ

Documentationの左のアイコンを開き、Filterを使って関数を検索できる

Swift Standard Library | Apple Developer Documentation

https://developer.apple.com/documentation/swift/swift-standard-library

Swiftの特徴

Playground

Objective-Cとの相互運用

オープンソース

LLVMコンパイラ

メモリの自動管理

Optional型

https://docs.swift.org/swift-book/index.html

グローバルスコープで書かれたコードはプログラムのエントリポイントとして使用される

セミコロンは必要ない

letで定数、varで変数を定義する

型推論

型変換は明示的に行う

\()で文字列の中に変数を埋め込むことができる

if, switch, for, while などがある

class

protocol

エラー処理のためのdo-catchとtry

ジェネリック型

async, await

https://docs.swift.org/swift-book/GuidedTour/GuidedTour.html

SwiftUI | Apple Developer Documentation

hzuikahzuika

iOSアプリ開発チュートリアル

iOS App Dev Tutorials | Apple Developer Documentation

2種類のフレームワークがある?

SwiftUI

UIKit

Using stacks to arrange views — iOS App Dev Tutorials | Apple Developer Documentation

プロジェクト作成

Choose a template for your new project: > iOS > Appliation > App

Product Name > 任意の名前

Interface > SwiftUI

Language > Swift

プロジェクトの保存場所を選択

View プロトコルに準拠した構造体を作成.

PreviewProvider プロトコルに準拠した構造体の previews プロパティに作成した View を登録

ContentView.swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Viewの中にTextやLabelなどをVStackやHStackを使って配置する

View | Apple Developer Documentation

protocol View

アプリのユーザー インターフェイスの一部を表し、ビューの構成に使用する修飾子を提供する型。

PreviewProvider | Apple Developer Documentation

protocol PreviewProvider : _PreviewProvider

Xcode でビュー プレビューを生成するタイプ

Text | Apple Developer Documentation

1 行以上の読み取り専用テキストを表示するビュー

@frozen struct Text

ProgressView | Apple Developer Documentation

タスクの完了に向けた進行状況を示すビュー

struct ProgressView<Label, CurrentValueLabel> where Label : View, CurrentValueLabel : View

VStack | Apple Developer Documentation

サブビューを縦一列に並べたビュー

@frozen struct VStack<Content> where Content : View

HStack | Apple Developer Documentation

サブビューを水平に並べたビュー

@frozen struct HStack<Content> where Content : View

Label | Apple Developer Documentation

タイトル付きのアイコンで構成される、ユーザー インターフェイス アイテムの標準ラベル。

struct Label<Title, Icon> where Title : View, Icon : View

SF Symbols - Apple Developer をアイコンとして使用できる

Spacer | Apple Developer Documentation

含まれているスタック レイアウトの長軸に沿って、またはスタックに含まれていない場合は両方の軸に沿って拡張する柔軟なスペース。

@frozen struct Spacer

Circle | Apple Developer Documentation

それを含むビューのフレームを中心とする円

@frozen struct Circle

Button | Apple Developer Documentation

アクションを開始するコントロール。

struct Button<Label> where Label : View

actionにボタンクリック時のイベントを登録する

Image | Apple Developer Documentation

A view that displays an image.

@frozen struct Image

Creating a card view — iOS App Dev Tutorials | Apple Developer Documentation

import Foundationimport SwiftUI だと何が変わるのだろう?

https://note.com/kaigian/n/n7fbbb0b4f016

SwiftUIFoundation が使用されているので,通常は明示的に Foundation を import する必要はない.
SwiftUI を使用しない(今回のチュートリアルでは構造体を定義するだけの)ファイルでは,Foundation を import する.

別のファイルで定義された構造体 (DailyScrum) を使用する際に,特に何もせずにいきなり使用できる?

Color | Apple Developer Documentation

特定のコンテキストに適応する色の表現

@frozen struct Color

LabelStyle | Apple Developer Documentation

ビュー内のすべてのラベルにカスタムの外観を適用するタイプ。

protocol LabelStyle

Displaying data in a list — iOS App Dev Tutorials | Apple Developer Documentation

hzuikahzuika

音楽プレーヤー

Media Player | Apple Developer Documentation

Media Player Framework

アプリ内から曲、オーディオ ポッドキャスト、オーディオ ブックなどを検索して再生

MusicKit (Apple Music と連携したりできる) の一部

https://qiita.com/bebeji_nappa/items/97b12b16174030f4d3cb

MPMusicPlayerControllerなるものがあるらしい

MPMusicPlayerController | Apple Developer Documentation

デバイスの音楽アプリ ライブラリからオーディオ メディア アイテムを再生するために使用されるオブジェクト。

class MPMusicPlayerController : NSObject

Playing Audio Using the Built-In Music Player | Apple Developer Documentation

音楽プレーヤーに関する記事

これを見ると,songs()で曲の情報を取得している

// Get the music player.
let musicPlayer = MPMusicPlayerApplicationController.applicationQueuePlayer
// Add a playback queue containing all songs on the device.
musicPlayer.setQueue(with: .songs())

songs() | Apple Developer Documentation

class func songs() -> MPMediaQuery

音楽アイテムに一致し、コレクションを曲名でグループ化およびソートするメディア クエリを作成します。

MPMediaQueryを返すらしい

MPMediaQuery | Apple Developer Documentation

フィルターとグループ化タイプを使用して、デバイスのメディア ライブラリから一連のメディア アイテムを指定するクエリ。

class MPMediaQuery : NSObject

items | Apple Developer Documentation

メディア クエリの述語に一致するメディア アイテムの配列。

var items: [MPMediaItem]? { get }

MPMediaQueryitems[MPMediaItem]が取得できる

hzuikahzuika

AVFAudio | Apple Developer Documentation

AVFAudio Framework

音楽再生フレームワーク

オーディオの再生、録音、および処理。アプリのシステム オーディオ動作を構成

AVAudioPlayer | Apple Developer Documentation

class AVAudioPlayer : NSObject

ファイルまたはバッファからオーディオ データを再生するオブジェクト

  • ファイルまたはバッファからオーディオ データを再生,一時停止,停止
  • ボリューム、パン、レート、ループ動作を制御
  • タイムラインの指定されたポイントからオーディオを非同期的に再生

AVAudioPlayer で複数ファイルを再生する際は毎回init()を呼び出す必要がある?

オーディオを再生するためのアプリの準備の詳細については、 iOS および tvOS アプリのオーディオ再生の構成を参照してください

ストリーミングや位置オーディオの再生など、より高度な再生機能については、代わりにAVAudioEngineを使用してください。

https://i-app-tec.com/ios/avaudioplayer.html

AVAudioPlayer を使っている記事

init(contentsOf:) | Apple Developer Documentation

ファイルからオーディオを再生するプレーヤーを作成します

init(contentsOf url: URL) throws

AVAudioPlayerURLを受け取ることができる.

hzuikahzuika

AVAudioEngine | Apple Developer Documentation

class AVAudioEngine : NSObject

オーディオ ノードのグラフを管理し、再生を制御し、リアルタイム レンダリングの制約を構成するオブジェクト

AVAudioEngine は,AVAudioNode を組み合わせて音楽を再生する.再生ノードと出力ノードの間に,ミキサーノードを接続したりできるらしい.

https://qiita.com/tsuruken/items/610b177c39639b918874

AVAudioEngine を使用した記事

曲の再生は,AVAudioPlayerNodeが担う

AVAudioPlayerNodescheduleFile()AVAudioFileを受け取る

AVAudioFileURLを受け取る

MPMediaItemassetURLURLを取得

こちらの記事の次/前の曲に移動する にて,

AVAudioEngineには曲のキューを保持する機能がありません。
なのでアプリケーションコードとして再生リストを保持しておく必要があります。

AVAudioPlayerNode | Apple Developer Documentation

オーディオ ファイルのバッファまたはセグメントの再生をスケジュールするためのオブジェクト。

class AVAudioPlayerNode : AVAudioNode

AVAudioEngineで再生するノード

AVAudioPlayerNodeAVAudioFileか,AVAudioPCMBufferを受け取ることができる.

AVAudioFile | Apple Developer Documentation

システムが読み取りまたは書き込み用に開くことができるオーディオ ファイルを表すオブジェクト。

class AVAudioFile : NSObject

URLを受け取る

init(forReading fileURL: URL) throws

readでファイルからバッファ (AVAudioFile -> AVAudioPCMBuffer)
writeでバッファからファイル (AVAudioPCMBuffer -> AVAudioFile )

func read(into buffer: AVAudioPCMBuffer) throws

func write(from buffer: AVAudioPCMBuffer) throws

AVAudioPCMBuffer | Apple Developer Documentation

PCM オーディオ形式で使用するオーディオ バッファーを表すオブジェクト。

class AVAudioPCMBuffer : AVAudioBuffer

PCMとは,Pulse Code Modulation(パルス符号変調)のこと
https://ja.wikipedia.org/wiki/パルス符号変調

MPMediaItem | Apple Developer Documentation

メディア ライブラリに含まれる 1 つのアイテムを表すプロパティのコレクション。

class MPMediaItem : MPMediaEntity

メディア(曲)の情報が入っている

assetURL | Apple Developer Documentation

メディア アイテムを指す URL。

var assetURL: URL? { get }
hzuikahzuika

ファイルの読み書き

https://capibara1969.com/2836/

DocumentsフォルダのURLを取得するコード

FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first

FileManager | Apple Developer Documentation

class FileManager : NSObject

ファイル システムのコンテンツへの便利なインターフェイスであり、それと対話する主要な手段です。

default | Apple Developer Documentation

プロセスの共有ファイル マネージャー オブジェクト。

class var `default`: FileManager { get }

urls(for:in:) | Apple Developer Documentation

要求されたドメイン内の指定された共通ディレクトリの URL の配列を返します。

func urls(
    for directory: FileManager.SearchPathDirectory,
    in domainMask: FileManager.SearchPathDomainMask
) -> [URL]

FileManager.SearchPathDirectory.documentDirectory | Apple Developer Documentation

ドキュメント ディレクトリ。

case documentDirectory = 9

userDomainMask | Apple Developer Documentation

ユーザーのホーム ディレクトリ — ユーザーの個人的なアイテム (~) をインストールする場所。

static var userDomainMask: FileManager.SearchPathDomainMask { get }

URL | Apple Developer Documentation

リモート サーバー上のアイテムやローカル ファイルへのパスなど、リソースの場所を識別する値。

struct URL
hzuikahzuika

hzuikahzuika

アイコンボタンを変更する

    @State var isPlay = false
    
    var body: some View {
        Button(action: {
            isPlay = !isPlay
        }, label: {
            Image(systemName: self.isPlay ? "pause" : "play")
        }).padding()
hzuikahzuika

再生はする...けれどあまり良くない例?

2023-01-03 23:41:05.623814+0900 MusicPlayer[22403:1091876] [plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x600003231ba0> F8BB1C28-BAE8-11D6-9C31-00039315CD46
import AVFoundation

struct ContentView: View {
    @State var isPlay = false
    
    @State var audioPlayer: AVAudioPlayer!
    
    var body: some View {
        Button(action: {
            isPlay = !isPlay
            
            if isPlay {
                self.audioPlayer.play()
            } else {
                self.audioPlayer.pause()
            }
        }, label: {
            Image(systemName: self.isPlay ? "pause" : "play")
        }).onAppear(perform: {
            guard let soundURL = Bundle.main.url(forResource: "sample", withExtension: "mp3") else {
              print("Not Found")
              return
            }
            
            self.audioPlayer = try! AVAudioPlayer(contentsOf: soundURL)
        }).padding()
    }
}

https://naoya-ono.com/swift/swift-play-sound/

https://swiswiswift.com/2020-12-12/

SwiftUIは struct を使うので AVAudioPlayerDelegate に準拠することができません。

hzuikahzuika

https://naoya-ono.com/swift/swift-play-sound/

https://swiswiswift.com/2020-12-12/

これらを参考にして,MusicPlayerクラスを作成

MusicPlayer.swift
import Foundation
import AVFoundation

class MusicPlayer: NSObject, ObservableObject, AVAudioPlayerDelegate {
    
    var audioPlayer: AVAudioPlayer?
    
    func setUrl(url: URL) {
        audioPlayer = try! AVAudioPlayer(contentsOf: url)
        audioPlayer?.delegate = self
    }

    func play() {
        audioPlayer?.play()
    }
    
    func pause() {
        audioPlayer?.pause()
    }
    
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        print("Did finish Playing")
    }
}
ContentView.swift
struct ContentView: View {
    @State var isPlay = false
    
    @ObservedObject var musicPlayer = MusicPlayer()

    var body: some View {
        Button(action: {
            isPlay = !isPlay
            
            if isPlay {
                musicPlayer.play()
            } else {
                musicPlayer.pause()
            }
        }, label: {
            Image(systemName: isPlay ? "pause" : "play")
        }).onAppear(perform: {
            guard let soundURL = Bundle.main.url(forResource: "sample", withExtension: "mp3") else {
              print("Not Found")
              return
            }
            
            musicPlayer.setUrl(url: soundURL)
        }).padding()
    }
}

AVAudioPlayerDelegate

AVAudioPlayer

次は,

  • 再生時間の表示
  • 次の曲の再生

などを行いたい