Oluwatobi Omotayo
Oluwatobi Omotayo

Reputation: 1879

Working with array with different data types in Swift

I'm working on a music application that is able to play a playlist of songs, I have two data types representing a song in my project; "Track" which is of NSManagedObject for songs saved on the device by the user, and "JSONTrack" which represents songs decodable from a json web service. Users should be able to add both types to a an array of playlist. How do I achieve this with Swift, making an array for the different data types and work on that array: My current code handling one of the data types looks like this:

var playlistTracks = [Track]()

@objc fileprivate func handlePrevTrack() {
    if playlistTracks.isEmpty {
        return
    }

    let currentTrackIndex = playlistTracks.index { (tr) -> Bool in
        return self.track?.trackTitle == tr.trackTitle && self.track?.albumTitle == tr.albumTitle
    }

    guard let index = currentTrackIndex else { return }

    let prevTrack: Track

    if index == 0 {
        let count = playlistTracks.count
        prevTrack = playlistTracks[count - 1]
    } else {
        prevTrack = playlistTracks[index - 1]
    }

    self.track = prevTrack
}

@objc func handleNextTrack() {
    if playlistTracks.count == 0 {
        return
    }

    let currentTrackIndex = playlistTracks.index { (tr) -> Bool in
        return self.track?.trackTitle == tr.trackTitle && self.track?.albumTitle == tr.albumTitle
    }

    guard let index = currentTrackIndex else { return }

    let nextTrack: Track
    if index == playlistTracks.count - 1 {
        nextTrack = playlistTracks[0]
    } else {
        nextTrack = playlistTracks[index + 1]
    }

    self.track = nextTrack

}

handling next and previous selection. I would like to do the same for two different types of songs which are represented by two different data types.

Upvotes: 0

Views: 533

Answers (3)

Au Ris
Au Ris

Reputation: 4659

Using a protocol is probably the most common, but enum also works well.

To elaborate on the enum option:

class JSONTrack: NSObject {}
class OtherTrack: NSObject {}

enum Track {
    case jsonTrack(JSONTrack)
    case otherTrack(OtherTrack)

    // enum can be handy if you want to do type checking
    // and e.g. present specific data for that type
    var label: String {
        switch self {
        case .jsonTrack:
            return "Json track"
        case .otherTrack:
            return "Other track"
        }
    }
}

let jsonTrack = JSONTrack()
let otherTrack = OtherTrack()
let tracks: [Track] = [Track.jsonTrack(jsonTrack), Track.otherTrack(otherTrack)]

let labelOfTrack1 = tracks.first!.label
print(labelOfTrack1)
// prints "Json track"

Upvotes: 1

Francescu
Francescu

Reputation: 17054

Multiple solutions to your problem here

1. Use a protocol

You could make both JSONTrack and Track conforms to a protocol named TrackProtocol for instance with common method names. Then you could manipulate your array of TrackProtocol seamlessly.

Best solution

2. Use an enum

Create a TrackEnum enum containing both.

enum TrackEnum {
    case json(JSONTrack)
    case coreData(Track)
}

Then your array is array of TrackEnum and you extract every time which one it is.

3. Use Any

You can do an array of Any and check at runtime for the content type.

Worst solution.

Upvotes: 1

juhan_h
juhan_h

Reputation: 4011

Use a protocol that has the methods/properties necessary for next and previous actions. Have both of your track types implement the protocol. Have your array have the type of the protocol.

protocol Track {
    title: String
    albumTitle: String
    // other method and properties
}

class JSONTrack: Track {
    // implementation
}

class CoreDataTrack: Track {
    // implementation
}

let tracks = [Track]()

Upvotes: 1

Related Questions