I have searched everywhere and I am unable to find a swifty example of how to use the Apple ITunesLibraryFramework. I have been trying to figure out how to use this Framework in Swift 5.2.
I want to get information directly from the Music library rather than having to rely on a XML library export file.
I have the below code in my playground and it prints nothing legible. How would I access the fields for playlists and mediaItems and be able to read them in human readable form?
I have installed the framework in the project. This is my project playground code:
import Foundation
import iTunesLibrary
var library:ITLibrary
do {
let library = try ITLibrary(apiVersion: "1.1")
let mediaItems = library.allMediaItems
let playlists = library.allPlaylists
print("Media Folder Location - \(String(describing: library.mediaFolderLocation))")
print("\nPlaylists - \(playlists)")
print("\nTracks - \(mediaItems)")
} catch let error as NSError {
print(error)
}
This is the ITLibrary.m file that I imported the header:
#import <Foundation/Foundation.h>
#import <iTunesLibrary/iTunesLibrary.h>
When I run the above code in the project playground all I get is a bunch of binary data for both playlists and mediaItems. All I want to do is iterate over the data and collect information from the library for use in my program. It's probably something easy, but I haven't found it yet.
EDIT: - After using @Vincent's answer I ran into another problem with the following code:
import Foundation
import iTunesLibrary
let library = try ITLibrary(apiVersion: "1.1")
typealias tracks = [NSNumber:TrackInfo]
var trackInfo = TrackInfo()
struct TrackInfo {
var title = ""
var artist = ""
var album = ""
var totalTime = 0
var year = 0
var persistentID = ""
var location:URL!
}
let songs = library.allMediaItems
let playlists = library.allPlaylists
for playlist in playlists {
if playlist.name.lowercased().contains("test-") {
print("New Playlist---------------\nName: \(playlist.name)")
for song in playlist.items {
trackInfo.title = song.title
print("\n\nNew Track-----------------\nTitle: \(trackInfo.title)")
if song.artist?.name != nil {
trackInfo.artist = song.artist?.name as! String
}
print("Artist: \(trackInfo.artist)")
trackInfo.album = song.album.title!
print("Albumn Name: \(trackInfo.album)")
trackInfo.totalTime = song.totalTime
print("Total Time: \(trackInfo.totalTime)")
trackInfo.year = song.year
print("Year: \(trackInfo.year)")
trackInfo.location = song.location!
print("Location: \(trackInfo.location!)")
var persistentID = song.persistentID
tracks.updateValue(song.persistentID, trackInfo)
}
}
}
The issue I'm having is getting the tracks info into the trackInfo dictionary. I'm trying to use the track persistentID (NSNumber) as the key for the dictionary, which I have declared. For some reason it isn't allowing me to use it.
Here's how you can have it print each playlist and track:
Each ITLibPlaylist
or ITLibMediaItem
object contains many information about each playlist/media item. To get only the name/title of each, you will have to iterate through the results to retrieve them.
For this example below, the name of each playlist's name is printed.
print("\nPlaylists -")
for playlist in playlists {
print(playlist.name)
}
Which will print (for example):
Playlists -
Library
Music
For this example below, the name of each track's name is printed.
print("\nTracks -")
for mediaItem in mediaItems {
print(mediaItem.title)
}
Which will print (for example):
Tracks -
Ev'ry Time We Say Goodbye
My Favorite Things
But Not for Me
Summertime
Edit: Here's the secondary solution to the secondary problem:
First things first, a dictionary should be initialised, instead of using typealias
.
typealias
only makes an alias for a pre existing type, like
typealias NumberWithAlotOfDecimals = Double
let a: NumberWithAlotOfDecimals = 10.1
let b: Double = 10.1
both will a
and b
are Double
, as NumberWithAlotOfDecimals
is just an alias for Double
.
Here's how to initialise:
//typealias tracks = [NSNumber:TrackInfo] // not this
var tracks = [NSNumber:TrackInfo]() // but this
Secondly, nullable objects should be properly handled
if let artistName = song.artist?.name {
trackInfo.artist = artistName
}
and
if let title = song.album.title {
trackInfo.album = title
}
if let location = song.location {
trackInfo.location = location
print("Location: \(location)")
}
instead of
if song.artist?.name != nil {
trackInfo.artist = song.artist?.name as! String
}
Please do not use !
to force unwrap nullable objects as that will cause runtime crashes when the object is nil
.
Lastly, this is the way to store key value into dictionary in Swift.
let persistentID = song.persistentID
//tracks.updateValue(song.persistentID, trackInfo) // not this
tracks[persistentID] = trackInfo // this