0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

iOSアプリ開発で強引にフォルダ指定して使用してみた

Posted at

今回の記事は失敗例となります

FileList_Get_testApp.swift

import SwiftUI
import SwiftData

@main
struct FileList_Get_testApp: App {
    @EnvironmentObject var userSettings: UserSettings // ✅ `UserSettings` を環境オブジェクトとして使用

    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(UserSettings())
        }
    }
}

FolderPickerView.swift

import SwiftUI
import UniformTypeIdentifiers

struct FolderPickerView: View {
    @State private var videoFiles: [URL] = [] // ✅ フォルダ内の動画ファイルを保存
    @State private var isDocumentPickerPresented = false
    @State private var folderURL: URL?
    @EnvironmentObject var userSettings: UserSettings // ✅ `UserSettings` を環境オブジェクトとして使用
    
    var body: some View {
        NavigationStack {
            VStack {
                Text("フォルダ内の動画一覧")
                    .font(.title)
                    .padding()

                if videoFiles.isEmpty {
                    Text("フォルダを選択してください")
                        .foregroundColor(.gray)
                } else {
                    List(videoFiles, id: \.self) { file in
                        NavigationLink(destination: VideoPlayerView(videoURL: file).environmentObject(UserSettings())) {
                            Text(file.lastPathComponent)
                        }
                    }
                }

                Button("フォルダを選択") {
                    isDocumentPickerPresented = true
                }
                .padding()
            }
            .navigationTitle("フォルダ選択")
            .sheet(isPresented: $isDocumentPickerPresented) {
                FolderPicker(videoFiles: $videoFiles, folderURL: $folderURL)
            }
            .onAppear {
                loadFolderBookmark() // ✅ アプリ再起動後にフォルダを復元
            }
        }
    }

    // ✅ `UserDefaults` からフォルダのブックマークを復元
    private func loadFolderBookmark() {
        guard let bookmarkData = UserDefaults.standard.data(forKey: "SavedFolderBookmark") else {
            print("ℹ️ 保存されたブックマークデータがありません")
            return
        }

        do {
            var isStale = false
            let restoredURL = try URL(resolvingBookmarkData: bookmarkData, options: [], relativeTo: nil, bookmarkDataIsStale: &isStale)

            if isStale {
                print("⚠️ ブックマークが古いため、再保存が必要")
                return
            }

            if restoredURL.startAccessingSecurityScopedResource() {
                self.folderURL = restoredURL
                print("✅ フォルダブックマークを復元しました: \(restoredURL)")
            } else {
                print("❌ Security-Scoped URL にアクセスできません")
            }
        } catch {
            print("❌ フォルダのブックマーク復元エラー: \(error.localizedDescription)")
        }
    }
}

// ✅ Security-Scoped URL を使ってフォルダの内容を取得
struct FolderPicker: UIViewControllerRepresentable {
    @Binding var videoFiles: [URL]
    @Binding var folderURL: URL?

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
        let picker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.folder])
        picker.delegate = context.coordinator
        picker.allowsMultipleSelection = false
        return picker
    }

    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {}

    class Coordinator: NSObject, UIDocumentPickerDelegate {
        let parent: FolderPicker

        init(_ parent: FolderPicker) {
            self.parent = parent
        }

        func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
            guard let selectedFolderURL = urls.first else { return }
            print("✅ 選択したフォルダ: \(selectedFolderURL)")

            // ✅ Security-Scoped URL の許可
            if selectedFolderURL.startAccessingSecurityScopedResource() {
                defer { selectedFolderURL.stopAccessingSecurityScopedResource() }

                // ✅ フォルダのブックマークを保存
                saveFolderBookmark(folderURL: selectedFolderURL)

                do {
                    let fileManager = FileManager.default
                    let files = try fileManager.contentsOfDirectory(at: selectedFolderURL, includingPropertiesForKeys: nil)

                    let videoFiles = files.filter { $0.pathExtension.lowercased() == "mp4" || $0.pathExtension.lowercased() == "mov" }

                    DispatchQueue.main.async {
                        self.parent.videoFiles = videoFiles
                        self.parent.folderURL = selectedFolderURL
                    }
                } catch {
                    print("❌ フォルダの内容を取得できませんでした: \(error.localizedDescription)")
                }
            } else {
                print("❌ Security-Scoped URL のアクセスに失敗しました")
            }
        }

        // ✅ `Security-Scoped URL` を `UserDefaults` に保存
        private func saveFolderBookmark(folderURL: URL) {
            do {
                let bookmarkData = try folderURL.bookmarkData(options: [], includingResourceValuesForKeys: nil, relativeTo: nil)
                UserDefaults.standard.set(bookmarkData, forKey: "SavedFolderBookmark")
                print("✅ フォルダのブックマークを保存しました")
            } catch {
                print("❌ フォルダのブックマーク保存エラー: \(error.localizedDescription)")
            }
        }
    }
}

VideoPlayerView.swift

import SwiftUI
import AVKit

struct VideoPlayerView: View {
    let videoURL: URL
    @State private var fileStatus: String = "🔍 パスを確認中..."
    
    var body: some View {
        VStack {
            VideoPlayer(player: AVPlayer(url: videoURL))
                .frame(height: 400)
                .cornerRadius(12)
                .padding()
                .onAppear {
                    checkFileStatus() // ✅ ファイルの存在チェック
                }

            Text("動画を再生中")
                .font(.headline)
                .padding()

            VStack {
                Text("📂 ファイルパス:")
                    .font(.caption)
                Text(videoURL.absoluteString) // ✅ `absoluteString` でファイルの正確なパスを表示
                    .font(.caption)
                    .foregroundColor(.gray)
                    .padding(.bottom, 4)

                Text("📝 ステータス: \(fileStatus)")
                    .font(.caption)
                    .foregroundColor(fileStatus.contains("✅") ? .green : .red)
            }
            .padding()
        }
        .navigationTitle("動画再生")
    }

    // ✅ `Security-Scoped URL` を適用し、フォルダ内のファイルを確認
    private func checkFileStatus() {
        let fileManager = FileManager.default
        let path = videoURL.path

        // ✅ `Security-Scoped URL` のアクセス許可を確認
        if videoURL.startAccessingSecurityScopedResource() {
            defer { videoURL.stopAccessingSecurityScopedResource() }

            do {
                let parentDirectory = videoURL.deletingLastPathComponent() // ✅ フォルダのURL
                let filesInDirectory = try fileManager.contentsOfDirectory(at: parentDirectory, includingPropertiesForKeys: nil)

                if filesInDirectory.contains(videoURL) {
                    fileStatus = "✅ ファイルが見つかりました"
                    print("✅ ファイルが存在: \(path)")
                } else {
                    fileStatus = "❌ フォルダ内にファイルが見つかりません"
                    print("❌ フォルダ内にファイルが見つからない: \(path)")
                }
            } catch {
                fileStatus = "❌ フォルダの内容を取得できませんでした"
                print("❌ `FileManager` のエラー: \(error.localizedDescription)")
            }
        } else {
            fileStatus = "❌ Security-Scoped URL にアクセスできません"
            print("❌ Security-Scoped URL にアクセスできません: \(path)")
        }
    }
}

UserSettings.swift

//import AVFoundation
import SwiftUI
import AVKit

class UserSettings: ObservableObject {
    @Published var player = AVPlayer()

    // ✅ 動画URLを設定し、AVPlayerItem を更新
    func setVideoURL(_ url: URL) {
        print("🎥 設定する動画URL: \(url.absoluteString)")

        // ✅ Security-Scoped URL のアクセス許可
        if url.startAccessingSecurityScopedResource() {
            defer { url.stopAccessingSecurityScopedResource() } // ✅ 使用後に解放

            let playerItem = AVPlayerItem(url: url)
            player.replaceCurrentItem(with: playerItem) // ✅ `AVPlayerItem` を設定
        } else {
            print("❌ Security-Scoped URL にアクセスできません")
        }
    }

    // ✅ 再生開始
    func play() {
        player.play()
    }

    // ✅ 一時停止
    func pause() {
        player.pause()
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?