iosswiftxcodeswiftuics193p

Can't Infer Generic Type 'S' in SwiftUI 'Text' in CS193p project, in Xcode


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!


Solution

  • 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))
                }