Hi,
I have been working on a project that enables users to listen to their favorite music using a streaming service, which so far was Spotify. The app had a programmable 3D/2D interface with the ability to connect to devices in your home and have them react to music. As of September 2024, Spotify decomissioned their Audio Analysis API. I have seen other posts mention playing Apple Music through AVFoundation, which would break DRM and so it’s not supported. However, the Spotify Audio Analysis API does not allow for a full frequency reconstruction. It is entirely temporal data on beats, kicks, loudness, and timbre changes, which themselves are operators on the spectral data from the FFT. It would be very useful for the developer community if we get the ability to do this and it will probably Apple Music among developers and those who use their apps a lot more.
Would love to hear your thoughts about this and Happy New Year!
Post
Replies
Boosts
Views
Activity
I have a CoreData database, and I have attempted to migrate it to an app group to share it between my main app and a widget extension. The database seems that seems to be fetched by the app and the widget seem to be completely separate from each other.
My CoreData code (persistence.swift)
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let newUserData = Userdata(context: viewContext)
newUserData.hourly = 15.3
let newSession = Session(context: viewContext)
newSession.id = UUID()
newSession.start = Date()
newSession.end = Date()
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "FManger")
let storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.omaribrahim-uchicago.FManger")!.appendingPathComponent("FManger.sqlite")
var defaultURL: URL?
if let storeDescription = container.persistentStoreDescriptions.first, let url = storeDescription.url {
defaultURL = FileManager.default.fileExists(atPath: url.path) ? url : nil
}
if defaultURL == nil {
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeURL)]
}
container.loadPersistentStores(completionHandler: { [unowned container] (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
if let url = defaultURL, url.absoluteString != storeURL.absoluteString {
let coordinator = container.persistentStoreCoordinator
if let oldStore = coordinator.persistentStore(for: url) {
do {
try coordinator.migratePersistentStore(oldStore, to: storeURL, options: nil, withType: NSSQLiteStoreType)
} catch {
print(error.localizedDescription)
}
// delete old store
let fileCoordinator = NSFileCoordinator(filePresenter: nil)
fileCoordinator.coordinate(writingItemAt: url, options: .forDeleting, error: nil, byAccessor: { url in
do {
try FileManager.default.removeItem(at: url)
} catch {
print(error.localizedDescription)
}
})
}
}
})
}
}
My Widget data constructor and main function
import WidgetKit
import SwiftUI
import Intents
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), data: .previewData, error: false, configuration: ConfigurationIntent())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), data: .previewData, error: false, configuration: configuration)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
@FetchRequest(
sortDescriptors:[NSSortDescriptor(key: "start", ascending: false)],
animation: .default)
var sessions: FetchedResults<Session>
if (sessions.count > 0) {
let checkedIn = sessions[0].end != nil
let totalMinutes = totalMinutes(sessions: sessions)
var auxMinutes = 0.0
if (checkedIn) {
auxMinutes = timeBetween(fromDate: sessions[0].start!, toDate: Date())
}
let currentDate = Date()
for i in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: i, to: currentDate)!
let cSession = fromMinutes(minutes: (Double(i) * 60) + auxMinutes)
let widgetData = SimpleEntry.PayData(currentSession: cSession,
grossTotal: totalMinutes * 15.3 + cSession,
paycheckDay: Date() + (7 * 24 * 60 * 60),
checkedIn: checkedIn)
let entry = SimpleEntry(date: entryDate,
data: widgetData,
error: false,
configuration: configuration)
entries.append(entry)
}
}
else {
let entry = SimpleEntry(date: Date(),
data: .error,
error: true,
configuration: configuration)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
@main
struct Widgets: Widget {
let kind: String = "Widgets"
let persistenceController = PersistenceController.shared
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
WidgetsEntryView(entry: entry)
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
.configurationDisplayName("Pay Tracker")
.description("Check your current shift's earning, total earnings, and the date of your next payment in a glance.")
}
}
I am pretty sure I did something wrong, but what is it?