Making a deck of cards help

Hi, i'm new to swift. I was trying to make a deck of card but i keep getting this message. Does anyone have an idea on how to fix it?

Could you also post the type declarations for Card etc.?

Moving this to Using Swift

Xcode shows four errors.

First Error

let myCard = Card(rank: aRank!, suit: aSuit!)
             ^  Cannot invoke initializer for type 'Card' with an argument list of type '(rank: Card.Rank, suit: Card.Suit)'

We can't diagnose this without seeing the definition of the Card type.

Second Error

var deckOne = generateDeckOfCards()
              ^ Cannot use instance member 'generateDeckOfCards' within property initializer; property initializers run before 'self' is available

This error is telling you that you can't call generateDeckOfCards from this location because generateDeckOfCards is an instance method. The reason Swift doesn't let you call an instance method here is because, in general, the instance method might rely on the instance being fully initialized, and it's not fully initialized yet.

In this particular case, your generateDeckOfCards method doesn't use any instance methods or properties at all, so you can change it to be a static or class method instead:

static func makeDeckOfCards() -> [Card] {
    var myDeckOfCards: Array = [Card]()
    etc.
}

// Assuming this class is named DeckOfCards...
var deckOne = DeckOfCards.makeDeckOfCards()

If you fix the Second Error, Swift will give you an error on the next line:

var singleCard = deckOne[51]
                 ^ Cannot use instance member 'deckOne' within property initializer; property initializers run before 'self' is available

This new error is very similar to the Second Error. You can't use one instance property in another property's initializer. Instead, you need to write an instance initializer:

var deckOne = DeckOfCards.makeDeckOfCards()
var singleCard: Card

init() {
    // Property initializers run before init runs,
    // so deckOne is already initialized.
    singleCard = deckOne[51]        
}

Third Error

var theCard = cardsInfo(singleCard)
              ^ Cannot use instance member 'cardsInfo' within property initializer; property initializers run before 'self' is available

This is the same error as the Second Error. You can't call the instance method cardsInfo here.

Since cardsInfo doesn't use any instance properties or methods, and uses several Card properties, you can replace cardsInfo with a computed property on the Card type. Delete your cardsInfo method and put this at global scope:

extension Card {
    var cardInfo: (name: String, emoji: String, points: Int) {
        return (name: rank.rankDescription(), emoji: suit.rawValue, points: rank.cardsValue())
    }
}

Then you can try to declare theCard like this, and get a different error:

var theCard = singleCard.cardInfo
              ^ Cannot use instance member 'singleCard' within property initializer; property initializers run before 'self' is available

This is the same error you got after fixing the Second Error, and you fix this the same way. Initialize theCard in init:

var deckOne = DeckOfCards.makeDeckOfCards()
var singleCard: Card
var theCard: (name: String, emoji: String, points: Int)

init() {
    // Property initializers run before init runs,
    // so deckOne is already initialized.
    singleCard = deckOne[51]
    theCard = singleCard.cardInfo
}

Fourth Error

theCard.name
^ Expected declaration

You are directly inside a class or struct declaration here, so the only things allowed are declarations. theCard.name is not a declaration. Declarations usually start with let, var, or func.

I guess, since you are editing a playground, you wanted the playground to show the value of theCard.name here. That won't work for two reasons. First, playground execution stops at the first error. Second, you need to create a DeckOfCards instance and then you can ask it for its theCard.

class DeckOfCards {
    static func makeDeckOfCards() {
         blah blah blah
    }

    var deckOne = DeckOfCards.makeDeckOfCards()
    var singleCard: Card
    var theCard: (name: String, emoji: String, points: Int)

    init() {
        // Property initializers run before init runs,
        // so deckOne is already initialized.
        singleCard = deckOne[51]
        theCard = singleCard.cardInfo
    }
} // <-- This brace ends the declaration of DeckOfCards.

// AFTER ending the declaration of DeckOfCards, at the global scope:
let deck = DeckOfCards()
let card = deck.theCard
card.name
card.emoji
card.points
5 Likes

It looks there like the enum Rank doesn't have a closing brace, so the brace that you think is closing Card, is actually closing Rank. Insert one after the closing brace of func cardsValue().

I think the main issues you're seeing above, Cannot use instance member 'singleCard' within property initializer; property initializers run before 'self' is available are a direct result of not closing this enum properly. If you close this enum, then the } after it will close Card, which will push these declarations outside of Card.

It can also be helpful, in Xcode, to select all your code and press Ctrl+I, which will re-indent your code the way the compiler sees it. After doing that, if something is indented, you know it's inside something else.

5 Likes

Just a note: why can't Suit be CaseIteratable so you can do Suit.allCases.map { $0.rawValue }?

1 Like

You may also be interested in this example Swift Package: GitHub - apple/example-package-deckofplayingcards: Example package for use with the Swift Package Manager