swiftitunesituneslibrary

ITunes Library Framework in Swift 5.2


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.


Solution

  • 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