As Protocol doesn't conform to itself?, you are getting the error: 
cannot automatically synthesize Decodable because ArtistData does not conform to Decodable
Codable requires all the properties of the conforming type are of concrete type or constrained to Codable by means of generics. The type of the properties must be a full fledged type but not Protocol. Hence your second approach doesn't work out as you expected. 
Though @vadian already posted an excellent answer, I'm posting another approach which may be more inline with your thought process. 
First, the Artist type should reflect the original payload data structure: 
struct Artist: Codable {
    let id: String
    let data: Either<BandArtist, IndividualArtist>
}
Where the Either type is like: 
enum Either<L, R> {
    case left(L)
    case right(R)
}
// moving Codable requirement conformance to extension
extension Either: Codable where L: Codable, R: Codable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            // first try to decode as left type
            self = try .left(container.decode(L.self))
        } catch {
            do {
                // if the decode fails try to decode as right type
                self = try .right(container.decode(R.self))
            } catch {
                // both of the types failed? throw type mismatch error
                throw DecodingError.typeMismatch(Either.self,
                                   .init(codingPath: decoder.codingPath,
                                         debugDescription: "Expected either \(L.self) or \(R.self)",
                                         underlyingError: error))
            }
        }
    }    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case let .left(left):
            try container.encode(left)
        case let .right(right):
            try container.encode(right)
        }
    }
}
This should essentially solve your problem in the first place. 
Now to extend this answer a little far, as I can see what you tried to achieve, where you deliberately tried to use constant value for the type of both IndividualArtist and BandArtist, you need to account for the decoding part manually. Otherwise the values of type properties here will be dumped by the JSON payload at the time of decoding (when the auto synthesized init(from decoder: Decoder) gets called). That essentially means, if the JSON is: 
{
  "id":"123",
  "data":{
    "type":"individual",
    "name":"Queen"
  }
}
the JSONDecoder still will decode this as a BandArtist whereas this should not be the case as far as I understand like the way you are concerned. To tackle this, you need to provide custom implementation of Decodable requirement. (@vadian's answer account for this with the nested container) 
Below is how you can do it from the types themselves: 
struct IndividualArtist: Codable {
    let type = ArtistType.individual
    let firstName: String
    let lastName: String
}
extension IndividualArtist {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(ArtistType.self, forKey: .type)
        guard self.type == type else {
            throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Payload doesn't match the expected value"))
        }
        firstName = try container.decode(String.self, forKey: .firstName)
        lastName = try container.decode(String.self, forKey: .lastName)
    }
}
struct BandArtist: Codable {
    let type = ArtistType.band
    let name: String
}
extension BandArtist {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(ArtistType.self, forKey: .type)
        guard self.type == type else {
            throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Payload doesn't match the expected value"))
        }
        name = try container.decode(String.self, forKey: .name)
    }
}