first commit
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
Binary file not shown.
+19
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>music player.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>music playerWatch Watch App.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 290 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon 1.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,117 @@
|
||||
import Foundation
|
||||
import WatchConnectivity
|
||||
import Combine
|
||||
|
||||
// MARK: - Лёгкая модель трека для передачи на часы
|
||||
struct WatchTrackInfo: Codable {
|
||||
let id: String // используем имя файла как стабильный идентификатор
|
||||
let title: String
|
||||
let artist: String
|
||||
}
|
||||
|
||||
// MARK: - Связь iPhone <-> Apple Watch
|
||||
// Добавь этот файл в таргет "music player" (основное приложение).
|
||||
class PhoneConnectivityManager: NSObject, WCSessionDelegate, ObservableObject {
|
||||
static let shared = PhoneConnectivityManager()
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
activateSession()
|
||||
// Откладываем обращение к MusicManager.shared на следующий "тик" —
|
||||
// иначе при первом запуске получится циклическая инициализация
|
||||
// двух синглтонов друг через друга и приложение упадёт.
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.observeMusicManager()
|
||||
}
|
||||
}
|
||||
|
||||
private func activateSession() {
|
||||
guard WCSession.isSupported() else { return }
|
||||
let session = WCSession.default
|
||||
session.delegate = self
|
||||
session.activate()
|
||||
}
|
||||
|
||||
// MARK: - Отправка состояния плеера на часы
|
||||
|
||||
private func observeMusicManager() {
|
||||
let manager = MusicManager.shared
|
||||
Publishers.CombineLatest4(
|
||||
manager.$tracks,
|
||||
manager.$currentTrackIndex,
|
||||
manager.$isPlaying,
|
||||
manager.$currentTitle
|
||||
)
|
||||
.debounce(for: .milliseconds(250), scheduler: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
self?.pushStateToWatch()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func pushStateToWatch() {
|
||||
guard WCSession.default.activationState == .activated else { return }
|
||||
let manager = MusicManager.shared
|
||||
|
||||
let trackInfos = manager.tracks.map {
|
||||
WatchTrackInfo(id: $0.url.lastPathComponent, title: $0.title, artist: $0.artist)
|
||||
}
|
||||
guard let tracksData = try? JSONEncoder().encode(trackInfos) else { return }
|
||||
|
||||
let context: [String: Any] = [
|
||||
"tracks": tracksData,
|
||||
"isPlaying": manager.isPlaying,
|
||||
"currentTitle": manager.currentTitle,
|
||||
"currentArtist": manager.currentArtist,
|
||||
"currentId": manager.currentTrack?.url.lastPathComponent ?? ""
|
||||
]
|
||||
|
||||
try? WCSession.default.updateApplicationContext(context)
|
||||
}
|
||||
|
||||
// MARK: - WCSessionDelegate
|
||||
|
||||
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
|
||||
DispatchQueue.main.async { self.pushStateToWatch() }
|
||||
}
|
||||
|
||||
func sessionDidBecomeInactive(_ session: WCSession) {}
|
||||
func sessionDidDeactivate(_ session: WCSession) { WCSession.default.activate() }
|
||||
|
||||
// Команды, пришедшие пока приложение на телефоне активно
|
||||
func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
|
||||
handleCommand(message)
|
||||
}
|
||||
|
||||
// Команды, пришедшие "в очереди" (если телефон был недоступен)
|
||||
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any]) {
|
||||
handleCommand(userInfo)
|
||||
}
|
||||
|
||||
// MARK: - Обработка команд с часов
|
||||
|
||||
private func handleCommand(_ command: [String: Any]) {
|
||||
guard let action = command["action"] as? String else { return }
|
||||
DispatchQueue.main.async {
|
||||
let manager = MusicManager.shared
|
||||
switch action {
|
||||
case "playPause":
|
||||
manager.playPause()
|
||||
case "next":
|
||||
manager.nextTrack()
|
||||
case "previous":
|
||||
manager.prevTrack()
|
||||
case "playTrack":
|
||||
if let id = command["id"] as? String,
|
||||
let index = manager.currentList.firstIndex(where: { $0.url.lastPathComponent == id }) {
|
||||
manager.playTrack(at: index)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
self.pushStateToWatch()
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// music_playerApp.swift
|
||||
// music player
|
||||
//
|
||||
// Created by aswer on 23.06.2026.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct music_playerApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// music_playerTests.swift
|
||||
// music playerTests
|
||||
//
|
||||
// Created by aswer on 23.06.2026.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import music_player
|
||||
|
||||
final class music_playerTests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
// Any test you write for XCTest can be annotated as throws and async.
|
||||
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
|
||||
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
|
||||
}
|
||||
|
||||
func testPerformanceExample() throws {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// music_playerUITests.swift
|
||||
// music playerUITests
|
||||
//
|
||||
// Created by aswer on 23.06.2026.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class music_playerUITests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
// In UI tests it is usually best to stop immediately when a failure occurs.
|
||||
continueAfterFailure = false
|
||||
|
||||
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// UI tests must launch the application that they test.
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testLaunchPerformance() throws {
|
||||
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
|
||||
// This measures how long it takes to launch your application.
|
||||
measure(metrics: [XCTApplicationLaunchMetric()]) {
|
||||
XCUIApplication().launch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// music_playerUITestsLaunchTests.swift
|
||||
// music playerUITests
|
||||
//
|
||||
// Created by aswer on 23.06.2026.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class music_playerUITestsLaunchTests: XCTestCase {
|
||||
|
||||
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
override func setUpWithError() throws {
|
||||
continueAfterFailure = false
|
||||
}
|
||||
|
||||
func testLaunch() throws {
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Insert steps here to perform after app launch but before taking a screenshot,
|
||||
// such as logging into a test account or navigating somewhere in the app
|
||||
|
||||
let attachment = XCTAttachment(screenshot: app.screenshot())
|
||||
attachment.name = "Launch Screen"
|
||||
attachment.lifetime = .keepAlways
|
||||
add(attachment)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"platform" : "watchos",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// music playerWatch Watch App
|
||||
//
|
||||
// Created by aswer on 30.06.2026.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Главный экран часов
|
||||
// Замени этим содержимым ContentView.swift, который Xcode сгенерировал
|
||||
// в таргете часов (структура должна остаться по имени ContentView,
|
||||
// чтобы App.swift на часах продолжил работать без изменений).
|
||||
struct ContentView: View {
|
||||
@StateObject private var connectivity = WatchConnectivityManager.shared
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List {
|
||||
if !connectivity.currentTitle.isEmpty {
|
||||
NavigationLink(destination: NowPlayingView()) {
|
||||
HStack {
|
||||
Image(systemName: connectivity.isPlaying ? "pause.circle.fill" : "play.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
VStack(alignment: .leading) {
|
||||
Text(connectivity.currentTitle)
|
||||
.font(.headline)
|
||||
.lineLimit(1)
|
||||
Text(connectivity.currentArtist)
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if connectivity.tracks.isEmpty {
|
||||
Text("Открой приложение на iPhone, чтобы список треков появился здесь")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.gray)
|
||||
} else {
|
||||
ForEach(connectivity.tracks) { track in
|
||||
Button(action: { connectivity.playTrack(id: track.id) }) {
|
||||
VStack(alignment: .leading) {
|
||||
Text(track.title)
|
||||
.font(.body)
|
||||
.foregroundColor(track.id == connectivity.currentId ? .green : .primary)
|
||||
.lineLimit(1)
|
||||
Text(track.artist)
|
||||
.font(.caption2)
|
||||
.foregroundColor(.gray)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Музыка")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Экран "Сейчас играет" на часах
|
||||
struct NowPlayingView: View {
|
||||
@StateObject private var connectivity = WatchConnectivityManager.shared
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 10) {
|
||||
Text(connectivity.currentTitle)
|
||||
.font(.headline)
|
||||
.multilineTextAlignment(.center)
|
||||
.lineLimit(2)
|
||||
|
||||
Text(connectivity.currentArtist)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.gray)
|
||||
.lineLimit(1)
|
||||
|
||||
HStack(spacing: 22) {
|
||||
Button(action: connectivity.previous) {
|
||||
Image(systemName: "backward.fill")
|
||||
.font(.title2)
|
||||
}
|
||||
|
||||
Button(action: connectivity.playPause) {
|
||||
Image(systemName: connectivity.isPlaying ? "pause.fill" : "play.fill")
|
||||
.font(.largeTitle)
|
||||
}
|
||||
|
||||
Button(action: connectivity.next) {
|
||||
Image(systemName: "forward.fill")
|
||||
.font(.title2)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.top, 6)
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle("Сейчас играет")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import Foundation
|
||||
import WatchConnectivity
|
||||
import Combine
|
||||
|
||||
// MARK: - Та же лёгкая модель трека, что и на телефоне
|
||||
struct WatchTrackInfo: Codable, Identifiable {
|
||||
let id: String
|
||||
let title: String
|
||||
let artist: String
|
||||
}
|
||||
|
||||
// MARK: - Связь Apple Watch <-> iPhone
|
||||
// Добавь этот файл в таргет часов (например "music playerWatch").
|
||||
class WatchConnectivityManager: NSObject, WCSessionDelegate, ObservableObject {
|
||||
static let shared = WatchConnectivityManager()
|
||||
|
||||
@Published var tracks: [WatchTrackInfo] = []
|
||||
@Published var isPlaying: Bool = false
|
||||
@Published var currentTitle: String = ""
|
||||
@Published var currentArtist: String = ""
|
||||
@Published var currentId: String = ""
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
guard WCSession.isSupported() else { return }
|
||||
let session = WCSession.default
|
||||
session.delegate = self
|
||||
session.activate()
|
||||
}
|
||||
|
||||
// MARK: - WCSessionDelegate
|
||||
|
||||
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {}
|
||||
|
||||
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) {
|
||||
DispatchQueue.main.async {
|
||||
self.apply(applicationContext)
|
||||
}
|
||||
}
|
||||
|
||||
private func apply(_ context: [String: Any]) {
|
||||
if let data = context["tracks"] as? Data,
|
||||
let decoded = try? JSONDecoder().decode([WatchTrackInfo].self, from: data) {
|
||||
self.tracks = decoded
|
||||
}
|
||||
self.isPlaying = context["isPlaying"] as? Bool ?? false
|
||||
self.currentTitle = context["currentTitle"] as? String ?? ""
|
||||
self.currentArtist = context["currentArtist"] as? String ?? ""
|
||||
self.currentId = context["currentId"] as? String ?? ""
|
||||
}
|
||||
|
||||
// MARK: - Отправка команд на телефон
|
||||
|
||||
private func send(action: String, extra: [String: Any] = [:]) {
|
||||
guard WCSession.isSupported() else { return }
|
||||
var payload = extra
|
||||
payload["action"] = action
|
||||
|
||||
let session = WCSession.default
|
||||
if session.isReachable {
|
||||
session.sendMessage(payload, replyHandler: nil) { _ in
|
||||
session.transferUserInfo(payload)
|
||||
}
|
||||
} else {
|
||||
session.transferUserInfo(payload)
|
||||
}
|
||||
}
|
||||
|
||||
func playPause() { send(action: "playPause") }
|
||||
func next() { send(action: "next") }
|
||||
func previous() { send(action: "previous") }
|
||||
func playTrack(id: String) { send(action: "playTrack", extra: ["id": id]) }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// music_playerWatchApp.swift
|
||||
// music playerWatch Watch App
|
||||
//
|
||||
// Created by aswer on 30.06.2026.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct music_playerWatch_Watch_AppApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// music_playerWatch_Watch_AppTests.swift
|
||||
// music playerWatch Watch AppTests
|
||||
//
|
||||
// Created by aswer on 30.06.2026.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import music_playerWatch_Watch_App
|
||||
|
||||
final class music_playerWatch_Watch_AppTests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
// Any test you write for XCTest can be annotated as throws and async.
|
||||
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
|
||||
// Tests marked async will run the test method on an arbitrary thread managed by the Swift runtime.
|
||||
}
|
||||
|
||||
func testPerformanceExample() throws {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// music_playerWatch_Watch_AppUITests.swift
|
||||
// music playerWatch Watch AppUITests
|
||||
//
|
||||
// Created by aswer on 30.06.2026.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class music_playerWatch_Watch_AppUITests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
// In UI tests it is usually best to stop immediately when a failure occurs.
|
||||
continueAfterFailure = false
|
||||
|
||||
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// UI tests must launch the application that they test.
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testLaunchPerformance() throws {
|
||||
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
|
||||
// This measures how long it takes to launch your application.
|
||||
measure(metrics: [XCTApplicationLaunchMetric()]) {
|
||||
XCUIApplication().launch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// music_playerWatch_Watch_AppUITestsLaunchTests.swift
|
||||
// music playerWatch Watch AppUITests
|
||||
//
|
||||
// Created by aswer on 30.06.2026.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class music_playerWatch_Watch_AppUITestsLaunchTests: XCTestCase {
|
||||
|
||||
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
override func setUpWithError() throws {
|
||||
continueAfterFailure = false
|
||||
}
|
||||
|
||||
func testLaunch() throws {
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Insert steps here to perform after app launch but before taking a screenshot,
|
||||
// such as logging into a test account or navigating somewhere in the app
|
||||
|
||||
let attachment = XCTAttachment(screenshot: app.screenshot())
|
||||
attachment.name = "Launch Screen"
|
||||
attachment.lifetime = .keepAlways
|
||||
add(attachment)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user