Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Build a Vending Machine App in Swift 2.0 Loading Data From a Resource Finishing Touches

Argument type 'VendingItem' does not conform to expected type 'itemType'

I know exactly why I am receiving this error, but I am just not sure how to fix it. Up in the VendingMachineType protocol, inventory is defined as below.

protocol VendingMachineType {
    var selection: [VendingSelection] { get } //Read only b/c you only have a set selection
    var inventory: [VendingSelection: ItemType]  { get set } //Inventory changes, must be get/set
    var amountDeposited: Double { get set }

    init(inventory: [VendingSelection: ItemType])
    func vend(selection: VendingSelection, quantity: Double) throws
    func deposit(amount: Double)
}

Inventory is clearly defined as a dictionary with keys of type 'VendingSelection' and values of type 'ItemType'. Although, in the InventoryUnarchiver class, specifically the vendingInventoryFromDictionary function we define item as a 'VendingItem' rather than an 'itemType'.

let item = VendingItem(price: price, quantity: quantity)

Not quite sure what I should do here. I will include my entire code below for further review. Thank you all very much.

  • Kyle P.
import Foundation

//Protocols

protocol VendingMachineType {
    var selection: [VendingSelection] { get } //Read only b/c you only have a set selection
    var inventory: [VendingSelection: ItemType]  { get set } //Inventory changes, must be get/set
    var amountDeposited: Double { get set }

    init(inventory: [VendingSelection: ItemType])
    func vend(selection: VendingSelection, quantity: Double) throws
    func deposit(amount: Double)
}

protocol ItemType {
    var price: Double { get }
    var quantity: Double { get set }
}

//Error Types
enum InventoryError: ErrorType {
    case invalidResource
    case conversionError
    case invalidKey
}


//HELPER Classes
class PListConverter {
    class func dictionaryFromFile(resource: String, ofType type: String) throws -> [String: AnyObject] {
        //resource is resource location. type is resource type

        guard let path = NSBundle.mainBundle().pathForResource(resource, ofType: type) else {
            //Stuff in here runs if it returns null
            throw InventoryError.invalidResource
        }

        guard let dictionary = NSDictionary(contentsOfFile: path), let castDictionary = dictionary as? [String: AnyObject] else {
            throw InventoryError.conversionError
        }

        return castDictionary
    }
}

class InventoryUnarchiver {
    class func vendingInventoryFromDictionary(dictionary: [String: AnyObject]) throws -> [VendingSelection : ItemType] {
        //Return type is specified as a protocol so  we MUST return "[VendingSelection : ItemType]"
        var inventory: [VendingSelection : ItemType] = [:] //Creates empty variable dictionary

        for (key, value) in dictionary {
            if let itemDict = value as? [String : Double],
                let price = itemDict["price"],
                let quantity = itemDict["quantity"]{
                //Second two let statements check to see if exists an itemDict key with string price and quantity (unwrapping optionals)
                let item = VendingItem(price: price, quantity: quantity)

                //Now we have price and quantity of type double. key as type string and value of type anyObject. We need to update the value of the inventory dictionary but to updateValue, we need key to be of type VendingSelection.
                //VendingSelection is an enum with a raw value of String
                //So, if we assign key to VendingSelection(rawValue: key) and the key string matches a VendingSelection case then key becomes of type VendingSelection
                guard let key = VendingSelection(rawValue: key) else {
                    //Means key does not match with enum case (THROW ERROR)
                    throw InventoryError.invalidKey
                }//Checks to see if key(String) matches a case of VendingSelection. If so key(String) becomes key(VendingSelection: matchingCase)
                inventory.updateValue(item, forKey: key)
            }
        }
    return inventory
    }
}

//CONCRETE TYPES

//Enums
enum VendingSelection: String {
    case Soda
    case DietSoda
    case Chips
    case Cookie
    case Sandwich
    case Wrap
    case CandyBar
    case PopTart
    case Water
    case FruitJuice
    case SportsDrink
    case Gum
}

struct VendingItem {
    //Value types (structs/enums) ARE THINGS
    //refrence types (protocols) DO THINGS
    let price: Double
    var quantity: Double
}

class VendingMachine: VendingMachineType {
    //Classes are good at modeling state, inventory can change
    let selection: [VendingSelection] = [.Soda, .DietSoda, .Chips, .Cookie, .Sandwich, .Wrap, .CandyBar, .PopTart, .Water, .FruitJuice, .SportsDrink, .Gum]
    var inventory: [VendingSelection : ItemType]
    var amountDeposited: Double = 10.0

    required init(inventory: [VendingSelection: ItemType]) {
        self.inventory = inventory
    }

    func vend(selection: VendingSelection, quantity: Double) throws {
        //add code
    }

    func deposit(amount: Double) {
        //add code
    }

}

1 Answer

Michael Hulet
Michael Hulet
47,912 Points

You just need to tell Swift that VendingItem is an ItemType. In other words, you need to tell Swift that VendingItem conforms to the ItemType protocol. Functionally speaking, you've already written all the code required to conform to ItemType, so you just need to add the annotation to VendingItem's declaration. You declare protocol conformance in Swift with the same syntax that you would use to specify that a class is a subclass of another class, like this:

struct VendingItem: ItemType{ //The only difference from your code is here on this line
    let price: Double
    var quantity: Double
}

Also, one comment in your code that bothered me is that you said that protocols are reference types, which they're not necessarily. Protocols are basically just an abstract concept that is neither a value type or a reference type by itself. It only becomes a thing to Swift when something else conforms to that protocol. That something else can (generally) be either a struct or a class or even an enum. Both structs and enums are value types, but classes are reference types, but all can conform to a protocol. Thus, it's most correct to say that a protocol on its own is neither a value type or a reference type, but it (in a way) takes on the type of whatever conforms to it

Thank you so much for that explanation on having the VendingItem struct conform to the ItemType protocol. That clears a lot of confusion up. Additionally, thank you for the wonderful explanation of protocols, I am just learning them and up until this point I have still been a bit confused as to what exactly a protocol is but your explanation makes perfect sense to me. I cannot thank you enough for helping me out, its a community as great as this one that makes Treehouse the perfect platform.

Thanks again!