2

Background

I am following Stanfords' CS193p iOS Development online course. I am with Xcode 11.3 and Swift 5.1.2.

Problem

By the end of lecture 5. ViewBuilder + Shape + ViewModifier, an error occurred to me, which is the Swift compiler reported 'Generic parameter 'S' could not be inferred'. Snapshot: IDE complains about it

Code Snippets

Code in EmojiMemoryGameView.swift

import SwiftUI

struct EmojiMemoryGameView: View {
    @ObservedObject var viewModel: EmojiMemoryGame
    var body: some View {
        Grid(viewModel.cards) { card in
            CardView(card: card).onTapGesture(perform: {
                self.viewModel.choose(card: card)
            })
            .padding(5)
        }
            .padding()
            .foregroundColor(Color.orange)
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let game = EmojiMemoryGame()
        game.choose(card: game.cards[0])
        return EmojiMemoryGameView(viewModel: game)
    }
}


struct CardView: View {
    var card: MemoryGame<String>.Card
    
    var body: some View {
        GeometryReader { geometry in
            self.body(for: geometry.size)
        }
    }
    
    @ViewBuilder
    private func body(for size: CGSize) -> some View {
        if card.isFaceUp || !card.isMatched {
            ZStack {
                Pie(startAngle: Angle.degrees(-90),
                    endAngle: Angle.degrees(-10),
                    clockwise: true)
                    .padding(5)
                    .opacity(0.3)
                Text(card.content)
                    .font(Font.system(size: fontSize(for: size)))
                }.cardify(isFaceUp: card.isFaceUp)
            //.modifier(Cardify(isFaceUp: card.isFaceUp))
        }
    }
    
    
    private func fontSize(for size: CGSize) -> CGFloat {
        return min(size.width, size.height) * fontScaleFactor
    }
    // MARK: - Drawing Constants
    
    private let fontScaleFactor: CGFloat = 0.75
}

}

Code in MemoryGame.swift

import Foundation

struct MemoryGame<CardContent> where CardContent: Equatable { // costraints and gains
    private(set) var cards: Array<Card>
    private var indexOfTheOneAndOnlyFaceUpCard: Int? {
        get {
            cards.indices.filter { cards[$0].isFaceUp }.only
        }
        set {
            for index in cards.indices {
                // newValue is a var only appears in set method
                cards[index].isFaceUp = index == newValue
            }
        }
    }
    
    init(numberOfPairsOfCards: Int, cardContentFactory: (Int) -> CardContent) {
        cards = Array<Card>()
        let maxIndex = 2 * numberOfPairsOfCards - 1
        var pairIndices = [Int]()
        for i in 0...maxIndex {
            let randomNumber = Double.random(in: 0...1)
            if randomNumber >= 0.5 {
                pairIndices.insert(i/2, at: 0)
            } else {
                pairIndices.append(i/2)
            }
        }
        for i in 0...maxIndex {
            let pairIndex = pairIndices[i]
            let content = cardContentFactory(pairIndex)
            cards.append(Card(id: i, content: content))
        }
    }
                    
    mutating func choose(card: Card) {
        print("card chosen: \(card)")
        if let chosenIndex = cards.firstIndex(matching: card), !cards[chosenIndex].isFaceUp, !cards[chosenIndex].isMatched {
            // comma here is like a sequential "AND"
            if let potentialMatchIndex = indexOfTheOneAndOnlyFaceUpCard {
                if cards[chosenIndex].content ==  cards[potentialMatchIndex].content {
                    cards[chosenIndex].isMatched = true
                    cards[potentialMatchIndex].isMatched = true
                }
            } else {
                indexOfTheOneAndOnlyFaceUpCard = chosenIndex
            }
            self.cards[chosenIndex].isFaceUp = true
        }
    }
    
    struct Card: Identifiable {
        /* Member variable 'id' is essential for the Identifiable protocol */
        var id: Int
        
        var isFaceUp: Bool = false
        var isMatched: Bool = false
        var content: CardContent
    }
}

However, when I alt-click the variable, it clearly shows the type is String. Snapshot: Type Declaration

My current project code is also available at Github

So how come the error occurred & how can I fix it?

Will appreciate your help!

ht.lee
  • 23
  • 5

1 Answers1

0

In your struct CardView: View, you have

if card.isFaceUp || !card.isMatched {
            ZStack {
                Pie(startAngle: Angle.degrees(-90),
                    endAngle: Angle.degrees(-10),
                    clockwise: true)
                    .padding(5)
                    .opacity(0.3)
                Text(card.content)
                    .font(Font.system(size: fontSize(for: size)))
                }.cardify(isFaceUp: card.isFaceUp)
            //.modifier(Cardify(isFaceUp: card.isFaceUp))
        }

add a return before ZStack and that should fix your issue.

if card.isFaceUp || !card.isMatched {
                return ZStack {
                    Pie(startAngle: Angle.degrees(-90),
                        endAngle: Angle.degrees(-10),
                        clockwise: true)
                        .padding(5)
                        .opacity(0.3)
                    Text(card.content)
                        .font(Font.system(size: fontSize(for: size)))
                    }.cardify(isFaceUp: card.isFaceUp)
                //.modifier(Cardify(isFaceUp: card.isFaceUp))
            }
Shamas S
  • 7,507
  • 10
  • 46
  • 58
  • It works but this is amazing! Thx a lot. So what happens after I added a return and what’s the difference? – ht.lee Jul 29 '20 at 05:28
  • without the return, compiler can automatically figure out the return type and match it with the function signature. But if we have multiple statements inside, which in your case is the `if` statement, you have to explicitly tell which one is being returned. – Shamas S Jul 29 '20 at 05:44