initial commit

This commit is contained in:
2024-07-28 01:04:21 +01:00
parent 4538f24737
commit dc80455f3e
9 changed files with 325 additions and 21 deletions

54
.gitignore vendored Normal file
View File

@@ -0,0 +1,54 @@
# Created by https://www.toptal.com/developers/gitignore/api/xcode,macos
# Edit at https://www.toptal.com/developers/gitignore?templates=xcode,macos
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### Xcode ###
## User settings
xcuserdata/
## Xcode 8 and earlier
*.xcscmblueprint
*.xccheckout
### Xcode Patch ###
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcodeproj/project.xcworkspace/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
**/xcshareddata/WorkspaceSettings.xcsettings
# End of https://www.toptal.com/developers/gitignore/api/xcode,macos

View File

@@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
F12B73452C55505E0064A230 /* NeuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F12B73442C55505E0064A230 /* NeuButton.swift */; };
F12B73472C55AC6B0064A230 /* PlaybackManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F12B73462C55AC6B0064A230 /* PlaybackManager.swift */; };
F12B73492C55C0C40064A230 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = F12B73482C55C0C40064A230 /* .gitignore */; };
F13839702C51BABD00B4814F /* InfinifiIOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F138396F2C51BABD00B4814F /* InfinifiIOSApp.swift */; };
F13839722C51BABD00B4814F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13839712C51BABD00B4814F /* ContentView.swift */; };
F13839742C51BABE00B4814F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F13839732C51BABE00B4814F /* Assets.xcassets */; };
@@ -14,6 +17,9 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
F12B73442C55505E0064A230 /* NeuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeuButton.swift; sourceTree = "<group>"; };
F12B73462C55AC6B0064A230 /* PlaybackManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackManager.swift; sourceTree = "<group>"; };
F12B73482C55C0C40064A230 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = SOURCE_ROOT; };
F138396C2C51BABD00B4814F /* InfinifiIOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InfinifiIOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
F138396F2C51BABD00B4814F /* InfinifiIOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfinifiIOSApp.swift; sourceTree = "<group>"; };
F13839712C51BABD00B4814F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -55,6 +61,9 @@
F13839712C51BABD00B4814F /* ContentView.swift */,
F13839732C51BABE00B4814F /* Assets.xcassets */,
F13839752C51BABE00B4814F /* Preview Content */,
F12B73442C55505E0064A230 /* NeuButton.swift */,
F12B73462C55AC6B0064A230 /* PlaybackManager.swift */,
F12B73482C55C0C40064A230 /* .gitignore */,
);
path = InfinifiIOS;
sourceTree = "<group>";
@@ -126,6 +135,7 @@
buildActionMask = 2147483647;
files = (
F13839772C51BABE00B4814F /* Preview Assets.xcassets in Resources */,
F12B73492C55C0C40064A230 /* .gitignore in Resources */,
F13839742C51BABE00B4814F /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -137,7 +147,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F12B73452C55505E0064A230 /* NeuButton.swift in Sources */,
F13839722C51BABD00B4814F /* ContentView.swift in Sources */,
F12B73472C55AC6B0064A230 /* PlaybackManager.swift in Sources */,
F13839702C51BABD00B4814F /* InfinifiIOSApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -275,6 +287,7 @@
DEVELOPMENT_TEAM = 5FG7YZ49ZA;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = infinifi;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@@ -304,6 +317,7 @@
DEVELOPMENT_TEAM = 5FG7YZ49ZA;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = infinifi;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF5",
"green" : "0xF1",
"red" : "0xEF"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2E",
"green" : "0x1E",
"red" : "0x1E"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xCC",
"green" : "0xC0",
"red" : "0xBC"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x5A",
"green" : "0x47",
"red" : "0x45"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x5A",
"green" : "0x47",
"red" : "0x45"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF4",
"green" : "0xD6",
"red" : "0xCD"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,21 +1,48 @@
//
// ContentView.swift
// InfinifiIOS
//
// Created by Kenneth on 24/07/2024.
//
import AVKit
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
@ObservedObject private var playbackManager = PlaybackManager()
private func toggleAudioPlayback() {
switch playbackManager.playbackState {
case .playing:
playbackManager.stop()
case .paused:
playbackManager.nextTrack()
}
}
var body: some View {
let buttonImageName = switch playbackManager.playbackState {
case .paused: "play"
case .playing: "pause"
}
VStack(alignment: .center) {
Text("infinifi")
.font(.title3)
.bold()
.monospaced()
.padding()
Spacer()
NeuButton(action: {
toggleAudioPlayback()
}) {
Image(systemName: buttonImageName)
.font(.system(size: 24))
.tint(.text)
}
Spacer()
}
.frame(
maxWidth: .infinity,
maxHeight: .infinity
)
.background(.base)
}
}

View File

@@ -1,10 +1,3 @@
//
// InfinifiIOSApp.swift
// InfinifiIOS
//
// Created by Kenneth on 24/07/2024.
//
import SwiftUI
@main

View File

@@ -0,0 +1,63 @@
import Foundation
import SwiftUI
struct NeuButtonStyle: PrimitiveButtonStyle {
private let onPressStateChange: (_ isPressed: Bool) -> Void
private let impact = UIImpactFeedbackGenerator(style: .medium)
init(onPressStateChange: @escaping (_: Bool) -> Void) {
self.onPressStateChange = onPressStateChange
}
func makeBody(configuration: Configuration) -> some View {
configuration.label
.onTapGesture {
configuration.trigger()
}
.onLongPressGesture(minimumDuration: 0) {} onPressingChanged: { isPressing in
onPressStateChange(isPressing)
impact.impactOccurred()
}
}
}
struct NeuButton<Content>: View where Content: View {
let content: () -> Content
let action: () -> Void
@State private var isPressed = false
init(action: @escaping () -> Void, @ViewBuilder content: @escaping () -> Content) {
self.content = content
self.action = action
}
var body: some View {
Button(action: action) {
Rectangle()
.fill(isPressed ? .base : .text)
.offset(
x: isPressed ? 0 : 4,
y: isPressed ? 0 : 4
)
.overlay {
ZStack {
Rectangle()
.fill(isPressed ? .text : .base)
content()
.foregroundStyle(isPressed ? .base : .text)
}
}
}
.border(.text, width: 2)
.frame(width: 64, height: 64)
.offset(
x: isPressed ? 6 : 0,
y: isPressed ? 6 : 0
)
.buttonStyle(NeuButtonStyle { isPressed in
self.isPressed = isPressed
})
}
}

View File

@@ -0,0 +1,39 @@
import AVKit
import Foundation
enum PlaybackState {
case playing
case paused
}
class PlaybackManager: ObservableObject {
@Published var playbackState: PlaybackState = .paused
@Published var hasError = false
private var audioPlayer = AVPlayer()
func nextTrack() {
let now = Date().timeIntervalSince1970
// add timestamp to the url to prevent caching
let playerItem = AVPlayerItem(url: URL(string: "https://infinifi.cafe/current.mp3?t=\(now)")!)
NotificationCenter.default.addObserver(self, selector: #selector(playbackFinished), name: AVPlayerItem.didPlayToEndTimeNotification, object: playerItem)
audioPlayer.replaceCurrentItem(with: playerItem)
audioPlayer.play()
playbackState = .playing
}
func stop() {
audioPlayer.pause()
playbackState = .paused
}
@objc
private func playbackFinished() {
NotificationCenter.default.removeObserver(self, name: AVPlayerItem.didPlayToEndTimeNotification, object: audioPlayer.currentItem)
if playbackState == .playing {
nextTrack()
}
}
}