1
1
mirror of https://github.com/danbee/persephone synced 2025-03-04 08:39:11 +00:00

Add iOS app skeleton

This commit is contained in:
Daniel Barber 2020-03-29 12:50:47 -04:00
parent 90c0df5c5d
commit 573257f1a8
Signed by: danbarber
GPG Key ID: 931D8112E0103DD8
48 changed files with 1368 additions and 153 deletions

View File

@ -6,14 +6,13 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
import ReSwift
struct App {
static let store = Store<AppState>(reducer: appReducer, state: nil)
static let trackTimer = TrackTimer()
static let userNotificationsController = UserNotificationsController()
static let mpdServerController = MPDServerController()
static let mpdServerDelegate = MPDServerDelegate()
static let mpdServerController = MPDServerController(delegate: mpdServerDelegate)
static var mpdClient: MPDClient!
}

View File

@ -1,14 +0,0 @@
//
// ArtistListActions.swift
// Persephone
//
// Created by Daniel Barber on 2019/9/29.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import ReSwift
struct UpdateArtistListAction: Action {
var artists: [String]
}

View File

@ -1,13 +0,0 @@
//
// ArtistListState.swift
// Persephone
//
// Created by Daniel Barber on 2019/9/20.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import ReSwift
struct ArtistListState: StateType, Equatable {
var artists: [Artist] = []
}

View File

@ -1,25 +0,0 @@
//
// ArtistReducer.swift
// Persephone
//
// Created by Daniel Barber on 2019/9/29.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import ReSwift
func artistListReducer(action: Action, state: ArtistListState?) -> ArtistListState {
var state = state ?? ArtistListState()
switch action {
case let action as UpdateArtistListAction:
state.artists = action.artists.map { Artist(name: $0) }
default:
break
}
return state
}

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,11 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>Persephone-iOS.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>Persephone.xcscheme</key>
<dict>
<key>orderHint</key>
@ -14,6 +19,11 @@
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>libmpdclient.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>

View File

@ -10,8 +10,8 @@ import Foundation
import ReSwift
class MPDServerController {
init() {
App.mpdClient = MPDClient(withDelegate: App.mpdServerDelegate)
init(delegate: MPDClientDelegate? = nil) {
App.mpdClient = MPDClient(withDelegate: delegate)
App.store.subscribe(self) {
$0.select { $0.preferencesState.mpdServer }

View File

@ -6,7 +6,8 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import Dispatch
import NotificationCenter
class MPDServerDelegate: MPDClientDelegate {
func didConnect(mpdClient: MPDClient) {
@ -67,8 +68,5 @@ class MPDServerDelegate: MPDClientDelegate {
}
func didLoadArtists(mpdClient: MPDClient, artists: [String]) {
DispatchQueue.main.async {
App.store.dispatch(UpdateArtistListAction(artists: artists))
}
}
}

18
Shared/Lib/MachTime.swift Normal file
View File

@ -0,0 +1,18 @@
//
// MachTime.swift
// Persephone
//
// Created by Daniel Barber on 2020-3-19.
// Copyright © 2020 Dan Barber. All rights reserved.
//
func machTime() -> UInt64 {
var info = mach_timebase_info()
mach_timebase_info(&info)
return mach_absolute_time() * UInt64(info.numer) / UInt64(info.denom)
}
func machTimeS() -> Double {
return Double(machTime()) / 1_000_000_000
}

View File

@ -6,7 +6,6 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import CryptoSwift
struct Album {

View File

@ -1,24 +0,0 @@
//
// Artist.swift
// Persephone
//
// Created by Daniel Barber on 2019/10/04.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
struct Artist {
var name: String
var image: Loading<NSImage?> = .notLoaded
init(name: String) {
self.name = name
}
}
extension Artist: Equatable {
static func == (lhs: Artist, rhs: Artist) -> Bool {
return lhs.name == rhs.name
}
}

View File

@ -6,7 +6,6 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import CryptoSwift
struct DraggedAlbum: Codable {

View File

@ -6,8 +6,6 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
enum Loading<T> {
case notLoaded
case loading

View File

@ -6,8 +6,6 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
struct MPDServer {
let hostDefault = "127.0.0.1"
let portDefault = 6600

View File

@ -6,8 +6,6 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
struct QueueItem: Hashable {
var song: Song
var queuePos: Int

View File

@ -6,8 +6,6 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
struct Song {
var mpdSong: MPDClient.MPDSong

View File

@ -6,17 +6,17 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import Foundation
class TrackTimer: NSObject {
class TrackTimer {
var timer: Timer?
var startTime: CFTimeInterval = CACurrentMediaTime()
var startTime: CFTimeInterval = CFTimeInterval(machTimeS())
var startElapsed: Double = 0
func start(elapsedTimeMs: UInt?) {
guard let elapsedTimeMs = elapsedTimeMs else { return }
startTime = CACurrentMediaTime()
startTime = CFTimeInterval(machTimeS())
startElapsed = Double(elapsedTimeMs) / 1000
DispatchQueue.main.async {
@ -26,7 +26,7 @@ class TrackTimer: NSObject {
withTimeInterval: 0.25,
repeats: true
) { _ in
let currentTime = CACurrentMediaTime()
let currentTime = CFTimeInterval(machTimeS())
let timeDiff = currentTime - self.startTime
let newElapsedTimeMs = UInt((self.startElapsed + timeDiff) * 1000)

View File

@ -6,7 +6,6 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import ReSwift
struct UpdateAlbumListAction: Action {

View File

@ -13,7 +13,6 @@ struct AppState: StateType {
var playerState = PlayerState()
var queueState = QueueState()
var albumListState = AlbumListState()
var artistListState = ArtistListState()
var preferencesState = PreferencesState()
var uiState = UIState()
}

View File

@ -14,7 +14,6 @@ func appReducer(action: Action, state: AppState?) -> AppState {
playerState: playerReducer(action: action, state: state?.playerState),
queueState: queueReducer(action: action, state: state?.queueState),
albumListState: albumListReducer(action: action, state: state?.albumListState),
artistListState: artistListReducer(action: action, state: state?.artistListState),
preferencesState: preferencesReducer(action: action, state: state?.preferencesState),
uiState: uiReducer(action: action, state: state?.uiState)
)

View File

@ -6,7 +6,7 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import Dispatch
import ReSwift
func playerReducer(action: Action, state: PlayerState?) -> PlayerState {

View File

@ -6,7 +6,7 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import Dispatch
import ReSwift
func queueReducer(action: Action, state: QueueState?) -> QueueState {

View File

@ -6,7 +6,7 @@
// Copyright © 2019 Dan Barber. All rights reserved.
//
import AppKit
import Dispatch
import ReSwift
func uiReducer(action: Action, state: UIState?) -> UIState {

View File

@ -1,10 +1,4 @@
#!/bin/sh
brew install meson
git submodule update --init
cd libmpdclient && \
meson . output && \
ninja -C output
cd ..
git apply --directory=libmpdclient/ libmpdclient.patch

18
iOS/App.swift Normal file
View File

@ -0,0 +1,18 @@
//
// App.swift
// Persephone
//
// Created by Daniel Barber on 2019/4/30.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import ReSwift
struct App {
static let store = Store<AppState>(reducer: appReducer, state: nil)
static let trackTimer = TrackTimer()
//static let userNotificationsController = UserNotificationsController()
static let mpdServerDelegate = MPDServerDelegate()
static let mpdServerController = MPDServerController(delegate: mpdServerDelegate)
static var mpdClient: MPDClient!
}

30
iOS/AppDelegate.swift Normal file
View File

@ -0,0 +1,30 @@
//
// AppDelegate.swift
// Persephone-iOS
//
// Created by Daniel Barber on 2020-3-13.
// Copyright © 2020 Dan Barber. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

62
iOS/Info.plist Normal file
View File

@ -0,0 +1,62 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

22
iOSTests/Info.plist Normal file
View File

@ -0,0 +1,22 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -0,0 +1,34 @@
//
// Persephone_iOSTests.swift
// Persephone-iOSTests
//
// Created by Daniel Barber on 2020-3-13.
// Copyright © 2020 Dan Barber. All rights reserved.
//
import XCTest
@testable import Persephone_iOS
class Persephone_iOSTests: XCTestCase {
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}

22
iOSUITests/Info.plist Normal file
View File

@ -0,0 +1,22 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -0,0 +1,43 @@
//
// Persephone_iOSUITests.swift
// Persephone-iOSUITests
//
// Created by Daniel Barber on 2020-3-13.
// Copyright © 2020 Dan Barber. All rights reserved.
//
import XCTest
class Persephone_iOSUITests: XCTestCase {
override func setUp() {
// 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 its 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 tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use recording to get started writing UI tests.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testLaunchPerformance() {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) {
XCUIApplication().launch()
}
}
}
}

24
libmpdclient.patch Normal file
View File

@ -0,0 +1,24 @@
diff --git a/src/socket.c b/src/socket.c
index 8f684b2..65498f2 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
+#include <sys/time.h>
#ifdef _WIN32
# include <winsock2.h>
diff --git a/src/sync.c b/src/sync.c
index 3fefffe..ab96fb7 100644
--- a/src/sync.c
+++ b/src/sync.c
@@ -34,6 +34,7 @@
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
+#include <sys/time.h>
#ifndef _WIN32
#include <sys/select.h>