diff --git a/iosApp/GoogleService-Info.plist b/iosApp/GoogleService-Info.plist new file mode 100644 index 00000000..ba1ebfcb --- /dev/null +++ b/iosApp/GoogleService-Info.plist @@ -0,0 +1,34 @@ + + + + + CLIENT_ID + 1074796601956-ibaf9r1sunn4tfm8c9og40ejuhkcvukf.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.1074796601956-ibaf9r1sunn4tfm8c9og40ejuhkcvukf + API_KEY + AIzaSyA-Fg5YNScjNlj_XPwBNeafUoqfZuuCyEk + GCM_SENDER_ID + 1074796601956 + PLIST_VERSION + 1 + BUNDLE_ID + io.newm.ios + PROJECT_ID + projectnewm-38b24 + STORAGE_BUCKET + projectnewm-38b24.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:1074796601956:ios:68c857bcac45cfbd7db571 + + \ No newline at end of file diff --git a/iosApp/Modules/AudioPlayer/PlayQueue/PlayQueue.swift b/iosApp/Modules/AudioPlayer/PlayQueue/PlayQueue.swift index 66c53f18..b0fc4c01 100644 --- a/iosApp/Modules/AudioPlayer/PlayQueue/PlayQueue.swift +++ b/iosApp/Modules/AudioPlayer/PlayQueue/PlayQueue.swift @@ -2,7 +2,6 @@ import Foundation import shared import Utilities -@MainActor struct PlayQueue { var originalTracks: Set = [] { didSet { @@ -204,5 +203,9 @@ extension PlayQueue { case queueIsEmpty case invalidIndex case trackNotInQueue + + var errorDescription: String? { + "There was an error playing the song." + } } } diff --git a/iosApp/Modules/AudioPlayer/UI/PlayButton.swift b/iosApp/Modules/AudioPlayer/UI/PlayButton.swift index ec370138..25b350cc 100644 --- a/iosApp/Modules/AudioPlayer/UI/PlayButton.swift +++ b/iosApp/Modules/AudioPlayer/UI/PlayButton.swift @@ -2,6 +2,8 @@ import SwiftUI import SharedUI import Resolver import ModuleLinker +import shared +import Analytics public struct PlayButton: View { @InjectedObject private var audioPlayer: VLCAudioPlayer @@ -13,8 +15,10 @@ public struct PlayButton: View { switch audioPlayer.state { case .playing: audioPlayer.pause() + logPauseTapped() case .paused, .stopped: audioPlayer.play() + logPlayTapped() case .buffering: break } @@ -47,6 +51,30 @@ public struct PlayButton: View { } } +extension PlayButton { + private func logPauseTapped() { + NEWMAnalytics.trackClickEvent( + buttonName: AppScreens.MusicPlayerScreen().PAUSE_BUTTON, + screenName: AppScreens.MusicPlayerScreen().name, + properties: + [ + "song_id": audioPlayer.currentTrack?.id ?? "", + ] + ) + } + + private func logPlayTapped() { + NEWMAnalytics.trackClickEvent( + buttonName: AppScreens.MusicPlayerScreen().PLAY_BUTTON, + screenName: AppScreens.MusicPlayerScreen().name, + properties: + [ + "song_id": audioPlayer.currentTrack?.id ?? "", + ] + ) + } +} + #Preview { PlayButton() .preferredColorScheme(.dark) diff --git a/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/Seconds.swift b/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/Seconds.swift index bde8f725..7855e38d 100644 --- a/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/Seconds.swift +++ b/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/Seconds.swift @@ -1,7 +1,7 @@ import Foundation import VLCKitSPM -extension VLCTime { +public extension VLCTime { var seconds: Double? { value.flatMap { $0.doubleValue / 1_000 } } diff --git a/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/VLCAudioPlayer+MediaPlayer.swift b/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/VLCAudioPlayer+MediaPlayer.swift index 1909399e..a76e5ac7 100644 --- a/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/VLCAudioPlayer+MediaPlayer.swift +++ b/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/VLCAudioPlayer+MediaPlayer.swift @@ -36,8 +36,8 @@ extension VLCAudioPlayer { nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = duration } - if let currentTime { - nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentTime + if let seconds = currentTime?.seconds { + nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = seconds } MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo diff --git a/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/VLCAudioPlayer+VLCMediaPlayerDelegate.swift b/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/VLCAudioPlayer+VLCMediaPlayerDelegate.swift index 71fced88..1e3badef 100644 --- a/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/VLCAudioPlayer+VLCMediaPlayerDelegate.swift +++ b/iosApp/Modules/AudioPlayer/VLCAudioPlayer/Extensions/VLCAudioPlayer+VLCMediaPlayerDelegate.swift @@ -9,12 +9,22 @@ class VLCAudioPlayerDelegate: NSObject, VLCMediaPlayerDelegate { } private var continuation: AsyncStream.Continuation! + private var lastYieldTime: Date? + private let throttleInterval: TimeInterval = 1.0// Throttle interval in seconds func mediaPlayerStateChanged(_ aNotification: Foundation.Notification) { continuation.yield() } func mediaPlayerTimeChanged(_ aNotification: Foundation.Notification) { + // Throttle timeChanged calls based on the last yield time + let now = Date() + + if let lastTime = lastYieldTime, now.timeIntervalSince(lastTime) < throttleInterval { + return // Skip this event if it's within the throttle interval + } + + lastYieldTime = now continuation.yield() } diff --git a/iosApp/Modules/AudioPlayer/VLCAudioPlayer/VLCAudioPlayer.swift b/iosApp/Modules/AudioPlayer/VLCAudioPlayer/VLCAudioPlayer.swift index c9117bf2..bfca07ef 100644 --- a/iosApp/Modules/AudioPlayer/VLCAudioPlayer/VLCAudioPlayer.swift +++ b/iosApp/Modules/AudioPlayer/VLCAudioPlayer/VLCAudioPlayer.swift @@ -20,7 +20,11 @@ public class VLCAudioPlayer: ObservableObject { static let sharedPlayer = VLCAudioPlayer() private var mediaPlayer: VLCMediaPlayer - @Published private var playQueue = PlayQueue() + private var playQueue = PlayQueue() { + didSet { + update() + } + } @Published private var fileManager = FileManagerService() lazy private var delegate: VLCAudioPlayerDelegate = VLCAudioPlayerDelegate() private var cancels = Set() @@ -30,24 +34,16 @@ public class VLCAudioPlayer: ObservableObject { private var _errors = PassthroughSubject() public var errors: AnyPublisher { _errors.eraseToAnyPublisher() } - public var state: PlaybackState { - if mediaPlayer.isPlaying { - return .playing - } else if mediaPlayer.state == .buffering { - return .buffering - } else if mediaPlayer.state == .paused { - return .paused - } else { - return .stopped - } - } - public var duration: TimeInterval? { mediaPlayer.media?.length.seconds } - public var currentTime: TimeInterval? { mediaPlayer.time.seconds } - public var percentPlayed: Float? { mediaPlayer.position } - public var title: String? { mediaPlayer.media?.metaData.title } - public var artist: String? { mediaPlayer.media?.metaData.artist } - public var artworkUrl: URL? { mediaPlayer.media?.metaData.artworkURL } - public var willPlay: Bool { mediaPlayer.willPlay } + @Published public var state: PlaybackState = .stopped + @Published public var duration: TimeInterval? + @Published public var currentTime: VLCTime? + @Published public var percentPlayed: Float? + @Published public var title: String? + @Published public var artist: String? + @Published public var artworkUrl: URL? + @Published public var currentTrack: NFTTrack? + @Published public var isPlaying: Bool = false + @Injected var errorReporter: any ErrorReporting public var textFilter: String? { @@ -81,7 +77,7 @@ public class VLCAudioPlayer: ObservableObject { fileManager.objectWillChange .receive(on: DispatchQueue.main) .sink { [weak self] in - self?.objectWillChange.send() + self?.update() }.store(in: &cancels) NotificationCenter.default.publisher(for: Notification.Name(Notification().walletConnectionStateChanged)).sink { [weak self] _ in @@ -105,18 +101,36 @@ public class VLCAudioPlayer: ObservableObject { playCurrentTrackInQueue() } else if mediaPlayer.state == .error { _errors.send("Unable to load \(currentTrack?.title ?? "song")") - } - DispatchQueue.main.async { [weak self] in - self?.objectWillChange.send() + } else if mediaPlayer.time != currentTime { + Task { @MainActor in + update() + } } } } } - public func setTracks(_ tracks: Set) { - playQueue.originalTracks = tracks + private func update() { + title = mediaPlayer.media?.metaData.title + duration = mediaPlayer.media?.length.seconds + currentTime = mediaPlayer.time + percentPlayed = mediaPlayer.position + title = mediaPlayer.media?.metaData.title + artist = mediaPlayer.media?.metaData.artist + artworkUrl = mediaPlayer.media?.metaData.artworkURL + currentTrack = try? playQueue.currentTrack() + isPlaying = state == .playing + state = if mediaPlayer.isPlaying { + .playing + } else if mediaPlayer.state == .buffering { + .buffering + } else if mediaPlayer.state == .paused { + .paused + } else { + .stopped + } } - + public var hasNextTrack: Bool { playQueue.hasNextTrack } @@ -164,7 +178,7 @@ public class VLCAudioPlayer: ObservableObject { try await fileManager.download(track: track) { [weak self] progress in guard let self else { return } print("progress for [\(track.title)]: \(progress)") - DispatchQueue.main.async { + Task { @MainActor in self.loadingProgress[track] = progress } } @@ -219,24 +233,12 @@ public class VLCAudioPlayer: ObservableObject { public func setTracks(_ tracks: Set, playFirstTrack: Bool = true) { playQueue.originalTracks = tracks - try! playQueue.seekToFirst() - if playFirstTrack { + if playFirstTrack, tracks.isEmpty == false { + try? playQueue.seekToFirst() playCurrentTrackInQueue() } } - public var isPlaying: Bool { - state == .playing - } - - public var currentTrack: NFTTrack? { - try? playQueue.currentTrack() - } - - public func trackIsPlaying(_ track: NFTTrack) -> Bool { - currentTrack == track - } - public func trackIsDownloaded(_ track: NFTTrack) -> Bool { fileManager.fileExists(for: URL(string: track.audioUrl)!) } @@ -249,8 +251,8 @@ public class VLCAudioPlayer: ObservableObject { fileManager.clearFiles() } - public func removeDownloadedSong(_ song: NFTTrack) { - fileManager.clearFile(at: URL(string: song.audioUrl)!) + public func removeDownloadedSong(_ song: NFTTrack) async { + await fileManager.clearFile(at: URL(string: song.audioUrl)!) } deinit { diff --git a/iosApp/Modules/DI/ModuleLinker/Protocols/ErrorReporting.swift b/iosApp/Modules/DI/ModuleLinker/Protocols/ErrorReporting.swift index 4ff2916c..647d2590 100644 --- a/iosApp/Modules/DI/ModuleLinker/Protocols/ErrorReporting.swift +++ b/iosApp/Modules/DI/ModuleLinker/Protocols/ErrorReporting.swift @@ -1,6 +1,6 @@ import Foundation -public protocol ErrorReporting { +public protocol ErrorReporting: Sendable { func logError(_ error: String) func logError(_ error: Error) } diff --git a/iosApp/Modules/DI/ModuleLinker/Protocols/Library.swift b/iosApp/Modules/DI/ModuleLinker/Protocols/Library.swift index 0d13628b..014a78ee 100644 --- a/iosApp/Modules/DI/ModuleLinker/Protocols/Library.swift +++ b/iosApp/Modules/DI/ModuleLinker/Protocols/Library.swift @@ -2,5 +2,6 @@ import Foundation import SwiftUI public protocol LibraryViewProviding { + @MainActor func libraryView() -> AnyView } diff --git a/iosApp/Modules/DI/ModuleLinker/Protocols/NowPlaying.swift b/iosApp/Modules/DI/ModuleLinker/Protocols/NowPlaying.swift index dbc827b4..9b96ac92 100644 --- a/iosApp/Modules/DI/ModuleLinker/Protocols/NowPlaying.swift +++ b/iosApp/Modules/DI/ModuleLinker/Protocols/NowPlaying.swift @@ -5,6 +5,7 @@ public protocol NowPlayingViewProviding { func nowPlayingView() -> AnyView } +@MainActor public protocol MiniNowPlayingViewProviding { func miniNowPlayingView() -> AnyView } diff --git a/iosApp/Modules/Helpers/Analytics/AnalyticsModule.swift b/iosApp/Modules/Helpers/Analytics/AnalyticsModule.swift new file mode 100644 index 00000000..b2c3de81 --- /dev/null +++ b/iosApp/Modules/Helpers/Analytics/AnalyticsModule.swift @@ -0,0 +1,24 @@ +import ModuleLinker +import Foundation +import Resolver +import FirebaseCore + +final public class AnalyticsModule: Module { + public static var shared = AnalyticsModule() + private let newmAnalytics = NEWMAnalytics() + + init() { + FirebaseApp.configure() + NEWMAnalytics().setup() + } + + public func registerAllServices() { + + } + +#if DEBUG + public func registerAllMockedServices(mockResolver: Resolver) { + + } +#endif +} diff --git a/iosApp/Modules/Helpers/Analytics/NEWMAnalytics.swift b/iosApp/Modules/Helpers/Analytics/NEWMAnalytics.swift new file mode 100644 index 00000000..23288bfc --- /dev/null +++ b/iosApp/Modules/Helpers/Analytics/NEWMAnalytics.swift @@ -0,0 +1,46 @@ +@preconcurrency import shared +import FirebaseAnalytics +import Combine +import Resolver +import Foundation +import SharedExtensions + +public class NEWMAnalytics { + private var cancels: Set = [] + + func setup() { + NotificationCenter.default.publisher(for: shared.Notification().loginStateChanged) + .receive(on: DispatchQueue.main) + .sink { _ in + Task { @MainActor in + let user = Resolver.resolve(UserDetailsUseCase.self) + NEWMAnalytics.setUserId(userId: (try? await user.fetchLoggedInUserDetails().id) ?? "") + } + } + .store(in: &cancels) + + Task { @MainActor in + let user = Resolver.resolve(UserDetailsUseCase.self) + NEWMAnalytics.setUserId(userId: (try? await user.fetchLoggedInUserDetails().id) ?? "") + } + } + + static public func trackClickEvent(buttonName: String, screenName: String, properties: [String : Any]?) { + var properties = properties + properties?["button_name"] = buttonName + properties?["screen_name"] = screenName + Analytics.logEvent("button_click", parameters: properties) + } + + static public func trackEvent(eventName: String, properties: [String : Any]?) { + Analytics.logEvent(eventName, parameters: properties) + } + + static public func setUserId(userId: String) { + Analytics.setUserID(userId) + } + + static public func setUserProperty(propertyName: String, value: String) { + Analytics.setUserProperty(value, forName: propertyName) + } +} diff --git a/iosApp/Modules/Helpers/Files/FileManagerService.swift b/iosApp/Modules/Helpers/Files/FileManagerService.swift index 4ebde113..b7c531d4 100644 --- a/iosApp/Modules/Helpers/Files/FileManagerService.swift +++ b/iosApp/Modules/Helpers/Files/FileManagerService.swift @@ -50,13 +50,15 @@ public class FileManagerService: ObservableObject { } } - public func clearFile(at url: URL) { - do { - try FileManager.default.removeItem(at: try fileURL(forDownloadURL: url)) - objectWillChange.send() - } catch { - errorLogger.logError("Error clearing documents directory: \(error)") - } + public func clearFile(at url: URL) async { + await Task { + do { + try FileManager.default.removeItem(at: try fileURL(forDownloadURL: url)) + objectWillChange.send() + } catch { + errorLogger.logError("Error clearing documents directory: \(error)") + } + }.value } public func download(url: URL, progressHandler: @escaping ProgressHandler) async throws { diff --git a/iosApp/Modules/Helpers/Logging/SentryErrorReporter.swift b/iosApp/Modules/Helpers/Logging/SentryErrorReporter.swift index 3fd2a83f..1e6daada 100644 --- a/iosApp/Modules/Helpers/Logging/SentryErrorReporter.swift +++ b/iosApp/Modules/Helpers/Logging/SentryErrorReporter.swift @@ -6,12 +6,16 @@ import shared class SentryErrorReporter: ErrorReporting { func logError(_ error: String) { print("ERROR: \(error)") +#if !DEBUG SentrySDK.capture(error: error) +#endif } func logError(_ error: Error) { print("ERROR: \(error.kmmException?.description() ?? error)") +#if !DEBUG SentrySDK.capture(error: error.kmmException ?? error) +#endif } } diff --git a/iosApp/Modules/Helpers/SharedUI/XPubScannerView.swift b/iosApp/Modules/Helpers/SharedUI/ConnectWalletToAccountScannerView.swift similarity index 97% rename from iosApp/Modules/Helpers/SharedUI/XPubScannerView.swift rename to iosApp/Modules/Helpers/SharedUI/ConnectWalletToAccountScannerView.swift index b641d688..0b7abab5 100644 --- a/iosApp/Modules/Helpers/SharedUI/XPubScannerView.swift +++ b/iosApp/Modules/Helpers/SharedUI/ConnectWalletToAccountScannerView.swift @@ -1,9 +1,10 @@ import SwiftUI -import shared +@preconcurrency import shared import Resolver import ModuleLinker import Utilities import Colors +import Analytics @MainActor public struct ConnectWalletToAccountScannerView: View { @@ -60,6 +61,7 @@ public struct ConnectWalletToAccountScannerView: View { .loadingToast(shouldShow: $isLoading) .toast(shouldShow: $showCopiedToast, type: .copied) .background(.black) + .analyticsScreen(name: AppScreens.ConnectWalletScannerScreen().name) } @ViewBuilder diff --git a/iosApp/Modules/Helpers/SharedUI/QRCodeScannerView.swift b/iosApp/Modules/Helpers/SharedUI/QRCodeScannerView.swift index 5c90fcef..03af107a 100644 --- a/iosApp/Modules/Helpers/SharedUI/QRCodeScannerView.swift +++ b/iosApp/Modules/Helpers/SharedUI/QRCodeScannerView.swift @@ -44,28 +44,3 @@ public struct QRCodeScannerView: UIViewControllerRepresentable { public func updateUIViewController(_ uiViewController: QRCodeReaderViewController, context: Context) {} } - -private struct ContentView: View { - @State private var scannedCode: String? - - var body: some View { - VStack { - if let scannedCode = scannedCode { - Text("Scanned code is: \(scannedCode)") - } else { - QRCodeScannerView { - switch $0 { - case .success(let qrCode): - scannedCode = qrCode - case .failure: - fatalError() - } - } - } - } - } -} - -#Preview { - ContentView() -} diff --git a/iosApp/Modules/Helpers/SharedUI/Resources/Icon+Generated.swift b/iosApp/Modules/Helpers/SharedUI/Resources/Icon+Generated.swift index 7088dc85..7ba82206 100644 --- a/iosApp/Modules/Helpers/SharedUI/Resources/Icon+Generated.swift +++ b/iosApp/Modules/Helpers/SharedUI/Resources/Icon+Generated.swift @@ -52,9 +52,9 @@ public enum Asset { public static let royaltiesIcon = ImageAsset(name: "RoyaltiesIcon") public static let arrowSmallDown = ImageAsset(name: "arrow-small-down") public static let backArrow = ImageAsset(name: "back-arrow") - public static let checkboxCircleFill = ImageAsset(name: "checkbox-circle-fill") public static let checkmark = ImageAsset(name: "checkmark") public static let download = ImageAsset(name: "download") + public static let fileDownloadFill = ImageAsset(name: "file-download-fill") public static let logo = ImageAsset(name: "logo") public static let placeholder = ImageAsset(name: "placeholder") public static let playMiniFill = ImageAsset(name: "play-mini-fill") diff --git a/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/checkbox-circle-fill.imageset/checkbox-circle-fill.svg b/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/checkbox-circle-fill.imageset/checkbox-circle-fill.svg deleted file mode 100644 index bb15efd3..00000000 --- a/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/checkbox-circle-fill.imageset/checkbox-circle-fill.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/download.imageset/Contents.json b/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/download.imageset/Contents.json index cae44869..8af5b624 100644 --- a/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/download.imageset/Contents.json +++ b/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/download.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "file-download-fill.svg", + "filename" : "download-simple.svg", "idiom" : "universal" } ], diff --git a/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/download.imageset/download-simple.svg b/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/download.imageset/download-simple.svg new file mode 100644 index 00000000..f2f0c4a0 --- /dev/null +++ b/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/download.imageset/download-simple.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/checkbox-circle-fill.imageset/Contents.json b/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/file-download-fill.imageset/Contents.json similarity index 72% rename from iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/checkbox-circle-fill.imageset/Contents.json rename to iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/file-download-fill.imageset/Contents.json index 49f43cf5..cae44869 100644 --- a/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/checkbox-circle-fill.imageset/Contents.json +++ b/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/file-download-fill.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "checkbox-circle-fill.svg", + "filename" : "file-download-fill.svg", "idiom" : "universal" } ], diff --git a/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/download.imageset/file-download-fill.svg b/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/file-download-fill.imageset/file-download-fill.svg similarity index 80% rename from iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/download.imageset/file-download-fill.svg rename to iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/file-download-fill.imageset/file-download-fill.svg index 955facc5..1049aec2 100644 --- a/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/download.imageset/file-download-fill.svg +++ b/iosApp/Modules/Helpers/SharedUI/Resources/Media.xcassets/file-download-fill.imageset/file-download-fill.svg @@ -1,9 +1,9 @@ - - + + - + diff --git a/iosApp/Modules/Helpers/Utilities/NEWMError.swift b/iosApp/Modules/Helpers/Utilities/NEWMError.swift index 6024c673..f513ca29 100644 --- a/iosApp/Modules/Helpers/Utilities/NEWMError.swift +++ b/iosApp/Modules/Helpers/Utilities/NEWMError.swift @@ -55,7 +55,7 @@ public struct ErrorSet { mutating public func append(_ error: Error) { - errors.append(NEWMError(errorDescription: "\(error)", failureReason: nil, recoverySuggestion: nil, underlyingError: error)) + errors.append(NEWMError(errorDescription: "\(error.localizedDescription)", failureReason: nil, recoverySuggestion: nil, underlyingError: error)) } mutating diff --git a/iosApp/Modules/Resources/Colors/Resources/Assets.xcassets/Mobile Design System/System States/Success.colorset/Contents.json b/iosApp/Modules/Resources/Colors/Resources/Assets.xcassets/Mobile Design System/System States/Success.colorset/Contents.json new file mode 100644 index 00000000..f87b2a5c --- /dev/null +++ b/iosApp/Modules/Resources/Colors/Resources/Assets.xcassets/Mobile Design System/System States/Success.colorset/Contents.json @@ -0,0 +1,29 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x67", + "green" : "0xCD", + "red" : "0x68" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/Modules/Resources/Colors/Resources/Color+Generated.swift b/iosApp/Modules/Resources/Colors/Resources/Color+Generated.swift index e5384b9a..c419fc2d 100644 --- a/iosApp/Modules/Resources/Colors/Resources/Color+Generated.swift +++ b/iosApp/Modules/Resources/Colors/Resources/Color+Generated.swift @@ -54,6 +54,7 @@ public enum NEWMColor { public static let company = ColorAsset(name: "Company") public static let music = ColorAsset(name: "Music") public static let error = ColorAsset(name: "Error") + public static let success = ColorAsset(name: "Success") } // swiftlint:enable identifier_name line_length nesting type_body_length type_name diff --git a/iosApp/Modules/Screens/Library/LibraryModule.swift b/iosApp/Modules/Screens/Library/LibraryModule.swift index 159fb2ef..9933619b 100644 --- a/iosApp/Modules/Screens/Library/LibraryModule.swift +++ b/iosApp/Modules/Screens/Library/LibraryModule.swift @@ -26,6 +26,10 @@ public final class LibraryModule: Module { Resolver.register { DisconnectWalletUseCaseProvider().get() as DisconnectWalletUseCase } + + Resolver.register { + SyncWalletConnectionsUseCaseProvider().get() as SyncWalletConnectionsUseCase + } } } diff --git a/iosApp/Modules/Screens/Library/UI/LIbraryView.swift b/iosApp/Modules/Screens/Library/UI/LIbraryView.swift index 690d045b..35783b36 100644 --- a/iosApp/Modules/Screens/Library/UI/LIbraryView.swift +++ b/iosApp/Modules/Screens/Library/UI/LIbraryView.swift @@ -9,111 +9,145 @@ import ModuleLinker import Kingfisher import AudioPlayer import Mocks +import Analytics +import UIKit +@MainActor struct LibraryView: View { @StateObject private var viewModel = LibraryViewModel() - @State var showFilter = false - + @State private var showFilter = false + public var body: some View { - NavigationView { - Group { - if viewModel.showLoading { - loadingView - } else if viewModel.showNoSongsMessage { - noSongsMessage - } else { - loadedView + ZStack { + NavigationView { + Group { + if viewModel.showLoading { + loadingView + } else if viewModel.showNoSongsMessage { + noSongsMessage + } else { + loadedView + } } - } - .refreshable { - await viewModel.refresh() - } - .sheet(isPresented: .constant(viewModel.showCodeScanner), onDismiss: { - viewModel.scannerDismissed() - }) { - ConnectWalletToAccountScannerView { - viewModel.codeScanned() + .refreshable { + trackRefresh() + await viewModel.refresh() } - } - .sheet(isPresented: $showFilter) { - filterView - .presentationDetents([.height(350)]) - } - .alert(isPresented: .constant(viewModel.errors.currentError != nil), error: viewModel.errors.currentError) { - Button { - viewModel.errors.popFirstError() - } label: { - Text("Ok") + .sheet(isPresented: .constant(viewModel.showCodeScanner), onDismiss: { + viewModel.scannerDismissed() + }) { + ConnectWalletToAccountScannerView { + viewModel.codeScanned() + } } - } - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .topBarLeading) { - Text(String.library) - .font(.newmTitle1) - .foregroundStyle(Gradients.libraryGradient.gradient) + .sheet(isPresented: $showFilter) { + filterView + .presentationDetents([.height(350)]) + } + .alert(isPresented: .constant(viewModel.errors.currentError != nil), error: viewModel.errors.currentError) { + Button { + viewModel.errors.popFirstError() + } label: { + Text("Ok") + } } - if viewModel.filteredSortedTracks.isEmpty == false { - ToolbarItem(placement: .topBarTrailing) { - Button(action: { - showFilter = true - }, label: { - Image("Filter Icon") - .resizable() - .renderingMode(.template) - .frame(width: 30, height: 30) - }) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .topBarLeading) { + Text(String.library) + .font(.newmTitle1) + .foregroundStyle(Gradients.libraryGradient.gradient) + } + if viewModel.filteredSortedTracks.isEmpty == false { + ToolbarItem(placement: .topBarTrailing) { + Button(action: { + showFilter = true + }, label: { + Image("Filter Icon") + .resizable() + .renderingMode(.template) + .frame(width: 40, height: 40) + .foregroundStyle(Gradients.mainPrimary) + }) + } } } } + + Color.black + .opacity(showFilter ? 0.5 : 0) + .animation(.easeInOut, value: showFilter) + .ignoresSafeArea() } } + private func trackRefresh() { + NEWMAnalytics.trackClickEvent( + buttonName: AppScreens.NFTLibraryScreen().REFRESH_BUTTON, + screenName: AppScreens.NFTLibraryScreen().name, + properties: nil + ) + } + @ViewBuilder fileprivate var noSongsMessage: some View { - ZStack { - VStack { - Text("Your library is empty.") - .font( - Font.custom("Inter", size: 24) - .weight(.bold) - ) - .multilineTextAlignment(.center) - .foregroundColor(.white) - .frame(width: 358, alignment: .top) - .padding(.bottom) - - Text("Time to rescue it with your epic music stash!\nLet’s fill this up. 🎶") - .font( - Font.custom("Inter", size: 14) - .weight(.medium) - ) - .multilineTextAlignment(.center) - .foregroundColor(Color(red: 0.56, green: 0.56, blue: 0.57)) - .frame(width: 358, alignment: .top) - .padding(.bottom) - - if viewModel.walletIsConnected { - Link(destination: URL(string: "https://newm.io/recordstore")!) { - Text("Visit Record Store") - .frame(maxWidth: .infinity) - .padding() - .background(Gradients.mainPrimary.opacity(0.08)) - .foregroundColor(NEWMColor.midMusic.swiftUIColor) - .cornerRadius(8) + ScrollView { + ZStack { + VStack { + Text("Your library is empty.") + .font( + Font.custom("Inter", size: 24) + .weight(.bold) + ) + .multilineTextAlignment(.center) + .foregroundColor(.white) + .frame(width: 358, alignment: .top) + .padding(.bottom) + + Text("Time to rescue it with your epic music stash!\nLet’s fill this up. 🎶") + .font( + Font.custom("Inter", size: 14) + .weight(.medium) + ) + .multilineTextAlignment(.center) + .foregroundColor(Color(red: 0.56, green: 0.56, blue: 0.57)) + .frame(width: 358, alignment: .top) + .padding(.bottom) + + if viewModel.walletIsConnected { + Button { + trackRecordStore() + UIApplication.shared.open(URL(string: "https://newm.io/recordstore")!) + } label: { + Text("Visit Record Store") + .frame(maxWidth: .infinity) + .padding() + .background(Gradients.mainPrimary.opacity(0.08)) + .foregroundColor(NEWMColor.midMusic()) + .cornerRadius(8) + } } } - } - if viewModel.walletIsConnected == false { - VStack { - Spacer() - ConnectWalletAlertView { - viewModel.connectWallet() + if viewModel.walletIsConnected == false { + VStack { + Spacer() + ConnectWalletAlertView { + viewModel.connectWallet() + } } + .padding() } - .padding() } } + .analyticsScreen(name: AppScreens.NFTLibraryEmptyWalletScreen().name) + } + + private func trackRecordStore() { + NEWMAnalytics.trackClickEvent( + buttonName: AppScreens.NFTLibraryEmptyWalletScreen().VISIT_RECORDS_BUTTON, + screenName: AppScreens.NFTLibraryEmptyWalletScreen().name, + properties: nil + ) } @ViewBuilder @@ -124,80 +158,14 @@ struct LibraryView: View { @ViewBuilder private var loadedView: some View { - NavigationView { - List { - ForEach(viewModel.filteredSortedTracks, id: \.id) { audioTrack in - row(for: audioTrack) - .frame(height: 40) - .padding(.leading, -6) - .padding([.bottom, .top], -1) - .swipeActions(allowsFullSwipe: false) { - Button { - viewModel.swipeAction(for: audioTrack) - } label: { - Text(viewModel.swipeText(for: audioTrack)) - } - } - } - .listRowSeparator(.hidden) - } + List(viewModel.filteredSortedTracks) { track in + TrackRow(viewModel: TrackRowModel(track: track) { error in + viewModel.errors.append(error) + }) } + .listRowSeparator(.hidden) .searchable(text: $viewModel.searchText, prompt: "Search") - } - - @ViewBuilder - fileprivate func row(for track: NFTTrack) -> some View { - Button(action: { - viewModel.trackTapped(track) - }) { - HStack { - KFImage(URL(string: track.imageUrl)) - .placeholder { - Image.placeholder - .resizable() - .frame(width: 40, height: 40) - .clipShape(RoundedRectangle(cornerRadius: 4)) - } - .setProcessor(DownsamplingImageProcessor(size: CGSize(width: 40, height: 40))) - .clipShape(RoundedRectangle(cornerRadius: 4)) - - VStack(alignment: .leading, spacing: 3) { - Text(track.title) - .font(Font.interMedium(ofSize: 14)) - .foregroundStyle(viewModel.trackIsPlaying(track) ? NEWMColor.pink() : .white) - HStack(alignment: .center, spacing: 4) { - if viewModel.trackIsDownloaded(track) { - Asset.Media.checkboxCircleFill.swiftUIImage - } - Text(track.artists.first ?? "") - .font(Font.inter(ofSize: 12)) - .foregroundStyle(try! Color(hex: "8F8F91")) - } - } - .padding(.leading, 4) - - Spacer() - - progressView(for: track) - } - .foregroundStyle(.white) - .tag(track.id) - } - } - - @ViewBuilder - private func progressView(for track: NFTTrack) -> some View { - if let progress = viewModel.loadingProgress(for: track) { - if 0 < progress, progress < 1 { - Gauge(value: progress, in: 0...1) { } - .gaugeStyle(.accessoryCircularCapacity) - .scaleEffect(0.5) - } else { - ProgressView() - } - } else { - EmptyView() - } + .analyticsScreen(name: AppScreens.NFTLibraryScreen().name) } @ViewBuilder @@ -207,6 +175,7 @@ struct LibraryView: View { VStack(alignment: .leading, spacing: 20) { Text("Filter songs under") .foregroundColor(.white) + .font(.inter(ofSize: 14)) Button(action: { viewModel.toggleLengthFilter() @@ -227,7 +196,8 @@ struct LibraryView: View { Text("Sort by") .foregroundColor(.white) .padding(.top) - + .font(.inter(ofSize: 14)) + VStack(alignment: .leading, spacing: 10) { Button(action: { viewModel.titleSortTapped() @@ -238,6 +208,7 @@ struct LibraryView: View { .background(viewModel.titleSortSelected ? NEWMColor.midMusic().erased : Gradients.mainPrimaryLight.erased) .foregroundColor(viewModel.titleSortSelected ? .black : NEWMColor.midMusic.swiftUIColor) .cornerRadius(8) + .font(.interMedium(ofSize: 14)) } Button(action: { @@ -249,6 +220,7 @@ struct LibraryView: View { .background(viewModel.artistSortSelected ? NEWMColor.midMusic().erased : Gradients.mainPrimaryLight.erased) .foregroundColor(viewModel.artistSortSelected ? .black : NEWMColor.midMusic.swiftUIColor) .cornerRadius(8) + .font(.interMedium(ofSize: 14)) } Button(action: { @@ -260,12 +232,17 @@ struct LibraryView: View { .background(viewModel.durationSortSelected ? NEWMColor.midMusic().erased : Gradients.mainPrimaryLight.erased) .foregroundColor(viewModel.durationSortSelected ? .black : NEWMColor.midMusic.swiftUIColor) .cornerRadius(8) + .font(.interMedium(ofSize: 14)) } } } } .padding() + .padding(.top, 15) .background(Color.black) + .clipShape(RoundedRectangle(cornerSize: CGSize(width: 20, height: 20))) + .ignoresSafeArea() + .analyticsScreen(name: AppScreens.NFTLibraryFilterScreen().name) } @ViewBuilder @@ -297,45 +274,15 @@ struct LibraryView: View { } } -#if DEBUG -//#Preview { -// Resolver.root = .mock -// LibraryModule.shared.registerAllMockedServices(mockResolver: .root) -// MocksModule.shared.registerAllMockedServices(mockResolver: .mock) -// AudioPlayerModule.shared.registerAllServices() -// let useCase = $0.resolve(ConnectWalletUseCase.self) -// Resolver.root.register { -// Task { -// try await useCase.connect(walletConnectionId: "newm34r343g3g343833") -// } -// return useCase as ConnectWalletUseCase -// } -// return LibraryView() -// .preferredColorScheme(.dark) -// .tint(.white) -//} + +#if DEBUG #Preview { Resolver.root = .mock LibraryModule.shared.registerAllMockedServices(mockResolver: .mock) - return LibraryView(showFilter: false)//.noSongsMessage + return LibraryView()//.noSongsMessage .preferredColorScheme(.dark) .tint(.white) .background(.black) } #endif -// -//struct FilterView: View { -// @State var lengthFilterSelected = false -// @State var sortOption = "Title (A to Z)" -// @Environment(\.dismiss) var dismiss -// -// static let titleAscen -// -// var body: some View { -// } -//} -// -//#Preview { -// FilterView() -//} diff --git a/iosApp/Modules/Screens/Library/UI/LibraryModule+ViewProviding.swift b/iosApp/Modules/Screens/Library/UI/LibraryModule+ViewProviding.swift index 780a46e0..e8bdf4d7 100644 --- a/iosApp/Modules/Screens/Library/UI/LibraryModule+ViewProviding.swift +++ b/iosApp/Modules/Screens/Library/UI/LibraryModule+ViewProviding.swift @@ -3,6 +3,7 @@ import SwiftUI import ModuleLinker extension LibraryModule: LibraryViewProviding { + @MainActor public func libraryView() -> AnyView { LibraryView().erased } diff --git a/iosApp/Modules/Screens/Library/UI/LibraryViewActionHandling.swift b/iosApp/Modules/Screens/Library/UI/LibraryViewActionHandling.swift deleted file mode 100644 index 22deeaa7..00000000 --- a/iosApp/Modules/Screens/Library/UI/LibraryViewActionHandling.swift +++ /dev/null @@ -1,3 +0,0 @@ -protocol LibraryViewActionHandling { - func songTapped(id: String) -} diff --git a/iosApp/Modules/Screens/Library/UI/LibraryViewModel.swift b/iosApp/Modules/Screens/Library/UI/LibraryViewModel.swift index e8a7be6c..047cc7df 100644 --- a/iosApp/Modules/Screens/Library/UI/LibraryViewModel.swift +++ b/iosApp/Modules/Screens/Library/UI/LibraryViewModel.swift @@ -7,15 +7,23 @@ import shared import AudioPlayer import Utilities import SharedExtensions +import Analytics @MainActor class LibraryViewModel: ObservableObject { @Published var searchText: String = "" { didSet { + if oldValue.isEmpty { + trackSearchTapped() + } sortAndFilterTracks() } } @Published var errors = ErrorSet() + @Published private(set) var filteredSortedTracks: [NFTTrack] = [] + @Published private(set) var showLoading: Bool = true + @Published private(set) var showCodeScanner: Bool = false + var durationFilter: Int? { set { audioPlayer.durationFilter = newValue @@ -23,7 +31,7 @@ class LibraryViewModel: ObservableObject { } get { audioPlayer.durationFilter } } - + private(set) var selectedSort: Sort { set { audioPlayer.sort = newValue @@ -58,29 +66,29 @@ class LibraryViewModel: ObservableObject { } private func sortAndFilterTracks() { - filteredSortedTracks = tracks + let filteredSortedTracks = tracks .filter { track in - return track.isAboveDurationFilter(durationFilter) && + track.isAboveDurationFilter(durationFilter) && track.containsSearchText(searchText) } .sorted(by: selectedSort.comparator) + + self.filteredSortedTracks = filteredSortedTracks + audioPlayer.setTracks(Set(filteredSortedTracks), playFirstTrack: false) } - @Published private(set) var route: LibraryRoute? - @Published private(set) var filteredSortedTracks: [NFTTrack] = [] - @Published private var tracks: [NFTTrack] = [] { + private var tracks: [NFTTrack] = [] { didSet { sortAndFilterTracks() } } - @Published private(set) var showLoading: Bool = true - @Published private(set) var showCodeScanner: Bool = false var walletIsConnected: Bool = false private var cancels: Set = [] @LazyInjected private var walletNFTTracksUseCase: any WalletNFTTracksUseCase @LazyInjected private var hasWalletConnectionsUseCase: any HasWalletConnectionsUseCase + @LazyInjected private var syncWalletConnectionsUseCase: any SyncWalletConnectionsUseCase @InjectedObject private var audioPlayer: VLCAudioPlayer @LazyInjected private var logger: any ErrorReporting @@ -103,13 +111,6 @@ class LibraryViewModel: ObservableObject { } .store(in: &cancels) - audioPlayer.objectWillChange - .receive(on: DispatchQueue.main) - .sink { [weak self] _ in - self?.objectWillChange.send() - } - .store(in: &cancels) - Task { [weak self] in guard let self else { return } for await error in audioPlayer.errors.values { @@ -118,15 +119,16 @@ class LibraryViewModel: ObservableObject { } Task { [weak self] in + try await self?.syncWalletConnectionsUseCase.syncWalletConnectionsFromNetworkToDevice() self?.walletIsConnected = try await self?.hasWalletConnectionsUseCase.hasWalletConnections().boolValue == true await self?.refresh() } } - + var showNoSongsMessage: Bool { tracks.isEmpty || walletIsConnected == false } - + func refresh() async { defer { showLoading = false @@ -160,79 +162,6 @@ class LibraryViewModel: ObservableObject { showCodeScanner = true } - private func downloadTrack(_ track: NFTTrack) { - Task { - do { - try await audioPlayer.downloadTrack(track) - } catch { - let userDidCancelCode = -999 - if (error as NSError).code != userDidCancelCode { - self.errors.append(NEWMError(errorDescription: "Could not download \"\(track.title)\". Please try again later.")) - } - } - } - } - - func removeDownloadedTrack(_ track: NFTTrack) { - audioPlayer.removeDownloadedSong(track) - } - - func trackTapped(_ track: NFTTrack) { - if audioPlayer.playQueueIsEmpty { - audioPlayer.setTracks(Set(tracks), playFirstTrack: false) - } - audioPlayer.seek(toTrack: track) - } - - func swipeAction(for track: NFTTrack) { - switch downloadState(for: track) { - case .downloaded: - audioPlayer.removeDownloadedSong(track) - case .downloading: - audioPlayer.cancelDownload(track) - case nil: - downloadTrack(track) - } - } - - func swipeText(for track: NFTTrack) -> String { - switch downloadState(for: track) { - case .downloaded: - "Delete download" - case .downloading: - "Cancel" - case nil: - "Download" - } - } - - func loadingProgress(for track: NFTTrack) -> Double? { - audioPlayer.loadingProgress[track] - } - - func trackIsPlaying(_ track: NFTTrack) -> Bool { - audioPlayer.trackIsPlaying(track) - } - - func trackIsDownloaded(_ track: NFTTrack) -> Bool { - audioPlayer.trackIsDownloaded(track) - } - - private enum DownloadState { - case downloaded - case downloading - } - - private func downloadState(for track: NFTTrack) -> DownloadState? { - return if trackIsDownloaded(track) { - .downloaded - } else if loadingProgress(for: track) == nil { - nil - } else { - .downloading - } - } - func titleSortTapped() { guard case .title(let ascending) = selectedSort else { selectedSort = titleSort @@ -297,3 +226,15 @@ extension LibraryViewModel { } } } + +extension NFTTrack: Sendable {} + +extension LibraryViewModel { + func trackSearchTapped() { + NEWMAnalytics.trackClickEvent( + buttonName: AppScreens.NFTLibraryScreen().SEARCH_BUTTON, + screenName: AppScreens.NFTLibraryScreen().name, + properties: nil + ) + } +} diff --git a/iosApp/Modules/Screens/Library/UI/TrackRow.swift b/iosApp/Modules/Screens/Library/UI/TrackRow.swift new file mode 100644 index 00000000..e46ed987 --- /dev/null +++ b/iosApp/Modules/Screens/Library/UI/TrackRow.swift @@ -0,0 +1,83 @@ +import SwiftUI +import Kingfisher +import Colors +import SharedUI + +struct TrackRow: View { + @ObservedObject private var viewModel: TrackRowModel + + init(viewModel: TrackRowModel) { + self.viewModel = viewModel + } + + var body: some View { + Button(action: { + viewModel.trackTapped() + }) { + HStack { + KFImage(viewModel.imageUrl) + .placeholder { + Image.placeholder + .resizable() + .frame(width: 40, height: 40) + .clipShape(RoundedRectangle(cornerRadius: 4)) + } + .setProcessor(DownsamplingImageProcessor(size: CGSize(width: 40, height: 40))) + .clipShape(RoundedRectangle(cornerRadius: 4)) + + VStack(alignment: .leading, spacing: 3) { + Text(viewModel.title) + .font(Font.interMedium(ofSize: 14)) + .foregroundStyle(viewModel.isPlaying ? NEWMColor.success() : .white) + Text(viewModel.artist) + .font(Font.inter(ofSize: 12)) + .foregroundStyle(try! Color(hex: "8F8F91")) + } + .padding(.leading, 4) + + Spacer() + + downloadView() + .tint(NEWMColor.success()) + } + .padding() + } + .frame(height: 40) + .padding(.leading, -6) + .padding([.bottom, .top], -1) + .swipeActions { + Button { + Task { + await viewModel.swipeAction() + } + } label: { + VStack(alignment: .center) { + Text(viewModel.swipeText) + .font(.interMedium(ofSize: 12)) + } + } + .tint(NEWMColor.success()) + } + .id(viewModel.id) + } + + @ViewBuilder + private func downloadView() -> some View { + if viewModel.isDownloaded { + Asset.Media.fileDownloadFill() + .renderingMode(.template) + .foregroundStyle(NEWMColor.success()) + } else if let progress = viewModel.loadingProgress { + if 0 < progress, progress < 1 { + Gauge(value: progress, in: 0...1) { } + .gaugeStyle(.accessoryCircularCapacity) + .scaleEffect(0.5) + .padding(.trailing, -20) + } else { + ProgressView() + } + } else { + EmptyView() + } + } +} diff --git a/iosApp/Modules/Screens/Library/UI/TrackRowModel.swift b/iosApp/Modules/Screens/Library/UI/TrackRowModel.swift new file mode 100644 index 00000000..ea30c423 --- /dev/null +++ b/iosApp/Modules/Screens/Library/UI/TrackRowModel.swift @@ -0,0 +1,101 @@ +import Utilities +import Combine +import SwiftUI +import shared +import AudioPlayer +import Resolver + +@MainActor +class TrackRowModel: ObservableObject, Identifiable { + private enum DownloadState { + case downloaded + case downloading + } + + private let track: NFTTrack + @Injected private var audioPlayer: VLCAudioPlayer + private var cancels: Set = [] + + @Published var isPlaying: Bool = false + @Published var isDownloaded: Bool = false + @Published var loadingProgress: Double? + let didError: (Error) -> () + + var imageUrl: URL? { URL(string: track.imageUrl) } + var title: String { track.title } + var artist: String { track.artists.first ?? "" } + nonisolated var id: String { track.id } + var swipeText: String { + switch downloadState { + case .downloaded: + "Delete download" + case .downloading: + "Cancel" + case nil: + "Download" + } + } + private var downloadState: DownloadState? { + return if isDownloaded { + .downloaded + } else if loadingProgress != nil { + .downloading + } else { + nil + } + } + + init(track: NFTTrack, didError: @escaping (Error) -> ()) { + self.track = track + self.didError = didError + + isPlaying = audioPlayer.currentTrack == track + isDownloaded = audioPlayer.trackIsDownloaded(track) + loadingProgress = audioPlayer.loadingProgress[track] + + audioPlayer.$currentTrack.sink { [weak self] currentTrack in + guard let self else { return } + let isCurrentTrackPlaying = currentTrack == track + if isCurrentTrackPlaying != isPlaying { + isPlaying = isCurrentTrackPlaying + } + }.store(in: &cancels) + + audioPlayer.$loadingProgress.sink { [weak self] loadingProgress in + guard let self else { return } + if self.loadingProgress != loadingProgress[track] { + self.loadingProgress = loadingProgress[track] + self.isDownloaded = audioPlayer.trackIsDownloaded(track) + } + }.store(in: &cancels) + } + + func trackTapped() { + audioPlayer.seek(toTrack: track) + } + + func swipeAction() async { + switch downloadState { + case .downloaded: + await audioPlayer.removeDownloadedSong(track) + isDownloaded = false + case .downloading: + audioPlayer.cancelDownload(track) + case nil: + await downloadTrack() + } + } + + private func downloadTrack() async { + do { + try await audioPlayer.downloadTrack(track) + } catch { + let userDidCancelCode = -999 + if (error as NSError).code != userDidCancelCode { + didError(NEWMError(errorDescription: "Could not download \"\(track.title)\". Please try again later.")) + } + } + } +} + +extension NFTTrack: Identifiable {} diff --git a/iosApp/Modules/Screens/Login/UI/Navigation/LandingViewRoutes.swift b/iosApp/Modules/Screens/Login/UI/Navigation/LandingViewRoutes.swift index f0087d5f..9527bc33 100644 --- a/iosApp/Modules/Screens/Login/UI/Navigation/LandingViewRoutes.swift +++ b/iosApp/Modules/Screens/Login/UI/Navigation/LandingViewRoutes.swift @@ -2,8 +2,6 @@ enum LandingRoute { case login case createAccount case codeConfirmation - case nickname - case done case forgotPassword case enterNewPassword } diff --git a/iosApp/Modules/Screens/Login/UI/ViewModels/LandingViewModel.swift b/iosApp/Modules/Screens/Login/UI/ViewModels/LandingViewModel.swift index b0e5751a..4a4bdc7e 100644 --- a/iosApp/Modules/Screens/Login/UI/ViewModels/LandingViewModel.swift +++ b/iosApp/Modules/Screens/Login/UI/ViewModels/LandingViewModel.swift @@ -121,10 +121,6 @@ class LandingViewModel: ObservableObject { } } - func requestNickname() { - navPath.append(.nickname) - } - func registerUser() { isLoading = true Task { @@ -134,7 +130,6 @@ class LandingViewModel: ObservableObject { passwordConfirmation: confirmPassword, verificationCode: confirmationCode, humanVerificationCode: try await recaptcha.execute(withAction: HumanVerificationAction.register.recaptchaAction)) - navPath.append(.done) } catch { handleError(error) } diff --git a/iosApp/Modules/Screens/Login/UI/Views/CodeConfirmationView.swift b/iosApp/Modules/Screens/Login/UI/Views/CodeConfirmationView.swift index 5e2b8806..fe825576 100644 --- a/iosApp/Modules/Screens/Login/UI/Views/CodeConfirmationView.swift +++ b/iosApp/Modules/Screens/Login/UI/Views/CodeConfirmationView.swift @@ -2,6 +2,8 @@ import Foundation import SwiftUI import Colors import SharedUI +import Analytics +import shared extension LandingView { @ViewBuilder @@ -33,7 +35,8 @@ extension LandingView { .textContentType(.oneTimeCode) Button { - viewModel.requestNickname() + viewModel.registerUser() + trackNextButton() } label: { buttonText(.next) } @@ -46,6 +49,14 @@ extension LandingView { } .padding() } + .analyticsScreen(name: AppScreens.EmailVerificationScreen().name) + } + + private func trackNextButton() { + NEWMAnalytics.trackClickEvent( + buttonName: AppScreens.EmailVerificationScreen().CONTINUE_BUTTON, + screenName: AppScreens.EmailVerificationScreen().name, + properties: nil) } } diff --git a/iosApp/Modules/Screens/Login/UI/Views/LandingView.swift b/iosApp/Modules/Screens/Login/UI/Views/LandingView.swift index 198226f9..7001da54 100644 --- a/iosApp/Modules/Screens/Login/UI/Views/LandingView.swift +++ b/iosApp/Modules/Screens/Login/UI/Views/LandingView.swift @@ -25,10 +25,6 @@ public struct LandingView: View { createAccountView.backButton() case .codeConfirmation: codeConfirmationView.backButton() - case .nickname: - nicknameView.backButton() - case .done: - doneView case .login: loginView.backButton() case .forgotPassword: diff --git a/iosApp/Modules/Screens/NowPlaying/NowPlayingModule.swift b/iosApp/Modules/Screens/NowPlaying/NowPlayingModule.swift index 1604c13f..0533ed6a 100644 --- a/iosApp/Modules/Screens/NowPlaying/NowPlayingModule.swift +++ b/iosApp/Modules/Screens/NowPlaying/NowPlayingModule.swift @@ -24,6 +24,7 @@ extension NowPlayingModule: NowPlayingViewProviding { } extension NowPlayingModule: MiniNowPlayingViewProviding { + @MainActor public func miniNowPlayingView() -> AnyView { MiniPlayerView().erased } diff --git a/iosApp/Modules/Screens/NowPlaying/UI/MiniPlayerView.swift b/iosApp/Modules/Screens/NowPlaying/UI/MiniPlayerView.swift index b3abb6f8..e89de090 100644 --- a/iosApp/Modules/Screens/NowPlaying/UI/MiniPlayerView.swift +++ b/iosApp/Modules/Screens/NowPlaying/UI/MiniPlayerView.swift @@ -6,6 +6,7 @@ import ModuleLinker import Colors import Kingfisher +@MainActor struct MiniPlayerView: View { @InjectedObject private var audioPlayer: VLCAudioPlayer @@ -49,17 +50,16 @@ struct MiniPlayerView: View { @ViewBuilder private var image: some View { - if let image = audioPlayer.artworkUrl { - KFImage(image) - .setProcessor(DownsamplingImageProcessor(size: CGSize(width: iconSize, height: iconSize))) - .appendProcessor(RoundCornerImageProcessor(radius: Radius.point(4))) - .placeholder { - Image.placeholder - .resizable() - .frame(width: iconSize, height: iconSize) - .clipShape(RoundedRectangle(cornerRadius: 4)) - } - .padding(.trailing, 8) + AsyncImage(url: audioPlayer.artworkUrl) { image in + image + .resizable() + .frame(width: iconSize, height: iconSize) + .fixedSize() + } placeholder: { + Image.placeholder + .resizable() + .frame(width: iconSize, height: iconSize) + .clipShape(RoundedRectangle(cornerRadius: 4)) } } } diff --git a/iosApp/Modules/Screens/NowPlaying/UI/NowPlayingView.swift b/iosApp/Modules/Screens/NowPlaying/UI/NowPlayingView.swift index 157d0dd0..7c20243f 100644 --- a/iosApp/Modules/Screens/NowPlaying/UI/NowPlayingView.swift +++ b/iosApp/Modules/Screens/NowPlaying/UI/NowPlayingView.swift @@ -7,6 +7,7 @@ import AudioPlayer import shared import Kingfisher import Colors +import Analytics public struct NowPlayingView: View { typealias Seconds = Int @@ -24,6 +25,7 @@ public struct NowPlayingView: View { } .background(background) .backButton() + .analyticsScreen(name: AppScreens.MusicPlayerScreen().name) //TODO: make an "ErrorProviding" protocol, replace all these. // .alert(isPresented: .constant(audioPlayer.errors.currentError != nil), error: audioPlayer.errors.currentError) { // Button { @@ -40,6 +42,10 @@ extension NowPlayingView { @ViewBuilder private var background: some View { KFImage(audioPlayer.artworkUrl) + .placeholder { progress in + Gauge(value: progress.fractionCompleted, in: 0...1) { } + .gaugeStyle(.accessoryCircularCapacity) + } .resizable(resizingMode: .stretch) .scaledToFill() } @@ -56,9 +62,10 @@ extension NowPlayingView { private var playbackTimeBinding: Binding { Binding(get: { - Float(audioPlayer.currentTime ?? 0) + Float(audioPlayer.currentTime?.seconds ?? 0) }, set: { playbackTime in audioPlayer.seek(toTime: Double(playbackTime)) + trackSeek() }) } @@ -71,7 +78,7 @@ extension NowPlayingView { VStack { HStack { - Text("\(audioPlayer.currentTime.playbackTimeString)") + Text(audioPlayer.currentTime?.stringValue ?? "--:--") Spacer() Text("\(audioPlayer.duration.playbackTimeString)") } @@ -109,6 +116,7 @@ extension NowPlayingView { private var shuffleButton: some View { Button { audioPlayer.shuffle.toggle() + trackShuffle() } label: { if audioPlayer.shuffle { Image(systemName: "shuffle") @@ -125,6 +133,7 @@ extension NowPlayingView { private var prevButton: some View { Button { audioPlayer.prev() + trackPrev() } label: { Image(systemName: "backward.end.fill") .tint(.white) @@ -137,6 +146,7 @@ extension NowPlayingView { private var nextButton: some View { Button { audioPlayer.next() + trackNext() } label: { Image(systemName: "forward.end.fill") .tint(.white) @@ -149,6 +159,7 @@ extension NowPlayingView { private var repeatButton: some View { Button { audioPlayer.cycleRepeatMode() + trackRepeat() } label: { switch audioPlayer.repeatMode { case .all: @@ -165,6 +176,46 @@ extension NowPlayingView { .scaleEffect(CGSize(width: 1.5, height: 1.5)) } } + +extension NowPlayingView { + func trackShuffle() { + trackPlayerButton(AppScreens.MusicPlayerScreen().TOGGLE_SHUFFLE_BUTTON) + } + + func trackNext() { + trackPlayerButton(AppScreens.MusicPlayerScreen().NEXT_BUTTON) + } + + func trackPrev() { + trackPlayerButton(AppScreens.MusicPlayerScreen().PREVIOUS_BUTTON) + } + + func trackSeek() { + NEWMAnalytics.trackClickEvent( + buttonName: AppScreens.MusicPlayerScreen().SEEK_ACTION, + screenName: AppScreens.MusicPlayerScreen().name, + properties: [ + "song_id": audioPlayer.currentTrack?.id ?? "", + "position": audioPlayer.currentTime ?? "" + ] + ) + } + + func trackPlayerButton(_ name: String) { + NEWMAnalytics.trackClickEvent( + buttonName: name, + screenName: AppScreens.MusicPlayerScreen().name, + properties: [ + "song_id": audioPlayer.currentTrack?.id ?? "" + ] + ) + } + + func trackRepeat() { + trackPlayerButton(AppScreens.MusicPlayerScreen().REPEAT_BUTTON) + } +} + // //#Preview { // Resolver.root = Resolver(child: .main) diff --git a/iosApp/Modules/Screens/Profile/ProfileView.swift b/iosApp/Modules/Screens/Profile/ProfileView.swift index 37b4ebcf..f95d05ad 100644 --- a/iosApp/Modules/Screens/Profile/ProfileView.swift +++ b/iosApp/Modules/Screens/Profile/ProfileView.swift @@ -8,6 +8,7 @@ import Kingfisher import shared import Combine import Utilities +import Analytics public struct ProfileView: View { @StateObject private var viewModel = ProfileViewModel() @@ -23,6 +24,7 @@ public struct ProfileView: View { .autocorrectionDisabled(true) .scrollDismissesKeyboard(.immediately) .padding([.bottom, .leading, .trailing]) + .analyticsScreen(name: AppScreens.AccountScreen().name) } } diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 4013c621..2acaaec5 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -77,6 +77,8 @@ 762515262CA6A5820039C8BD /* shared.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 761ADD3A2AE734AA001B56CF /* shared.xcframework */; }; 7625152C2CA91FAD0039C8BD /* VLCAudioPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7625152B2CA91FAD0039C8BD /* VLCAudioPlayerTests.swift */; }; 7625D1532942B0740033C669 /* LibraryRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7625D1512942B0740033C669 /* LibraryRoutes.swift */; }; + 762B1CD72CCF28390093BD86 /* TrackRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762B1CD62CCF28390093BD86 /* TrackRow.swift */; }; + 762B1CD92CCF28660093BD86 /* TrackRowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762B1CD82CCF28660093BD86 /* TrackRowModel.swift */; }; 762EF9A92B1F90C7009E08D8 /* AudioPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76C76132291F445B0054D2F3 /* AudioPlayer.framework */; }; 762EF9B52B21B30C009E08D8 /* DebugView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762EF9B42B21B30C009E08D8 /* DebugView.swift */; }; 762EF9B72B21B412009E08D8 /* ShakeResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762EF9B62B21B412009E08D8 /* ShakeResponder.swift */; }; @@ -89,7 +91,7 @@ 7639E6732AEB347F00254D71 /* GoogleSignInSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7639E6722AEB347F00254D71 /* GoogleSignInSwift */; }; 7639E6752AEB348800254D71 /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 7639E6742AEB348800254D71 /* GoogleSignIn */; }; 763B987D2B28644B00E6F492 /* (null) in Sources */ = {isa = PBXBuildFile; }; - 7642EB722B17CE8F009C8999 /* VLCKitSPM in Frameworks */ = {isa = PBXBuildFile; productRef = 7642EB712B17CE8F009C8999 /* VLCKitSPM */; }; + 763BF6F62CBB2D94003F5DFF /* shared.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 761ADD3A2AE734AA001B56CF /* shared.xcframework */; }; 76452A382B5A2C760001F47E /* ModuleLinker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76D01D9E28164B2100305605 /* ModuleLinker.framework */; }; 76452A4E2B5A37D50001F47E /* SharedExtensions.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76452A482B5A37D50001F47E /* SharedExtensions.framework */; }; 76452A4F2B5A37D50001F47E /* SharedExtensions.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 76452A482B5A37D50001F47E /* SharedExtensions.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -129,7 +131,7 @@ 7650E13E2B1798AA00D686B8 /* QRCodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7650E13D2B1798AA00D686B8 /* QRCodeScannerView.swift */; }; 7650E1412B1798E400D686B8 /* QRCodeReader in Frameworks */ = {isa = PBXBuildFile; productRef = 7650E1402B1798E400D686B8 /* QRCodeReader */; }; 7650E1432B1799E800D686B8 /* ConnectWalletAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7650E1422B1799E800D686B8 /* ConnectWalletAlertView.swift */; }; - 7650E1452B17A0F300D686B8 /* XPubScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7650E1442B17A0F300D686B8 /* XPubScannerView.swift */; }; + 7650E1452B17A0F300D686B8 /* ConnectWalletToAccountScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7650E1442B17A0F300D686B8 /* ConnectWalletToAccountScannerView.swift */; }; 7650E1522B17B3FA00D686B8 /* Profile.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7650E14B2B17B3FA00D686B8 /* Profile.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7650E1562B17B43900D686B8 /* Profile.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7650E14B2B17B3FA00D686B8 /* Profile.framework */; }; 7650E1592B17B44E00D686B8 /* ModuleLinker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76D01D9E28164B2100305605 /* ModuleLinker.framework */; platformFilter = ios; }; @@ -194,8 +196,24 @@ 765A366629E918CF00B939BD /* SharedUIModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 765A364729E918CF00B939BD /* SharedUIModule.swift */; }; 765D1615293EF8F70021FEC7 /* Gradients.swift in Sources */ = {isa = PBXBuildFile; fileRef = 765D1614293EF8F70021FEC7 /* Gradients.swift */; }; 765D1616293EFAE70021FEC7 /* Colors.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7618FD1728164CBD007573B4 /* Colors.framework */; }; - 765E35242B32458400AB3AF1 /* SentrySwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 765E35232B32458400AB3AF1 /* SentrySwiftUI */; }; 765E35262B3245C000AB3AF1 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 765E35252B3245C000AB3AF1 /* Sentry */; }; + 76610ABB2CBB45EB009F5693 /* Analytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76610AA82CBB45CE009F5693 /* Analytics.framework */; }; + 76610AC02CBB45F3009F5693 /* Analytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76610AA82CBB45CE009F5693 /* Analytics.framework */; }; + 76610AC52CBB45F8009F5693 /* Analytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76610AA82CBB45CE009F5693 /* Analytics.framework */; }; + 76610ACA2CBB45FC009F5693 /* Analytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76610AA82CBB45CE009F5693 /* Analytics.framework */; }; + 76610ACF2CBB45FF009F5693 /* Analytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76610AA82CBB45CE009F5693 /* Analytics.framework */; }; + 76610AD62CBB4666009F5693 /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 76610AD52CBB4666009F5693 /* FirebaseCore */; }; + 76610AD72CBB467C009F5693 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7655418F2CBB011500A0D425 /* GoogleService-Info.plist */; }; + 76610AD82CBB46D6009F5693 /* Analytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76610AA82CBB45CE009F5693 /* Analytics.framework */; }; + 76610AD92CBB46D6009F5693 /* Analytics.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 76610AA82CBB45CE009F5693 /* Analytics.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 76610ADC2CBB471C009F5693 /* Analytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76610AA82CBB45CE009F5693 /* Analytics.framework */; }; + 76610AF02CBB4A1A009F5693 /* FirebaseAnalyticsWithoutAdIdSupport in Frameworks */ = {isa = PBXBuildFile; productRef = 76610AEF2CBB4A1A009F5693 /* FirebaseAnalyticsWithoutAdIdSupport */; }; + 76610AF12CBB4A1E009F5693 /* ModuleLinker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76D01D9E28164B2100305605 /* ModuleLinker.framework */; }; + 76610AF72CBB4AE3009F5693 /* Resolver in Frameworks */ = {isa = PBXBuildFile; productRef = 76610AF62CBB4AE3009F5693 /* Resolver */; }; + 76610AFA2CBB4C6A009F5693 /* Analytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76610AA82CBB45CE009F5693 /* Analytics.framework */; platformFilter = ios; }; + 76610B002CBB4F9C009F5693 /* shared.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 761ADD3A2AE734AA001B56CF /* shared.xcframework */; }; + 76610B062CBB51D9009F5693 /* SharedExtensions.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76452A482B5A37D50001F47E /* SharedExtensions.framework */; }; + 76699D0A2CD2BA5400A0182C /* VLCKitSPM in Frameworks */ = {isa = PBXBuildFile; productRef = 76699D092CD2BA5400A0182C /* VLCKitSPM */; }; 766B50322C3A60F70030BDF3 /* MockHasWalletConnectionsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 766B50312C3A60F70030BDF3 /* MockHasWalletConnectionsUseCase.swift */; }; 766B50332C3B035D0030BDF3 /* Colors.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7618FD1728164CBD007573B4 /* Colors.framework */; }; 766B503A2C3B10A40030BDF3 /* SwiftUIToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 766B50392C3B10A40030BDF3 /* SwiftUIToolbar.swift */; }; @@ -219,7 +237,6 @@ 76829E232B5775D7007BCED5 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 76829E222B5775D7007BCED5 /* Kingfisher */; }; 7684E24E27DACBC400BDD21B /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7684E24D27DACBC400BDD21B /* SplashScreen.storyboard */; }; 768B49912903B89F0099911D /* LibraryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 768B49822903B89F0099911D /* LibraryViewModel.swift */; }; - 768B49932903B89F0099911D /* LibraryViewActionHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 768B49852903B89F0099911D /* LibraryViewActionHandling.swift */; }; 768B49942903B89F0099911D /* LIbraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 768B49862903B89F0099911D /* LIbraryView.swift */; }; 768B49962903B89F0099911D /* LibraryModule+ViewProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 768B49882903B89F0099911D /* LibraryModule+ViewProviding.swift */; }; 768B49972903B89F0099911D /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 768B498A2903B89F0099911D /* Strings.swift */; }; @@ -238,7 +255,6 @@ 768C69C32B5CDA30001A1B1A /* MockUserDetailsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 768C69C22B5CDA30001A1B1A /* MockUserDetailsUseCase.swift */; }; 769BB6882B5E0B85007CA421 /* MockChangePasswordUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 769BB6872B5E0B85007CA421 /* MockChangePasswordUseCase.swift */; }; 769BB6892B60B8C6007CA421 /* Utilities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76452A5F2B5A395A0001F47E /* Utilities.framework */; }; - 769F4B5B2B04C34F009D23C6 /* VLCKitSPM in Frameworks */ = {isa = PBXBuildFile; productRef = 769F4B5A2B04C34F009D23C6 /* VLCKitSPM */; }; 76A223FE281FB43900F7188A /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76A223FD281FB43900F7188A /* MockData.swift */; }; 76AB881D2B2AC6D100D8612A /* shared.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 761ADD3A2AE734AA001B56CF /* shared.xcframework */; }; 76AB88202B2ACB7400D8612A /* Profile.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7650E14B2B17B3FA00D686B8 /* Profile.framework */; }; @@ -285,6 +301,7 @@ 76D99ECB2B1B0D24002FB4B2 /* TabBarProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7618FCFC28164C92007573B4 /* TabBarProvider.swift */; }; 76D99ECC2B1B0D24002FB4B2 /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7618FCF928164C92007573B4 /* TabBar.swift */; }; 76D99ECD2B1B0D24002FB4B2 /* TabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7618FCFB28164C92007573B4 /* TabBarItem.swift */; }; + 76DA82BE2CC5FBFA005355D4 /* SentrySwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 765E35232B32458400AB3AF1 /* SentrySwiftUI */; }; 76DB3B942A1311F5001AF2AD /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76DB3B932A1311F5001AF2AD /* ActionButton.swift */; }; 76DC3A2928169995004D5021 /* Login.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76DC3A2828169995004D5021 /* Login.swift */; }; 76DC3A36281A0E76004D5021 /* Resolver in Frameworks */ = {isa = PBXBuildFile; productRef = 76DC3A35281A0E76004D5021 /* Resolver */; }; @@ -300,6 +317,7 @@ 76EAFC582A09087A00ACD862 /* DMSerifText-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 76EAFC542A0907FB00ACD862 /* DMSerifText-Regular.ttf */; }; 76EAFC592A09087A00ACD862 /* DMSerifText-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 76EAFC552A0907FB00ACD862 /* DMSerifText-Italic.ttf */; }; 76EAFC742A11657800ACD862 /* HeaderImageSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7648DE04292C69EF006F7259 /* HeaderImageSection.swift */; }; + 76F5F2E42CD2D64D00143640 /* VLCKitSPM in Frameworks */ = {isa = PBXBuildFile; productRef = 76F5F2E32CD2D64D00143640 /* VLCKitSPM */; }; 76F823C528BEC2B3003A524C /* Resolver in Frameworks */ = {isa = PBXBuildFile; productRef = 76F823C428BEC2B3003A524C /* Resolver */; }; 76F8E8F62BA42E470013595D /* Filters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76F8E8F52BA42E470013595D /* Filters.swift */; }; 76F8E8F82BA42E740013595D /* RepeatMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76F8E8F72BA42E740013595D /* RepeatMode.swift */; }; @@ -655,6 +673,76 @@ remoteGlobalIDString = 76D01D9D28164B2100305605; remoteInfo = ModuleLinker; }; + 76610ABD2CBB45EB009F5693 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 76610AA72CBB45CE009F5693; + remoteInfo = Analytics; + }; + 76610AC22CBB45F3009F5693 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 76610AA72CBB45CE009F5693; + remoteInfo = Analytics; + }; + 76610AC72CBB45F8009F5693 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 76610AA72CBB45CE009F5693; + remoteInfo = Analytics; + }; + 76610ACC2CBB45FC009F5693 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 76610AA72CBB45CE009F5693; + remoteInfo = Analytics; + }; + 76610AD12CBB45FF009F5693 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 76610AA72CBB45CE009F5693; + remoteInfo = Analytics; + }; + 76610ADA2CBB46D6009F5693 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 76610AA72CBB45CE009F5693; + remoteInfo = Analytics; + }; + 76610ADE2CBB471C009F5693 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 76610AA72CBB45CE009F5693; + remoteInfo = Analytics; + }; + 76610AF32CBB4A1E009F5693 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 76D01D9D28164B2100305605; + remoteInfo = ModuleLinker; + }; + 76610AFC2CBB4C6A009F5693 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 76610AA72CBB45CE009F5693; + remoteInfo = Analytics; + }; + 76610B082CBB51D9009F5693 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 76452A472B5A37D50001F47E; + remoteInfo = SharedExtensions; + }; 766B50352C3B035D0030BDF3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7555FF73242A565900829871 /* Project object */; @@ -902,6 +990,7 @@ 7618FD9828164DBB007573B4 /* NowPlaying.framework in Embed Frameworks */, 7618FD7828164DA2007573B4 /* Login.framework in Embed Frameworks */, 76C8502D2AEB37E200C2C388 /* Library.framework in Embed Frameworks */, + 76610AD92CBB46D6009F5693 /* Analytics.framework in Embed Frameworks */, 7618FD3828164CE2007573B4 /* Fonts.framework in Embed Frameworks */, 761E973D2B6BC3B100FDFC5E /* Mocks.framework in Embed Frameworks */, 7618FD1E28164CBD007573B4 /* Colors.framework in Embed Frameworks */, @@ -1000,7 +1089,6 @@ 761ADD3A2AE734AA001B56CF /* shared.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = shared.xcframework; path = ../shared/build/XCFrameworks/debug/shared.xcframework; sourceTree = ""; }; 761ADD412AE88A56001B56CF /* Notification+KMM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+KMM.swift"; sourceTree = ""; }; 761ADD452AEA0EAA001B56CF /* LandingViewModel+ErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LandingViewModel+ErrorHandling.swift"; sourceTree = ""; }; - 761E96FE2B6A60A300FDFC5E /* build.gradle.kts */ = {isa = PBXFileReference; lastKnownFileType = text; name = build.gradle.kts; path = ../shared/build.gradle.kts; sourceTree = ""; }; 761E97112B6A7C0500FDFC5E /* MockLoginUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLoginUseCase.swift; sourceTree = ""; }; 761E97132B6A7CAA00FDFC5E /* MockSignUpUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSignUpUseCase.swift; sourceTree = ""; }; 761E971A2B6A8EDA00FDFC5E /* MocksModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MocksModule.swift; sourceTree = ""; }; @@ -1012,6 +1100,8 @@ 7625D14D29429CCC0033C669 /* Filters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filters.swift; sourceTree = ""; }; 7625D1512942B0740033C669 /* LibraryRoutes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LibraryRoutes.swift; sourceTree = ""; }; 7627329128295EA5009D3C8A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 762B1CD62CCF28390093BD86 /* TrackRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackRow.swift; sourceTree = ""; }; + 762B1CD82CCF28660093BD86 /* TrackRowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackRowModel.swift; sourceTree = ""; }; 762EF9B42B21B30C009E08D8 /* DebugView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugView.swift; sourceTree = ""; }; 762EF9B62B21B412009E08D8 /* ShakeResponder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShakeResponder.swift; sourceTree = ""; }; 76308A872B7B1D16002FDC50 /* UserDefaultsPropertyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsPropertyWrapper.swift; sourceTree = ""; }; @@ -1049,7 +1139,7 @@ 7650E1322B15ECE400D686B8 /* FileManagerService+NFTTrack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManagerService+NFTTrack.swift"; sourceTree = ""; }; 7650E13D2B1798AA00D686B8 /* QRCodeScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScannerView.swift; sourceTree = ""; }; 7650E1422B1799E800D686B8 /* ConnectWalletAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectWalletAlertView.swift; sourceTree = ""; }; - 7650E1442B17A0F300D686B8 /* XPubScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPubScannerView.swift; sourceTree = ""; }; + 7650E1442B17A0F300D686B8 /* ConnectWalletToAccountScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectWalletToAccountScannerView.swift; sourceTree = ""; }; 7650E14B2B17B3FA00D686B8 /* Profile.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Profile.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7650E1602B17B51300D686B8 /* ProfileModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModule.swift; sourceTree = ""; }; 765161972C8E35D500E79860 /* NFTTrack+FilterAndSort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NFTTrack+FilterAndSort.swift"; sourceTree = ""; }; @@ -1057,6 +1147,7 @@ 765161A02C8E368C00E79860 /* LibraryFilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryFilterTests.swift; sourceTree = ""; }; 765161A82C8E39E900E79860 /* Library.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = Library.xctestplan; path = Tests/LibraryTests/Library.xctestplan; sourceTree = ""; }; 7651D6D327DFA33100FF41B3 /* iOSApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 7655418F2CBB011500A0D425 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 76562514284BEF30004B6E4B /* Library.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Library.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 76562526284BEFB7004B6E4B /* Library.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Library.swift; sourceTree = ""; }; 7656253F284BFBD1004B6E4B /* HomeRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeRoutes.swift; sourceTree = ""; }; @@ -1106,6 +1197,7 @@ 765D1605293EF22F0021FEC7 /* MarketplaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketplaceView.swift; sourceTree = ""; }; 765D1612293EF6DD0021FEC7 /* MarketplaceViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketplaceViewModel.swift; sourceTree = ""; }; 765D1614293EF8F70021FEC7 /* Gradients.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gradients.swift; sourceTree = ""; }; + 76610AA82CBB45CE009F5693 /* Analytics.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Analytics.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7669071229088D2A00D9F989 /* VLCAudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCAudioPlayer.swift; sourceTree = ""; }; 766B1EC62A15C74B0083DE3E /* KeyboardObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardObserver.swift; sourceTree = ""; }; 766B50312C3A60F70030BDF3 /* MockHasWalletConnectionsUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHasWalletConnectionsUseCase.swift; sourceTree = ""; }; @@ -1120,7 +1212,6 @@ 76829E202B5774C0007BCED5 /* NFTTrack+VLCMedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NFTTrack+VLCMedia.swift"; sourceTree = ""; }; 7684E24D27DACBC400BDD21B /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = SplashScreen.storyboard; sourceTree = ""; }; 768B49822903B89F0099911D /* LibraryViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LibraryViewModel.swift; sourceTree = ""; }; - 768B49852903B89F0099911D /* LibraryViewActionHandling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LibraryViewActionHandling.swift; sourceTree = ""; }; 768B49862903B89F0099911D /* LIbraryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LIbraryView.swift; sourceTree = ""; }; 768B49882903B89F0099911D /* LibraryModule+ViewProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LibraryModule+ViewProviding.swift"; sourceTree = ""; }; 768B498A2903B89F0099911D /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; @@ -1190,8 +1281,6 @@ 76D01D9E28164B2100305605 /* ModuleLinker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ModuleLinker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 76D09EFB27FAC4B700E2BAD3 /* iOSAppModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSAppModule.swift; sourceTree = ""; }; 76D1947E2B7FFD40003EA9A5 /* EnvironmentVariables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentVariables.swift; sourceTree = ""; }; - 76D99EC72B1934CC002FB4B2 /* commonMain */ = {isa = PBXFileReference; lastKnownFileType = folder; name = commonMain; path = ../shared/src/commonMain; sourceTree = ""; }; - 76D99EC82B1934CC002FB4B2 /* iosMain */ = {isa = PBXFileReference; lastKnownFileType = folder; name = iosMain; path = ../shared/src/iosMain; sourceTree = ""; }; 76DB3B932A1311F5001AF2AD /* ActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = ""; }; 76DB3B972A13252F001AF2AD /* String+Empty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Empty.swift"; sourceTree = ""; }; 76DC3A2828169995004D5021 /* Login.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Login.swift; sourceTree = ""; }; @@ -1241,12 +1330,16 @@ 76FFD12E2B95CA1700D77309 /* VLCMediaState+CustomStringConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VLCMediaState+CustomStringConvertible.swift"; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 76610AA92CBB45CE009F5693 /* Analytics */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Analytics; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 7555FF78242A565900829871 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7642EB722B17CE8F009C8999 /* VLCKitSPM in Frameworks */, + 76DA82BE2CC5FBFA005355D4 /* SentrySwiftUI in Frameworks */, 76452A4E2B5A37D50001F47E /* SharedExtensions.framework in Frameworks */, 7650E16D2B17CACE00D686B8 /* shared.xcframework in Frameworks */, 7650E1562B17B43900D686B8 /* Profile.framework in Frameworks */, @@ -1261,11 +1354,12 @@ 7618FD3728164CE2007573B4 /* Fonts.framework in Frameworks */, 76C8502C2AEB37E200C2C388 /* Library.framework in Frameworks */, 7650E1762B17CB3600D686B8 /* QRCodeReader in Frameworks */, + 76610AD82CBB46D6009F5693 /* Analytics.framework in Frameworks */, 76D01D7F28164AE700305605 /* Main.framework in Frameworks */, - 765E35242B32458400AB3AF1 /* SentrySwiftUI in Frameworks */, 76CBA3A92B14CE2D0088334F /* libFiles.a in Frameworks */, 7618FCD328164C5C007573B4 /* SharedUI.framework in Frameworks */, 7618FD1D28164CBD007573B4 /* Colors.framework in Frameworks */, + 76F5F2E42CD2D64D00143640 /* VLCKitSPM in Frameworks */, 76F823C528BEC2B3003A524C /* Resolver in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1288,6 +1382,7 @@ 76DC3A46281A0E9D004D5021 /* Resolver in Frameworks */, 769BB6892B60B8C6007CA421 /* Utilities.framework in Frameworks */, 7650E1412B1798E400D686B8 /* QRCodeReader in Frameworks */, + 76610ADC2CBB471C009F5693 /* Analytics.framework in Frameworks */, 7603003A2836FCB800BBF78C /* Fonts.framework in Frameworks */, 766E8FA228562CF200F18779 /* Colors.framework in Frameworks */, 7618FE1F28164E83007573B4 /* ModuleLinker.framework in Frameworks */, @@ -1317,11 +1412,13 @@ 76DC3A3A281A0E80004D5021 /* Resolver in Frameworks */, 7639E6752AEB348800254D71 /* GoogleSignIn in Frameworks */, 766E8FA828562EB300F18779 /* Fonts.framework in Frameworks */, + 76610ACA2CBB45FC009F5693 /* Analytics.framework in Frameworks */, 76452A6D2B5A39A60001F47E /* Utilities.framework in Frameworks */, 7639E6732AEB347F00254D71 /* GoogleSignInSwift in Frameworks */, 766E8FAC28562EBF00F18779 /* Colors.framework in Frameworks */, 76325CD628A13BED003836C7 /* SharedUI.framework in Frameworks */, 76452A722B5A39F10001F47E /* SharedExtensions.framework in Frameworks */, + 763BF6F62CBB2D94003F5DFF /* shared.xcframework in Frameworks */, 761E96FF2B6A79D000FDFC5E /* Mocks.framework in Frameworks */, 7618FDFF28164E67007573B4 /* ModuleLinker.framework in Frameworks */, 76D194882B8002AC003EA9A5 /* RecaptchaEnterprise in Frameworks */, @@ -1349,6 +1446,7 @@ 7650E1392B166A5700D686B8 /* Kingfisher in Frameworks */, 76452A982B5A3DEE0001F47E /* Utilities.framework in Frameworks */, 766E8FD828562FC800F18779 /* Colors.framework in Frameworks */, + 76610AC02CBB45F3009F5693 /* Analytics.framework in Frameworks */, 76DC3A36281A0E76004D5021 /* Resolver in Frameworks */, 76C76142291F4D340054D2F3 /* AudioPlayer.framework in Frameworks */, 7618FDF128164E59007573B4 /* ModuleLinker.framework in Frameworks */, @@ -1408,6 +1506,7 @@ 761E97152B6A88B600FDFC5E /* SharedExtensions.framework in Frameworks */, 76D99EC92B19EF11002FB4B2 /* shared.xcframework in Frameworks */, 7650E15F2B17B46800D686B8 /* Resolver in Frameworks */, + 76610ABB2CBB45EB009F5693 /* Analytics.framework in Frameworks */, 7650E1592B17B44E00D686B8 /* ModuleLinker.framework in Frameworks */, 766B50332C3B035D0030BDF3 /* Colors.framework in Frameworks */, 7650E1682B17C69A00D686B8 /* SharedUI.framework in Frameworks */, @@ -1437,6 +1536,20 @@ 76562525284BEF6C004B6E4B /* Resolver in Frameworks */, 7656251F284BEF68004B6E4B /* ModuleLinker.framework in Frameworks */, 76308A892B7B46C2002FDC50 /* Mocks.framework in Frameworks */, + 76610ACF2CBB45FF009F5693 /* Analytics.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 76610AA52CBB45CE009F5693 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 76610AD62CBB4666009F5693 /* FirebaseCore in Frameworks */, + 76610B062CBB51D9009F5693 /* SharedExtensions.framework in Frameworks */, + 76610B002CBB4F9C009F5693 /* shared.xcframework in Frameworks */, + 76610AF02CBB4A1A009F5693 /* FirebaseAnalyticsWithoutAdIdSupport in Frameworks */, + 76610AF72CBB4AE3009F5693 /* Resolver in Frameworks */, + 76610AF12CBB4A1E009F5693 /* ModuleLinker.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1472,7 +1585,8 @@ 76F8E8F92BA5A0A50013595D /* Mocks.framework in Frameworks */, 76829E232B5775D7007BCED5 /* Kingfisher in Frameworks */, 76AB881D2B2AC6D100D8612A /* shared.xcframework in Frameworks */, - 769F4B5B2B04C34F009D23C6 /* VLCKitSPM in Frameworks */, + 76610AFA2CBB4C6A009F5693 /* Analytics.framework in Frameworks */, + 76699D0A2CD2BA5400A0182C /* VLCKitSPM in Frameworks */, 76C7614E291F56DF0054D2F3 /* Resolver in Frameworks */, 76FFD1142B95492F00D77309 /* OrderedCollections in Frameworks */, 76C7614F291F56F30054D2F3 /* ModuleLinker.framework in Frameworks */, @@ -1486,6 +1600,7 @@ 76DC3A48281A0EA2004D5021 /* Resolver in Frameworks */, 76452A7B2B5A3A860001F47E /* Utilities.framework in Frameworks */, 760808C02B279E490015A2ED /* SentrySwiftUI in Frameworks */, + 76610AC52CBB45F8009F5693 /* Analytics.framework in Frameworks */, 76D01DC128164B6E00305605 /* ModuleLinker.framework in Frameworks */, 76AB88202B2ACB7400D8612A /* Profile.framework in Frameworks */, 762EF9A92B1F90C7009E08D8 /* AudioPlayer.framework in Frameworks */, @@ -1538,8 +1653,8 @@ 7555FF72242A565900829871 = { isa = PBXGroup; children = ( + 7655418F2CBB011500A0D425 /* GoogleService-Info.plist */, 765161A82C8E39E900E79860 /* Library.xctestplan */, - 76D99EC62B1934AD002FB4B2 /* Kotlin */, 761ADD3A2AE734AA001B56CF /* shared.xcframework */, 76EAFC5C2A0AE33E00ACD862 /* Secrets.xcconfig */, 76B990BB2835203300F818E4 /* Fonts-Info.plist */, @@ -1579,6 +1694,7 @@ 76FFD1192B9554DF00D77309 /* AudioPlayerTests.xctest */, 76F8E9022BA5AC5D0013595D /* NowPlayingApp.app */, 7651619E2C8E368C00E79860 /* LibraryTests.xctest */, + 76610AA82CBB45CE009F5693 /* Analytics.framework */, ); name = Products; sourceTree = ""; @@ -1839,6 +1955,7 @@ 7627EA3827FA685E00C0F7DA /* Helpers */ = { isa = PBXGroup; children = ( + 76610AA92CBB45CE009F5693 /* Analytics */, 760808A12B2799C00015A2ED /* Logging */, 76CE046529EE3F13002AF94B /* Utilities */, 76AA88E22A37B56600F25113 /* API */, @@ -2119,7 +2236,7 @@ 766B50392C3B10A40030BDF3 /* SwiftUIToolbar.swift */, 765A364429E918CF00B939BD /* TitleSection.swift */, 765A362629E918CF00B939BD /* ViewState.swift */, - 7650E1442B17A0F300D686B8 /* XPubScannerView.swift */, + 7650E1442B17A0F300D686B8 /* ConnectWalletToAccountScannerView.swift */, 762514EE2CA3AC9C0039C8BD /* ScannerHelpSheet.swift */, 765A364629E918CF00B939BD /* swiftgen.yml */, 765A362A29E918CF00B939BD /* Resources */, @@ -2240,8 +2357,9 @@ children = ( 768B49882903B89F0099911D /* LibraryModule+ViewProviding.swift */, 768B49862903B89F0099911D /* LIbraryView.swift */, - 768B49852903B89F0099911D /* LibraryViewActionHandling.swift */, 768B49822903B89F0099911D /* LibraryViewModel.swift */, + 762B1CD62CCF28390093BD86 /* TrackRow.swift */, + 762B1CD82CCF28660093BD86 /* TrackRowModel.swift */, ); path = UI; sourceTree = ""; @@ -2514,16 +2632,6 @@ path = Interfaces; sourceTree = ""; }; - 76D99EC62B1934AD002FB4B2 /* Kotlin */ = { - isa = PBXGroup; - children = ( - 761E96FE2B6A60A300FDFC5E /* build.gradle.kts */, - 76D99EC72B1934CC002FB4B2 /* commonMain */, - 76D99EC82B1934CC002FB4B2 /* iosMain */, - ); - name = Kotlin; - sourceTree = ""; - }; 76EAFC532A0907FB00ACD862 /* DM_Serif_Text */ = { isa = PBXGroup; children = ( @@ -2695,6 +2803,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 76610AA32CBB45CE009F5693 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 768C69A72B5CD971001A1B1A /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -2755,14 +2870,15 @@ 76452A4D2B5A37D50001F47E /* PBXTargetDependency */, 76452A642B5A395A0001F47E /* PBXTargetDependency */, 761E973F2B6BC3B100FDFC5E /* PBXTargetDependency */, + 76610ADB2CBB46D6009F5693 /* PBXTargetDependency */, ); name = iosApp; packageProductDependencies = ( 76F823C428BEC2B3003A524C /* Resolver */, 7650E1732B17CB2A00D686B8 /* Kingfisher */, 7650E1752B17CB3600D686B8 /* QRCodeReader */, - 7642EB712B17CE8F009C8999 /* VLCKitSPM */, 765E35232B32458400AB3AF1 /* SentrySwiftUI */, + 76F5F2E32CD2D64D00143640 /* VLCKitSPM */, ); productName = iosApp; productReference = 7555FF7B242A565900829871 /* iosApp.app */; @@ -2807,6 +2923,7 @@ 7603003D2836FCB800BBF78C /* PBXTargetDependency */, 766E8FA528562CF200F18779 /* PBXTargetDependency */, 769BB68C2B60B8C6007CA421 /* PBXTargetDependency */, + 76610ADF2CBB471C009F5693 /* PBXTargetDependency */, ); name = SharedUI; packageProductDependencies = ( @@ -2879,6 +2996,7 @@ 76452A702B5A39A60001F47E /* PBXTargetDependency */, 76452A752B5A39F10001F47E /* PBXTargetDependency */, 761E97022B6A79D000FDFC5E /* PBXTargetDependency */, + 76610ACD2CBB45FC009F5693 /* PBXTargetDependency */, ); name = Login; packageProductDependencies = ( @@ -2934,6 +3052,7 @@ 766E8FDB28562FC800F18779 /* PBXTargetDependency */, 76C76145291F4D340054D2F3 /* PBXTargetDependency */, 76452A9B2B5A3DEE0001F47E /* PBXTargetDependency */, + 76610AC32CBB45F3009F5693 /* PBXTargetDependency */, ); name = NowPlaying; packageProductDependencies = ( @@ -3059,6 +3178,7 @@ 761E97182B6A88B600FDFC5E /* PBXTargetDependency */, 761E97202B6A906900FDFC5E /* PBXTargetDependency */, 766B50362C3B035D0030BDF3 /* PBXTargetDependency */, + 76610ABE2CBB45EB009F5693 /* PBXTargetDependency */, ); name = Profile; packageProductDependencies = ( @@ -3107,6 +3227,7 @@ 764838E92B0CD53100D6905B /* PBXTargetDependency */, 76452AA02B5A3E420001F47E /* PBXTargetDependency */, 76308A8C2B7B46C2002FDC50 /* PBXTargetDependency */, + 76610AD22CBB45FF009F5693 /* PBXTargetDependency */, ); name = Library; packageProductDependencies = ( @@ -3117,6 +3238,34 @@ productReference = 76562514284BEF30004B6E4B /* Library.framework */; productType = "com.apple.product-type.framework"; }; + 76610AA72CBB45CE009F5693 /* Analytics */ = { + isa = PBXNativeTarget; + buildConfigurationList = 76610AB12CBB45CE009F5693 /* Build configuration list for PBXNativeTarget "Analytics" */; + buildPhases = ( + 76610AA32CBB45CE009F5693 /* Headers */, + 76610AA42CBB45CE009F5693 /* Sources */, + 76610AA52CBB45CE009F5693 /* Frameworks */, + 76610AA62CBB45CE009F5693 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 76610AF42CBB4A1E009F5693 /* PBXTargetDependency */, + 76610B092CBB51D9009F5693 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 76610AA92CBB45CE009F5693 /* Analytics */, + ); + name = Analytics; + packageProductDependencies = ( + 76610AD52CBB4666009F5693 /* FirebaseCore */, + 76610AEF2CBB4A1A009F5693 /* FirebaseAnalyticsWithoutAdIdSupport */, + 76610AF62CBB4AE3009F5693 /* Resolver */, + ); + productName = Analytics; + productReference = 76610AA82CBB45CE009F5693 /* Analytics.framework */; + productType = "com.apple.product-type.framework"; + }; 768C697D2B5CC3CF001A1B1A /* ProfileTests */ = { isa = PBXNativeTarget; buildConfigurationList = 768C69852B5CC3CF001A1B1A /* Build configuration list for PBXNativeTarget "ProfileTests" */; @@ -3178,13 +3327,14 @@ 76C76152291F56F30054D2F3 /* PBXTargetDependency */, 76C7615C291F5FC80054D2F3 /* PBXTargetDependency */, 76F8E8FC2BA5A0A50013595D /* PBXTargetDependency */, + 76610AFD2CBB4C6A009F5693 /* PBXTargetDependency */, ); name = AudioPlayer; packageProductDependencies = ( 76C7614D291F56DF0054D2F3 /* Resolver */, - 769F4B5A2B04C34F009D23C6 /* VLCKitSPM */, 76829E222B5775D7007BCED5 /* Kingfisher */, 76FFD1132B95492F00D77309 /* OrderedCollections */, + 76699D092CD2BA5400A0182C /* VLCKitSPM */, ); productName = AudioPlayer; productReference = 76C76132291F445B0054D2F3 /* AudioPlayer.framework */; @@ -3206,6 +3356,7 @@ 762EF9AC2B1F90C7009E08D8 /* PBXTargetDependency */, 76AB88232B2ACB7400D8612A /* PBXTargetDependency */, 76452A7E2B5A3A860001F47E /* PBXTargetDependency */, + 76610AC82CBB45F8009F5693 /* PBXTargetDependency */, ); name = Main; packageProductDependencies = ( @@ -3348,6 +3499,10 @@ CreatedOnToolsVersion = 13.4.1; LastSwiftMigration = 1340; }; + 76610AA72CBB45CE009F5693 = { + CreatedOnToolsVersion = 16.0; + LastSwiftMigration = 1600; + }; 768C697D2B5CC3CF001A1B1A = { CreatedOnToolsVersion = 15.2; }; @@ -3385,18 +3540,20 @@ packageReferences = ( 760C2C46281653B50030ACF4 /* XCRemoteSwiftPackageReference "Resolver" */, 76C9631929FFC13A00E6B612 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */, - 769F4B592B04C34F009D23C6 /* XCRemoteSwiftPackageReference "vlckit-spm" */, 7650E1352B1667AB00D686B8 /* XCRemoteSwiftPackageReference "Kingfisher" */, 7650E13F2B1798E400D686B8 /* XCRemoteSwiftPackageReference "QRCodeReader" */, 760808B62B279D5B0015A2ED /* XCRemoteSwiftPackageReference "sentry-cocoa" */, 76D194792B7FFB37003EA9A5 /* XCRemoteSwiftPackageReference "recaptcha-enterprise-mobile-sdk" */, 76FFD1122B95492F00D77309 /* XCRemoteSwiftPackageReference "swift-collections" */, + 76610A9F2CBB3DE3009F5693 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + 76699D082CD2BA5400A0182C /* XCRemoteSwiftPackageReference "vlckit-spm" */, ); productRefGroup = 7555FF7C242A565900829871 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 7555FF7A242A565900829871 /* iosApp */, + 76610AA72CBB45CE009F5693 /* Analytics */, 76C76131291F445B0054D2F3 /* AudioPlayer */, 7618FD1628164CBD007573B4 /* Colors */, 764838BC2B0C8CAF00D6905B /* Files */, @@ -3557,6 +3714,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 76610AA62CBB45CE009F5693 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 76610AD72CBB467C009F5693 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 768C697C2B5CC3CF001A1B1A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -3719,7 +3884,7 @@ 765A365329E918CF00B939BD /* PickerSheet.swift in Sources */, 765A365629E918CF00B939BD /* MockData.swift in Sources */, 762514EF2CA3ACAB0039C8BD /* ScannerHelpSheet.swift in Sources */, - 7650E1452B17A0F300D686B8 /* XPubScannerView.swift in Sources */, + 7650E1452B17A0F300D686B8 /* ConnectWalletToAccountScannerView.swift in Sources */, 765A365429E918CF00B939BD /* BackButton.swift in Sources */, 765A365029E918CF00B939BD /* RadioPicker.swift in Sources */, 765A365D29E918CF00B939BD /* ThisWeekSection.swift in Sources */, @@ -3887,9 +4052,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 762B1CD72CCF28390093BD86 /* TrackRow.swift in Sources */, 765161982C8E35D500E79860 /* NFTTrack+FilterAndSort.swift in Sources */, + 762B1CD92CCF28660093BD86 /* TrackRowModel.swift in Sources */, 768B49912903B89F0099911D /* LibraryViewModel.swift in Sources */, - 768B49932903B89F0099911D /* LibraryViewActionHandling.swift in Sources */, 768B49972903B89F0099911D /* Strings.swift in Sources */, 7625D1532942B0740033C669 /* LibraryRoutes.swift in Sources */, 768B49962903B89F0099911D /* LibraryModule+ViewProviding.swift in Sources */, @@ -3898,6 +4064,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 76610AA42CBB45CE009F5693 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 768C697A2B5CC3CF001A1B1A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4234,6 +4407,57 @@ target = 76D01D9D28164B2100305605 /* ModuleLinker */; targetProxy = 76562521284BEF68004B6E4B /* PBXContainerItemProxy */; }; + 76610ABE2CBB45EB009F5693 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 76610AA72CBB45CE009F5693 /* Analytics */; + targetProxy = 76610ABD2CBB45EB009F5693 /* PBXContainerItemProxy */; + }; + 76610AC32CBB45F3009F5693 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 76610AA72CBB45CE009F5693 /* Analytics */; + targetProxy = 76610AC22CBB45F3009F5693 /* PBXContainerItemProxy */; + }; + 76610AC82CBB45F8009F5693 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 76610AA72CBB45CE009F5693 /* Analytics */; + targetProxy = 76610AC72CBB45F8009F5693 /* PBXContainerItemProxy */; + }; + 76610ACD2CBB45FC009F5693 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 76610AA72CBB45CE009F5693 /* Analytics */; + targetProxy = 76610ACC2CBB45FC009F5693 /* PBXContainerItemProxy */; + }; + 76610AD22CBB45FF009F5693 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 76610AA72CBB45CE009F5693 /* Analytics */; + targetProxy = 76610AD12CBB45FF009F5693 /* PBXContainerItemProxy */; + }; + 76610ADB2CBB46D6009F5693 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 76610AA72CBB45CE009F5693 /* Analytics */; + targetProxy = 76610ADA2CBB46D6009F5693 /* PBXContainerItemProxy */; + }; + 76610ADF2CBB471C009F5693 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 76610AA72CBB45CE009F5693 /* Analytics */; + targetProxy = 76610ADE2CBB471C009F5693 /* PBXContainerItemProxy */; + }; + 76610AF42CBB4A1E009F5693 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 76D01D9D28164B2100305605 /* ModuleLinker */; + targetProxy = 76610AF32CBB4A1E009F5693 /* PBXContainerItemProxy */; + }; + 76610AFD2CBB4C6A009F5693 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + platformFilter = ios; + target = 76610AA72CBB45CE009F5693 /* Analytics */; + targetProxy = 76610AFC2CBB4C6A009F5693 /* PBXContainerItemProxy */; + }; + 76610B092CBB51D9009F5693 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 76452A472B5A37D50001F47E /* SharedExtensions */; + targetProxy = 76610B082CBB51D9009F5693 /* PBXContainerItemProxy */; + }; 766B50362C3B035D0030BDF3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 7618FD1628164CBD007573B4 /* Colors */; @@ -4494,11 +4718,13 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; RECAPTCHA_KEY_ID = "${RECAPTCHA_KEY_ID}"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_STRICT_CONCURRENCY = targeted; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -4562,11 +4788,13 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; RECAPTCHA_KEY_ID = "${RECAPTCHA_KEY_ID}"; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_STRICT_CONCURRENCY = targeted; + SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -4581,7 +4809,7 @@ CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES; CODE_SIGN_ENTITLEMENTS = iosApp/iosApp.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Resources/Preview Content\""; DEVELOPMENT_TEAM = TM6M46M9D2; ENABLE_PREVIEWS = YES; @@ -4606,7 +4834,6 @@ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; name = Debug; @@ -4621,7 +4848,7 @@ CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES; CODE_SIGN_ENTITLEMENTS = iosApp/iosApp.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Resources/Preview Content\""; DEVELOPMENT_TEAM = TM6M46M9D2; ENABLE_PREVIEWS = YES; @@ -4645,7 +4872,6 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; name = Release; @@ -4684,7 +4910,6 @@ SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -4723,7 +4948,6 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -4762,7 +4986,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -4800,7 +5023,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -4839,7 +5061,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -4877,7 +5098,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -4917,7 +5137,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -4956,7 +5175,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -4995,7 +5213,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5033,7 +5250,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5072,7 +5288,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5110,7 +5325,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5150,7 +5364,6 @@ SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = targeted; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5189,7 +5402,6 @@ SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = targeted; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5217,7 +5429,6 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -5240,7 +5451,6 @@ PRODUCT_BUNDLE_IDENTIFIER = io.newm.ios.ModuleLinkerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -5281,7 +5491,6 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5321,7 +5530,6 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5362,7 +5570,6 @@ SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5401,7 +5608,6 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5427,7 +5633,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -5449,7 +5654,6 @@ PRODUCT_BUNDLE_IDENTIFIER = io.newm.ios.UtilitiesTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -5466,11 +5670,9 @@ GCC_C_LANGUAGE_STANDARD = gnu17; IPHONEOS_DEPLOYMENT_TARGET = 17.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 16.0; }; @@ -5487,10 +5689,8 @@ GCC_C_LANGUAGE_STANDARD = gnu17; IPHONEOS_DEPLOYMENT_TARGET = 17.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 16.0; }; @@ -5535,7 +5735,6 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5578,7 +5777,6 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5604,7 +5802,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -5626,7 +5823,6 @@ PRODUCT_BUNDLE_IDENTIFIER = io.newm.ios.LibraryTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -5667,7 +5863,6 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5708,7 +5903,93 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 76610AB22CBB45CE009F5693 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = TM6M46M9D2; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 NEWM. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.newm.ios.Analytics; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 76610AB32CBB45CE009F5693 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = TM6M46M9D2; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 NEWM. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.newm.ios.Analytics; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5734,7 +6015,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -5756,7 +6036,6 @@ PRODUCT_BUNDLE_IDENTIFIER = io.newm.ios.ProfileTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -5797,7 +6076,6 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5837,7 +6115,6 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5875,9 +6152,10 @@ PRODUCT_BUNDLE_IDENTIFIER = io.newm.AudioPlayer; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5914,9 +6192,10 @@ PRODUCT_BUNDLE_IDENTIFIER = io.newm.AudioPlayer; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_STRICT_CONCURRENCY = complete; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5955,7 +6234,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -5993,7 +6271,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -6034,7 +6311,6 @@ SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -6073,7 +6349,6 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -6112,7 +6387,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -6147,7 +6421,6 @@ PRODUCT_BUNDLE_IDENTIFIER = io.newm.ios.NowPlayingApp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -6171,7 +6444,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -6193,7 +6465,6 @@ PRODUCT_BUNDLE_IDENTIFIER = io.newm.ios.AudioPlayerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -6354,6 +6625,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 76610AB12CBB45CE009F5693 /* Build configuration list for PBXNativeTarget "Analytics" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 76610AB22CBB45CE009F5693 /* Debug */, + 76610AB32CBB45CE009F5693 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 768C69852B5CC3CF001A1B1A /* Build configuration list for PBXNativeTarget "ProfileTests" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -6452,7 +6732,15 @@ minimumVersion = 10.1.1; }; }; - 769F4B592B04C34F009D23C6 /* XCRemoteSwiftPackageReference "vlckit-spm" */ = { + 76610A9F2CBB3DE3009F5693 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = exactVersion; + version = 11.3.0; + }; + }; + 76699D082CD2BA5400A0182C /* XCRemoteSwiftPackageReference "vlckit-spm" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/tylerjonesio/vlckit-spm"; requirement = { @@ -6527,11 +6815,6 @@ package = 76C9631929FFC13A00E6B612 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */; productName = GoogleSignIn; }; - 7642EB712B17CE8F009C8999 /* VLCKitSPM */ = { - isa = XCSwiftPackageProductDependency; - package = 769F4B592B04C34F009D23C6 /* XCRemoteSwiftPackageReference "vlckit-spm" */; - productName = VLCKitSPM; - }; 764838F12B0CD6C200D6905B /* Resolver */ = { isa = XCSwiftPackageProductDependency; package = 760C2C46281653B50030ACF4 /* XCRemoteSwiftPackageReference "Resolver" */; @@ -6587,6 +6870,26 @@ package = 760808B62B279D5B0015A2ED /* XCRemoteSwiftPackageReference "sentry-cocoa" */; productName = Sentry; }; + 76610AD52CBB4666009F5693 /* FirebaseCore */ = { + isa = XCSwiftPackageProductDependency; + package = 76610A9F2CBB3DE3009F5693 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCore; + }; + 76610AEF2CBB4A1A009F5693 /* FirebaseAnalyticsWithoutAdIdSupport */ = { + isa = XCSwiftPackageProductDependency; + package = 76610A9F2CBB3DE3009F5693 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAnalyticsWithoutAdIdSupport; + }; + 76610AF62CBB4AE3009F5693 /* Resolver */ = { + isa = XCSwiftPackageProductDependency; + package = 760C2C46281653B50030ACF4 /* XCRemoteSwiftPackageReference "Resolver" */; + productName = Resolver; + }; + 76699D092CD2BA5400A0182C /* VLCKitSPM */ = { + isa = XCSwiftPackageProductDependency; + package = 76699D082CD2BA5400A0182C /* XCRemoteSwiftPackageReference "vlckit-spm" */; + productName = VLCKitSPM; + }; 76829E222B5775D7007BCED5 /* Kingfisher */ = { isa = XCSwiftPackageProductDependency; package = 7650E1352B1667AB00D686B8 /* XCRemoteSwiftPackageReference "Kingfisher" */; @@ -6597,11 +6900,6 @@ package = 760C2C46281653B50030ACF4 /* XCRemoteSwiftPackageReference "Resolver" */; productName = Resolver; }; - 769F4B5A2B04C34F009D23C6 /* VLCKitSPM */ = { - isa = XCSwiftPackageProductDependency; - package = 769F4B592B04C34F009D23C6 /* XCRemoteSwiftPackageReference "vlckit-spm" */; - productName = VLCKitSPM; - }; 76C7614D291F56DF0054D2F3 /* Resolver */ = { isa = XCSwiftPackageProductDependency; package = 760C2C46281653B50030ACF4 /* XCRemoteSwiftPackageReference "Resolver" */; @@ -6642,6 +6940,11 @@ package = 760C2C46281653B50030ACF4 /* XCRemoteSwiftPackageReference "Resolver" */; productName = Resolver; }; + 76F5F2E32CD2D64D00143640 /* VLCKitSPM */ = { + isa = XCSwiftPackageProductDependency; + package = 76699D082CD2BA5400A0182C /* XCRemoteSwiftPackageReference "vlckit-spm" */; + productName = VLCKitSPM; + }; 76F823C428BEC2B3003A524C /* Resolver */ = { isa = XCSwiftPackageProductDependency; package = 760C2C46281653B50030ACF4 /* XCRemoteSwiftPackageReference "Resolver" */; diff --git a/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme b/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme index 8719d901..570d547f 100644 --- a/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme +++ b/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme @@ -105,6 +105,13 @@ ReferencedContainer = "container:iosApp.xcodeproj"> + + + + CFBundleVersion $(CURRENT_PROJECT_VERSION) + FirebaseAppDelegateProxyEnabled + GIDClientID 489785482974-hvron45nt22d0nb54qrplksne6202e19.apps.googleusercontent.com ITSAppUsesNonExemptEncryption diff --git a/shared/src/commonMain/kotlin/io.newm.shared/public/models/mocks/NFTTrackMocks.kt b/shared/src/commonMain/kotlin/io.newm.shared/public/models/mocks/NFTTrackMocks.kt index 3e30abfd..437a09cb 100644 --- a/shared/src/commonMain/kotlin/io.newm.shared/public/models/mocks/NFTTrackMocks.kt +++ b/shared/src/commonMain/kotlin/io.newm.shared/public/models/mocks/NFTTrackMocks.kt @@ -2,98 +2,110 @@ package io.newm.shared.public.models.mocks import io.newm.shared.public.models.NFTTrack - -val mockTracks: List = listOf( - makeMockTrack( - id = "0", - name = "Dripdropz", - imageUrl = "https://arweave.net/zBVmedCDTGBH06tTEA5u0aYFWkXPkr9w1GxGefxJIms", - songUrl = "https://arweave.net/tWDP3Lr4U3vMy_iYwm-FPBa6ad0aaMNdwHa7MUAFjuo", - duration = 20, - artists = listOf("Esco") - ), - makeMockTrack( - id = "1", - name = "Lost In My Own Zone", - imageUrl = "https://arweave.net/NkVFjDc24JfU4kgKE_vgz6hnuBpx_5ics2RTrrQEP6U", - songUrl = "https://arweave.net/VkFPK-T7xkMgblDbxjfKjI7UXeyu23AOyg7pm4VjxzY", - duration = 2, - artists = listOf("Abyss") - ), - makeMockTrack( - id = "2", - name = "Sexiest Man Alive", - imageUrl = "https://arweave.net/eYQ-pcX9qwh3KuVcdbClQQMkdgehula0ZhtSGomzdbg", - songUrl = "https://arweave.net/KN9xqm4VroNdlgo30lA5hvkOPasgFIbSUqAVAPn8ob0", - duration = -15, - artists = listOf("Mike Lerman") - ), - makeMockTrack( - id = "3", - name = "Daisuke", - imageUrl = "https://arweave.net/GlMlqHIPjwUtlPUfQxDdX1jWSjlKK1BCTBIekXgA66A", - songUrl = "https://arweave.net/QpgjmWmAHNeRVgx_Ylwvh16i3aWd8BBgyq7f16gaUu0", - duration = 45, - artists = listOf("Danketsu", "Mirai Music", "NSTASIA") - ), - makeMockTrack( - id = "4", - name = "Love In The Water", - imageUrl = "https://arweave.net/f5W8RZmAQimuz_vytFY9ofIzd9QpGaDIv2UXrrahTu4", - songUrl = "https://arweave.net/DeVRF-RTkRRHoP4M-L9AjIu35ilzgclhLOrgQB2Px34", - duration = 1, - artists = listOf("NIDO") - ), - makeMockTrack( - id = "5", - name = "New Song", - imageUrl = "https://arweave.net/xUauTN89ulvWAQ2Euz12ogF3EbDaiNPQKNe0I0Ib-mA", - songUrl = "https://arweave.net/jeVMmGLmrtV3Dn-TfPkdCAn-Qjei4A2kFUOhFuvSCKU", - duration = 50, - artists = listOf("Esco") - ), - makeMockTrack( - id = "6", - name = "Bigger Dreams", - imageUrl = "https://arweave.net/CuPFY2Ln7yUUhJX09G530kdPf93eGhAVlhjrtR7Jh5w", - songUrl = "https://arweave.net/P141o0RDAjSYlVQgTDgHNAORQTkMYIVCprmD_dKMVss", - duration = 93, - artists = listOf("MURS") - ), - makeMockTrack( - id = "7", - name = "Space Cowboy", - imageUrl = "https://arweave.net/qog8drrF9Oa55eWclrUejI65rn29gdcDX-Bj31VwBMc", - songUrl = "https://arweave.net/W-PMgNX28f1RE1qwLG7SIU14-NPEmatFr51-zUAsqFI", - duration = 123, - artists = listOf("JUSE") - ), - makeMockTrack( - id = "8", - name = "Best Song Ever", - imageUrl = "https://arweave.net/tlsj0fyL0BXU871-my1CvnNSBjMgXRn4zaO3taFVz3k", - songUrl = "https://arweave.net/6HwXcWvOwfOWTljGYTlce0_zJjg1UslgkvNue6pau0E", - duration = 324, - artists = listOf("Esco") - ), - makeMockTrack( - id = "9", - name = "Underdog, Pt. 2", - imageUrl = "https://arweave.net/eYQ-pcX9qwh3KuVcdbClQQMkdgehula0ZhtSGomzdbg", - songUrl = "https://arweave.net/Em9XiS87I9ff8Wx2WOt2GIZ650gaiRTSjkNfJdvluLs", - duration = 5, - artists = listOf("Mike Lerman") - ), - makeMockTrack( - id = "10", - name = "Make It Easy", - imageUrl = "https://arweave.net/eYQ-pcX9qwh3KuVcdbClQQMkdgehula0ZhtSGomzdbg", - songUrl = "https://arweave.net/bWGg7d-GhU7Y3ZTp9zfpv1JYXlS7JTxqZriFHtAgLF4", - duration = 30, - artists = listOf("Mike Lerman") - ) +val baseTracks = listOf( + makeMockTrack( + id = "0", + name = "Dripdropz", + imageUrl = "https://arweave.net/zBVmedCDTGBH06tTEA5u0aYFWkXPkr9w1GxGefxJIms", + songUrl = "https://arweave.net/tWDP3Lr4U3vMy_iYwm-FPBa6ad0aaMNdwHa7MUAFjuo", + duration = 20, + artists = listOf("Esco") + ), + makeMockTrack( + id = "1", + name = "Lost In My Own Zone", + imageUrl = "https://arweave.net/NkVFjDc24JfU4kgKE_vgz6hnuBpx_5ics2RTrrQEP6U", + songUrl = "https://arweave.net/VkFPK-T7xkMgblDbxjfKjI7UXeyu23AOyg7pm4VjxzY", + duration = 2, + artists = listOf("Abyss") + ), + makeMockTrack( + id = "2", + name = "Sexiest Man Alive", + imageUrl = "https://arweave.net/eYQ-pcX9qwh3KuVcdbClQQMkdgehula0ZhtSGomzdbg", + songUrl = "https://arweave.net/KN9xqm4VroNdlgo30lA5hvkOPasgFIbSUqAVAPn8ob0", + duration = -15, + artists = listOf("Mike Lerman") + ), + makeMockTrack( + id = "3", + name = "Daisuke", + imageUrl = "https://arweave.net/GlMlqHIPjwUtlPUfQxDdX1jWSjlKK1BCTBIekXgA66A", + songUrl = "https://arweave.net/QpgjmWmAHNeRVgx_Ylwvh16i3aWd8BBgyq7f16gaUu0", + duration = 45, + artists = listOf("Danketsu", "Mirai Music", "NSTASIA") + ), + makeMockTrack( + id = "4", + name = "Love In The Water", + imageUrl = "https://arweave.net/f5W8RZmAQimuz_vytFY9ofIzd9QpGaDIv2UXrrahTu4", + songUrl = "https://arweave.net/DeVRF-RTkRRHoP4M-L9AjIu35ilzgclhLOrgQB2Px34", + duration = 1, + artists = listOf("NIDO") + ), + makeMockTrack( + id = "5", + name = "New Song", + imageUrl = "https://arweave.net/xUauTN89ulvWAQ2Euz12ogF3EbDaiNPQKNe0I0Ib-mA", + songUrl = "https://arweave.net/jeVMmGLmrtV3Dn-TfPkdCAn-Qjei4A2kFUOhFuvSCKU", + duration = 50, + artists = listOf("Esco") + ), + makeMockTrack( + id = "6", + name = "Bigger Dreams", + imageUrl = "https://arweave.net/CuPFY2Ln7yUUhJX09G530kdPf93eGhAVlhjrtR7Jh5w", + songUrl = "https://arweave.net/P141o0RDAjSYlVQgTDgHNAORQTkMYIVCprmD_dKMVss", + duration = 93, + artists = listOf("MURS") + ), + makeMockTrack( + id = "7", + name = "Space Cowboy", + imageUrl = "https://arweave.net/qog8drrF9Oa55eWclrUejI65rn29gdcDX-Bj31VwBMc", + songUrl = "https://arweave.net/W-PMgNX28f1RE1qwLG7SIU14-NPEmatFr51-zUAsqFI", + duration = 123, + artists = listOf("JUSE") + ), + makeMockTrack( + id = "8", + name = "Best Song Ever", + imageUrl = "https://arweave.net/tlsj0fyL0BXU871-my1CvnNSBjMgXRn4zaO3taFVz3k", + songUrl = "https://arweave.net/6HwXcWvOwfOWTljGYTlce0_zJjg1UslgkvNue6pau0E", + duration = 324, + artists = listOf("Esco") + ), + makeMockTrack( + id = "9", + name = "Underdog, Pt. 2", + imageUrl = "https://arweave.net/eYQ-pcX9qwh3KuVcdbClQQMkdgehula0ZhtSGomzdbg", + songUrl = "https://arweave.net/Em9XiS87I9ff8Wx2WOt2GIZ650gaiRTSjkNfJdvluLs", + duration = 5, + artists = listOf("Mike Lerman") + ), + makeMockTrack( + id = "10", + name = "Make It Easy", + imageUrl = "https://arweave.net/eYQ-pcX9qwh3KuVcdbClQQMkdgehula0ZhtSGomzdbg", + songUrl = "https://arweave.net/bWGg7d-GhU7Y3ZTp9zfpv1JYXlS7JTxqZriFHtAgLF4", + duration = 30, + artists = listOf("Mike Lerman") + ) ) +// Generate 1000 mock tracks by repeating the base tracks +val mockTracks: List = (0 until 1000).map { i -> + val baseTrack = baseTracks[i % baseTracks.size] + makeMockTrack( + id = i.toString(), + name = baseTrack.title, + imageUrl = baseTrack.imageUrl, + songUrl = baseTrack.audioUrl, + duration = baseTrack.duration, + artists = baseTrack.artists + ) +} + private fun makeMockTrack( id: String, name: String,