From 2f77723cdf7567eef1bc06de7fd2563c97f05f87 Mon Sep 17 00:00:00 2001 From: Viktor Kushnerov Date: Tue, 31 Dec 2019 11:55:10 +0300 Subject: [PATCH] AudioPlayer features #58: Divide AudioStore to AudioStore and FileStore --- ReaderTranslator.xcodeproj/project.pbxproj | 4 ++ ReaderTranslator/Extentions/Logger.swift | 7 +- ReaderTranslator/Model/AvailableView.swift | 6 +- .../Networking/ConnectionServerStatus.swift | 5 +- ReaderTranslator/Networking/NetBrowser.swift | 10 +-- ReaderTranslator/Networking/PeerBrowser.swift | 8 +-- .../Networking/PeerConnection.swift | 10 +-- .../Networking/PeerListener.swift | 8 +-- .../Networking/ReaderTranslatorProtocol.swift | 4 +- ReaderTranslator/Networking/Server.swift | 10 +-- .../Views/StatusBarView/SettingsView.swift | 1 + ReaderTranslatorPlayer/Store/AudioStore.swift | 64 ++++++++++++------- ReaderTranslatorPlayer/Store/FileStore.swift | 46 +++++++++++++ .../PlayerContols/PlayerContolsView.swift | 8 +-- .../Views/ViewModes/FileListView.swift | 62 ++---------------- 15 files changed, 139 insertions(+), 114 deletions(-) create mode 100644 ReaderTranslatorPlayer/Store/FileStore.swift diff --git a/ReaderTranslator.xcodeproj/project.pbxproj b/ReaderTranslator.xcodeproj/project.pbxproj index 8cc45b9..3041714 100644 --- a/ReaderTranslator.xcodeproj/project.pbxproj +++ b/ReaderTranslator.xcodeproj/project.pbxproj @@ -56,6 +56,7 @@ F0305FD223812711002AC5F5 /* YTranslatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0305FD0238125E6002AC5F5 /* YTranslatorView.swift */; }; F0305FD32381271A002AC5F5 /* YTranslatorRepresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F062029423812264002EEAEE /* YTranslatorRepresenter.swift */; }; F0305FD42381271A002AC5F5 /* YTranslatorRepresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F062029423812264002EEAEE /* YTranslatorRepresenter.swift */; }; + F03176B323BB2C51004388A7 /* FileStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03176B223BB2C51004388A7 /* FileStore.swift */; }; F033F41223A6D3A800605325 /* DirectoryObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = F033F41123A6D3A800605325 /* DirectoryObserver.swift */; }; F033F41523A75B5600605325 /* SentencesView_Row.swift in Sources */ = {isa = PBXBuildFile; fileRef = F033F41423A75B5600605325 /* SentencesView_Row.swift */; }; F0369E3223A2D92C00C33139 /* StatusBarView_Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0369E3123A2D92C00C33139 /* StatusBarView_Sync.swift */; }; @@ -417,6 +418,7 @@ F02B04BE23A2898300F93B84 /* PeerConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerConnection.swift; sourceTree = ""; }; F02B04C223A2930D00F93B84 /* ConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionView.swift; sourceTree = ""; }; F0305FD0238125E6002AC5F5 /* YTranslatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YTranslatorView.swift; sourceTree = ""; }; + F03176B223BB2C51004388A7 /* FileStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileStore.swift; sourceTree = ""; }; F033F41123A6D3A800605325 /* DirectoryObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectoryObserver.swift; sourceTree = ""; }; F033F41423A75B5600605325 /* SentencesView_Row.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentencesView_Row.swift; sourceTree = ""; }; F0369E3123A2D92C00C33139 /* StatusBarView_Sync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarView_Sync.swift; sourceTree = ""; }; @@ -647,6 +649,7 @@ children = ( F012038C239286A2008D0B47 /* Store.swift */, F065095B23ADF971003D2410 /* AudioStore.swift */, + F03176B223BB2C51004388A7 /* FileStore.swift */, ); path = Store; sourceTree = ""; @@ -1395,6 +1398,7 @@ F06EE7C923AFF32700CF989E /* ConnectionView_Servers.swift in Sources */, F01203902392D586008D0B47 /* RoundButtonStyle.swift in Sources */, F033F41523A75B5600605325 /* SentencesView_Row.swift in Sources */, + F03176B323BB2C51004388A7 /* FileStore.swift in Sources */, F0C02D3723B26A3800B393A5 /* OSLog.swift in Sources */, F099424A23AD549C003CF1EB /* BookmarksStore.swift in Sources */, F0B4843F23A8C6860031A811 /* URL.swift in Sources */, diff --git a/ReaderTranslator/Extentions/Logger.swift b/ReaderTranslator/Extentions/Logger.swift index 2cee84e..1f91ed8 100644 --- a/ReaderTranslator/Extentions/Logger.swift +++ b/ReaderTranslator/Extentions/Logger.swift @@ -9,8 +9,9 @@ import os.log import Foundation final class Logger { - static func debug( - log: OSLog, + static func log( + log: OSLog = .default, + type: OSLogType = .error, className: AnyClass? = nil, callback: String = "", delegate: String = "", @@ -28,7 +29,7 @@ final class Logger { let className = className == nil ? "" : String(describing: className!) let value = value == nil ? "" : ": \(String(describing: value!))" - os_log("%s:%i %s%s%s.%s%s", log: log, type: .debug, + os_log("%s:%i %s%s%s.%s%s", log: log, type: type, file, line, className, delegate, callback, function, value) } } diff --git a/ReaderTranslator/Model/AvailableView.swift b/ReaderTranslator/Model/AvailableView.swift index 8f823ae..bf6758d 100644 --- a/ReaderTranslator/Model/AvailableView.swift +++ b/ReaderTranslator/Model/AvailableView.swift @@ -37,7 +37,9 @@ enum AvailableView: String, Codable, CaseIterable { var order: Binding { Binding( - get: { "\(ViewsStore.shared.viewOrder[self] ?? 0)" }, + get: { + "\(ViewsStore.shared.viewOrder[self] ?? 0)" + }, set: { ViewsStore.shared.viewOrder[self] = $0.intValue } @@ -45,7 +47,7 @@ enum AvailableView: String, Codable, CaseIterable { } var orderInt: Int { - order.wrappedValue.intValue + ViewsStore.shared.viewOrder[self] ?? 0 } var view: some View { diff --git a/ReaderTranslator/Networking/ConnectionServerStatus.swift b/ReaderTranslator/Networking/ConnectionServerStatus.swift index ce96f36..10c99de 100644 --- a/ReaderTranslator/Networking/ConnectionServerStatus.swift +++ b/ReaderTranslator/Networking/ConnectionServerStatus.swift @@ -5,7 +5,7 @@ // Created by Viktor Kushnerov on 13/12/19. // Copyright © 2019 Viktor Kushnerov. All rights reserved. // - +import Foundation import Network enum ErrorServerConnection: Error { @@ -56,3 +56,6 @@ enum ConnectionServerStatus: Equatable { } } } + + + diff --git a/ReaderTranslator/Networking/NetBrowser.swift b/ReaderTranslator/Networking/NetBrowser.swift index c91c5e3..e073b3e 100644 --- a/ReaderTranslator/Networking/NetBrowser.swift +++ b/ReaderTranslator/Networking/NetBrowser.swift @@ -22,7 +22,7 @@ class NetBrowser: ObservableObject { } func connectToServer(server: NWBrowser.Result, passcode: String) { - Logger.debug(log: .p2p) + Logger.log(log: .p2p) sharedConnection = PeerConnection( endpoint: server.endpoint, interface: server.interfaces.first, @@ -35,7 +35,7 @@ class NetBrowser: ObservableObject { extension NetBrowser: PeerBrowserDelegate { func refreshResults(results: Set) { - Logger.debug(log: .p2p, delegate: "PeerBrowserDelegate") + Logger.log(log: .p2p, delegate: "PeerBrowserDelegate") status = .none servers = results.map { $0 } } @@ -43,17 +43,17 @@ extension NetBrowser: PeerBrowserDelegate { extension NetBrowser: PeerConnectionDelegate { func connectionReady() { - Logger.debug(log: .p2p, delegate: "PeerBrowserDelegate") + Logger.log(log: .p2p, delegate: "PeerBrowserDelegate") status = .connected } func connectionFailed() { - Logger.debug(log: .p2p, delegate: "PeerBrowserDelegate") + Logger.log(log: .p2p, delegate: "PeerBrowserDelegate") status = .failed(error: "failed") } func receivedMessage(content: Data?, message: NWProtocolFramer.Message) { - Logger.debug(log: .p2p, delegate: "PeerBrowserDelegate") + Logger.log(log: .p2p, delegate: "PeerBrowserDelegate") // switch message.readerTranslatorMessageType { // case .invalid: print("Received invalid message") // case .send: store.bookmarks.merge(data: content) diff --git a/ReaderTranslator/Networking/PeerBrowser.swift b/ReaderTranslator/Networking/PeerBrowser.swift index a2b660c..7a8311b 100644 --- a/ReaderTranslator/Networking/PeerBrowser.swift +++ b/ReaderTranslator/Networking/PeerBrowser.swift @@ -28,7 +28,7 @@ class PeerBrowser { // Start browsing for services. func startBrowsing() { - Logger.debug(log: .p2p) + Logger.log(log: .p2p) // Create parameters, and allow browsing over peer-to-peer link. let parameters = NWParameters() parameters.includePeerToPeer = true @@ -37,11 +37,11 @@ class PeerBrowser { let browser = NWBrowser(for: .bonjour(type: "_reader_translator._tcp", domain: nil), using: parameters) self.browser = browser browser.stateUpdateHandler = { newState in - Logger.debug(log: .p2p, callback: "stateUpdateHandler", value: newState) + Logger.log(log: .p2p, callback: "stateUpdateHandler", value: newState) switch newState { case let .failed(error): // Restart the browser if it fails. - Logger.debug( + Logger.log( log: .p2p, className: Self.self, value: "Browser failed with \(error), restarting") @@ -54,7 +54,7 @@ class PeerBrowser { // When the list of discovered endpoints changes, refresh the delegate. browser.browseResultsChangedHandler = { results, _ in - Logger.debug(log: .p2p, callback: "browseResultsChangedHandler") + Logger.log(log: .p2p, callback: "browseResultsChangedHandler") self.delegate?.refreshResults(results: results) } diff --git a/ReaderTranslator/Networking/PeerConnection.swift b/ReaderTranslator/Networking/PeerConnection.swift index b342219..482e93d 100644 --- a/ReaderTranslator/Networking/PeerConnection.swift +++ b/ReaderTranslator/Networking/PeerConnection.swift @@ -52,13 +52,13 @@ class PeerConnection { // Handle starting the peer-to-peer connection for both inbound and outbound connections. func startConnection() { - Logger.debug(log: .p2p) + Logger.log(log: .p2p) guard let connection = connection else { return } connection.stateUpdateHandler = { newState in - Logger.debug(log: .p2p, callback: "stateUpdateHandler", value: newState) + Logger.log(log: .p2p, callback: "stateUpdateHandler", value: newState) switch newState { case .ready: print("\(connection) established") @@ -91,7 +91,7 @@ class PeerConnection { // Handle sending a "send" message. func sendBookmarks(_ bookmarks: String) { - Logger.debug(log: .p2p) + Logger.log(log: .p2p) guard let connection = connection else { return } @@ -112,13 +112,13 @@ class PeerConnection { // Receive a message, deliver it to your delegate, and continue receiving more messages. func receiveNextMessage() { - Logger.debug(log: .p2p) + Logger.log(log: .p2p) guard let connection = connection else { return } connection.receiveMessage { content, context, _, error in - Logger.debug(log: .p2p, callback: "receiveMessage") + Logger.log(log: .p2p, callback: "receiveMessage") // Extract your message type from the received context. let message = context?.protocolMetadata(definition: ReaderTranslatorProtocol.definition) if let message = message as? NWProtocolFramer.Message { diff --git a/ReaderTranslator/Networking/PeerListener.swift b/ReaderTranslator/Networking/PeerListener.swift index a4247b7..778bc40 100644 --- a/ReaderTranslator/Networking/PeerListener.swift +++ b/ReaderTranslator/Networking/PeerListener.swift @@ -26,7 +26,7 @@ class PeerListener { // Start listening and advertising. func startListening() { - Logger.debug(log: .p2p) + Logger.log(log: .p2p) do { // Create the listener object. let listener = try NWListener(using: NWParameters(passcode: passcode)) @@ -36,7 +36,7 @@ class PeerListener { listener.service = NWListener.Service(name: name, type: "_reader_translator._tcp") listener.newConnectionHandler = { newConnection in - Logger.debug(log: .p2p, callback: "newConnectionHandler", value: newConnection) + Logger.log(log: .p2p, callback: "newConnectionHandler", value: newConnection) if let delegate = self.delegate { if sharedConnection == nil { // Accept a new connection. @@ -57,14 +57,14 @@ class PeerListener { } func stopListening() { - Logger.debug(log: .p2p) + Logger.log(log: .p2p) listener?.cancel() sharedConnection = nil } // If the user changes their name, update the advertised name. func resetName(_ name: String) { - Logger.debug(log: .p2p, value: name) + Logger.log(log: .p2p, value: name) self.name = name if let listener = listener { // Reset the service to advertise. diff --git a/ReaderTranslator/Networking/ReaderTranslatorProtocol.swift b/ReaderTranslator/Networking/ReaderTranslatorProtocol.swift index 4533a09..5d4add5 100644 --- a/ReaderTranslator/Networking/ReaderTranslatorProtocol.swift +++ b/ReaderTranslator/Networking/ReaderTranslatorProtocol.swift @@ -37,7 +37,7 @@ class ReaderTranslatorProtocol: NWProtocolFramerImplementation { messageLength: Int, isComplete _: Bool ) { - Logger.debug(log: .p2p, value: message) + Logger.log(log: .p2p, value: message) // Extract the type of message. let type = message.readerTranslatorMessageType @@ -84,7 +84,7 @@ class ReaderTranslatorProtocol: NWProtocolFramerImplementation { messageType = parsedMessageType } let message = NWProtocolFramer.Message(readerTranslatorMessageType: messageType) - Logger.debug(log: .p2p, value: message) + Logger.log(log: .p2p, value: message) // Deliver the body of the message, along with the message object. if !framer.deliverInputNoCopy(length: Int(header.length), message: message, isComplete: true) { diff --git a/ReaderTranslator/Networking/Server.swift b/ReaderTranslator/Networking/Server.swift index eca8ca6..59fa412 100644 --- a/ReaderTranslator/Networking/Server.swift +++ b/ReaderTranslator/Networking/Server.swift @@ -21,7 +21,7 @@ class Server: ObservableObject { extension Server { func stateUpdateHandler(newState: NWListener.State) { - Logger.debug(log: .p2p, value: newState) + Logger.log(log: .p2p, value: newState) switch newState { case .ready: status = .ready @@ -38,7 +38,7 @@ extension Server { } func start() { - Logger.debug(log: .p2p) + Logger.log(log: .p2p) passcode = "1111" // self.generatePasscode connectionCount += 1 sharedListener?.stopListening() @@ -52,20 +52,20 @@ extension Server { extension Server: PeerConnectionDelegate { // When a connection becomes ready, move into game mode. func connectionReady() { - Logger.debug(log: .p2p, delegate: "PeerConnectionDelegate") + Logger.log(log: .p2p, delegate: "PeerConnectionDelegate") // navigationController?.performSegue(withIdentifier: "showGameSegue", sender: nil) status = .connected } // Ignore connection failures and messages prior to starting a game. func connectionFailed() { - Logger.debug(log: .p2p, delegate: "PeerConnectionDelegate") + Logger.log(log: .p2p, delegate: "PeerConnectionDelegate") status = .failed(error: .connection(text: "connection failed")) start() } func receivedMessage(content: Data?, message: NWProtocolFramer.Message) { - Logger.debug(log: .p2p, delegate: "PeerConnectionDelegate", value: message) + Logger.log(log: .p2p, delegate: "PeerConnectionDelegate", value: message) guard let content = content else { return } if let text = String(data: content, encoding: .unicode) { print(text) diff --git a/ReaderTranslator/Views/StatusBarView/SettingsView.swift b/ReaderTranslator/Views/StatusBarView/SettingsView.swift index 2096f2f..57be858 100644 --- a/ReaderTranslator/Views/StatusBarView/SettingsView.swift +++ b/ReaderTranslator/Views/StatusBarView/SettingsView.swift @@ -38,3 +38,4 @@ struct SettingsView_Previews: PreviewProvider { SettingsView() } } + diff --git a/ReaderTranslatorPlayer/Store/AudioStore.swift b/ReaderTranslatorPlayer/Store/AudioStore.swift index 03a60fa..45d4bde 100644 --- a/ReaderTranslatorPlayer/Store/AudioStore.swift +++ b/ReaderTranslatorPlayer/Store/AudioStore.swift @@ -9,9 +9,10 @@ import AVFoundation import SwiftUI +var PREVIEW_MODE: Bool = false + final class AudioStore: NSObject, ObservableObject { - private override init() { super.init() } - static var shared = AudioStore() + static let shared = AudioStore() private var timer: Timer? private var sleepTimer: Timer? @@ -19,7 +20,6 @@ final class AudioStore: NSObject, ObservableObject { private var someObservationContext = "" @Published var currentStatus = "0.0/0.0" - @Published var files: [URL] = [] @Published var isPlaying = false { willSet { guard let player = player else { return } @@ -42,21 +42,26 @@ final class AudioStore: NSObject, ObservableObject { @Published(key: "voiceVolume") var volume: Float = 1 @Published(key: "audioRate") var rate: Float = 1 @Published var sleepAfter = 0 -} -extension AudioStore { - func openAudio(url: URL) { + private override init() { + super.init() + do { - player = try AVAudioPlayer(contentsOf: url) - guard let player = player else { return } - player.delegate = self - player.enableRate = true - player.rate = rate + let sharedInstance = AVAudioSession.sharedInstance() + + try sharedInstance.setCategory(.playback, mode: .default, options: [.mixWithOthers, .allowAirPlay]) + try sharedInstance.setActive(true) } catch { - print(error) + Logger.log(type: .error, value: error) + } + + if player == nil { + if let lastAudio = lastAudio { openAudio(url: lastAudio) } } } +} +extension AudioStore { func invalidate() { timer?.invalidate() } @@ -68,18 +73,18 @@ extension AudioStore { self.currentStatus = String(format: "%.1f/%.1f", player.currentTime, player.duration) } } - + func setSleepTimer(minutes: Int) { - sleepTimer = Timer.scheduledTimer(withTimeInterval: Double(minutes), repeats: false) { _ in + sleepTimer = Timer.scheduledTimer(withTimeInterval: Double(minutes * 50), repeats: false) { _ in self.isPlaying = false } } - + func stopSleepTimer() { sleepTimer?.invalidate() sleepTimer = nil } - + var remainTimerTime: String { guard let fireDate = self.sleepTimer?.fireDate else { return "The timer is off" } let time = RelativeDateTimeFormatter().localizedString(for: fireDate, relativeTo: Date()) @@ -87,17 +92,30 @@ extension AudioStore { } } +extension AudioStore { + func openAudio(url: URL) { + do { + player = try AVAudioPlayer(contentsOf: url) + guard let player = player else { return } + player.delegate = self + player.enableRate = true + player.rate = rate + lastAudio = url + } catch { + print(error) + } + } +} + extension AudioStore: AVAudioPlayerDelegate { func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { if flag { - guard let lastAudio = lastAudio else { return } - guard let current = files.firstIndex(of: lastAudio) else { return } - let next = files.index(after: current) - if files.indices.contains(next) { - let url = files[next] - openAudio(url: url) - isPlaying = true + guard let url = FileStore.shared.nextFile(file: lastAudio) else { + isPlaying = false + return } + openAudio(url: url) + isPlaying = true } } } diff --git a/ReaderTranslatorPlayer/Store/FileStore.swift b/ReaderTranslatorPlayer/Store/FileStore.swift new file mode 100644 index 0000000..bdcbd7d --- /dev/null +++ b/ReaderTranslatorPlayer/Store/FileStore.swift @@ -0,0 +1,46 @@ +// +// FileStore.swift +// ReaderTranslatorPlayer +// +// Created by Viktor Kushnerov on 31/12/19. +// Copyright © 2019 Viktor Kushnerov. All rights reserved. +// + +import Foundation + +final class FileStore: ObservableObject { + static let shared = FileStore() + + static private var directoryObserver: DirectoryObserver? + + var files: [URL] { + guard let url = folderUrl else { return [] } + + do { + return try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil) + } catch { + Logger.log(type: .error, value: error) + return [] + } + } + + private let folderUrl: URL? = { + guard let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { + return nil + } + return documentsUrl.appendingPathComponent("/Inbox") + }() + + private init() { + + } + + func nextFile(file: URL?) -> URL? { + guard let file = file else { return nil } + guard let current = files.firstIndex(of: file) else { return nil } + let next = files.index(after: current) + + return files.indices.contains(next) ? files[next] : nil + } +} + diff --git a/ReaderTranslatorPlayer/Views/PlayerContols/PlayerContolsView.swift b/ReaderTranslatorPlayer/Views/PlayerContols/PlayerContolsView.swift index f7aca65..2f94c3f 100644 --- a/ReaderTranslatorPlayer/Views/PlayerContols/PlayerContolsView.swift +++ b/ReaderTranslatorPlayer/Views/PlayerContols/PlayerContolsView.swift @@ -73,13 +73,13 @@ struct PlayerControlsView: View { Array(-1...20).map { val in if val == -1 { return .cancel() } - let val = val * 10 - let value = val == 0 ? "Off" : String(describing: val) + let minutes = val * 10 + let value = minutes == 0 ? "Off" : String(describing: minutes) let action = { - if val == 0 { + if minutes == 0 { self.audioStore.stopSleepTimer() - } else { self.audioStore.setSleepTimer(minutes: val * 60) } + } else { self.audioStore.setSleepTimer(minutes: minutes) } } return .default(Text("\(value)"), action: action) } diff --git a/ReaderTranslatorPlayer/Views/ViewModes/FileListView.swift b/ReaderTranslatorPlayer/Views/ViewModes/FileListView.swift index 3131d25..7c18318 100644 --- a/ReaderTranslatorPlayer/Views/ViewModes/FileListView.swift +++ b/ReaderTranslatorPlayer/Views/ViewModes/FileListView.swift @@ -11,29 +11,13 @@ import Dispatch import SwiftUI struct FileListView: View { - var debug: Bool - + @ObservedObject var fileStore = FileStore.shared @ObservedObject var audioStore = AudioStore.shared - static var directoryObserver: DirectoryObserver? - - init(debug: Bool = false) { - self.debug = debug - do { - let sharedInstance = AVAudioSession.sharedInstance() - - try sharedInstance.setCategory(.playback, mode: .default, options: [.mixWithOthers, .allowAirPlay]) - try sharedInstance.setActive(true) - } catch { - print(error) - } - } - var body: some View { List { - ForEach(audioStore.files, id: \.self) { url in + ForEach(fileStore.files, id: \.self) { url in Button(action: { - self.audioStore.lastAudio = url self.audioStore.openAudio(url: url) self.audioStore.isPlaying = true }, label: { @@ -43,60 +27,26 @@ struct FileListView: View { } .onDelete { indexSet in guard let first = indexSet.first else { return } - let file = self.audioStore.files[first] + let file = self.fileStore.files[first] do { try FileManager.default.removeItem(at: file) -// self.refresh() } catch { print(error) } } } - .onAppear { - if let url = self.folderUrl { - RunLoop.main.perform { - if self.debug { - self.audioStore.files = [Bundle.main.bundleURL.appendingPathComponent("test audio")] - } else { - self.refresh() - } - } - Self.directoryObserver = DirectoryObserver(URL: url) { self.refresh() } - } - if self.audioStore.player == nil { - if let lastAudio = self.audioStore.lastAudio { self.audioStore.openAudio(url: lastAudio) } - } - } } - private let folderUrl: URL? = { - guard let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { - return nil - } - return documentsUrl.appendingPathComponent("/Inbox") - }() private func getColor(url: URL) -> Color { audioStore.lastAudio?.lastPathComponent == url.lastPathComponent ? Color.yellow : Color.primary } - - private func refresh() { - audioStore.files = [] - guard let url = folderUrl else { return } - - do { - let items = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil) - - audioStore.files = items - } catch { - print(error) - audioStore.files = [] - } - } } struct FileListView_Previews: PreviewProvider { static var previews: some View { - FileListView(debug: true) + PREVIEW_MODE = true + + return FileListView() } }