Search code examples
swiftprotocolsswift-protocols

Can I use Swift's map() on Protocols?


I have some model code where I have some Thoughts that i want to read and write to plists. I have the following code:

protocol Note {
    var body: String { get }
    var author: String { get }
    var favorite: Bool { get set }
    var creationDate: Date { get }
    var id: UUID { get }
    var plistRepresentation: [String: Any] { get }
    init(plist: [String: Any])
}

struct Thought: Note {
    let body: String
    let author: String
    var favorite: Bool
    let creationDate: Date
    let id: UUID
}

extension Thought {
    var plistRepresentation: [String: Any] {
        return [
            "body": body as Any,
            "author": author as Any,
            "favorite": favorite as Any,
            "creationDate": creationDate as Any,
            "id": id.uuidString as Any
        ]
    }

    init(plist: [String: Any]) {
        body = plist["body"] as! String
        author = plist["author"] as! String
        favorite = plist["favorite"] as! Bool
        creationDate = plist["creationDate"] as! Date
        id = UUID(uuidString: plist["id"] as! String)!
    }
}

for my data model, then down in my data write controller I have this method:

func fetchNotes() -> [Note] {
    guard let notePlists = NSArray(contentsOf: notesFileURL) as? [[String: Any]] else {
        return []
    }
    return notePlists.map(Note.init(plist:))
}

For some reason the line return notePlists.map(Note.init(plist:)) gives the error 'map' produces '[T]', not the expected contextual result type '[Note]' However, If I replace the line with return notePlists.map(Thought.init(plist:)) I have no issues. Clearly I can't map the initializer of a protocol? Why not and what's an alternate solution?


Solution

  • If you expect to have multiple types conforming to Note and would like to know which type of note it is stored in your dictionary you need to add an enumeration to your protocol with all your note types.

    enum NoteType {
        case thought 
    }
    

    add it to your protocol.

    protocol Note {
        var noteType: NoteType { get }
        // ...
    }
    

    and add it to your Note objects:

     struct Thought: Note {
         let noteType: NoteType = .thought
         // ...
      }
    

    This way you can read this property from your dictionary and map it accordingly.