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 trialKyle Papili
9,112 PointsArgument 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
47,913 PointsYou 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 struct
s and enum
s are value types, but class
es 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
Kyle Papili
9,112 PointsKyle Papili
9,112 PointsThank 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!