NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

SOLVED: How do I solve "Generic parameter 'T' could not be inferred" ?

Forums > SwiftUI

I'm on my project and I want to shuffle the array in my JSON file with .onAppear() but then I get an error like the one in the code below. I tried to find a solution but I just don't know what to do so if someone can help me solving this error I'd appreciate it.

struct HiraganaQuiz: View {

@State private var hiraganas: [Japanese] =  [] 

     var body: some View {
        ZStack {
          // body content
        }.onAppear {
          hiraganas = Bundle.main.decode("Hiragana.json").shuffled()
        } // this is where the error happens
     }
 }

this is the decode manager:

    extension Bundle  {

    func decode<T: Codable>(_ file: String) -> T {

        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle.")

        }

        guard let data = try? Data(contentsOf: url) else {
            fatalError("Failed to load \(file) from bundle.")
        }

        let decoder = JSONDecoder()

        guard let loadedData = try? decoder.decode(T.self, from: data) else {
            fatalError("Failed to decode \(file) from bundle.")
        }
        return loadedData
    }
}

   

It appears that you haven't passed T into the function. Also as you are only decoding the JSON here, so it is cleaner and better that the generic type is only Decodable. You will not be encoding here, it shouldn't be Codable in the function definition.

func decode<T: Codable>(_ file: String) -> T {

becomes

func decode<T:Decodable>(_ type: T.type, _ file: String) -> T {

And you may want to try, assuming Japanese is a type that is Codable or at least Decodable.

 }.onAppear {
          hiraganas = Bundle.main.decode(Japanese.self, "Hiragana.json").shuffled()
        }

   

Thank you so much for answering! Now I've got another error, that is " 'type' is not a member type of type 'T' " on this code :

 func decode<T:Decodable>(_ type: T.type, _ file: String) -> T {

   

@Schoco has a hard type of problem! Type!

Now I've got another error, that is " 'type' is not a member type of type 'T' " on this code

Function Signatures

Are you comfortable reading function signatures?

If not, here's a short note I wrote to another programmer about signatures. See -> Function Signatures

You have a complex signature, but you should practice reading them (out loud!) until they make sense to you. Let's take a look at your signature and see if we can decode it. It might help you understand why you're getting errors.

I use two techniques.
One: Eat the elephant one bite at a time. Let's break your signature into bite sized pieces.
Two: Use the Rubber Duck technique to explain what you're doing.

// Your function signature.....
func decode<T:Decodable>(_ type: T.type, _ file: String) -> T {

First Bite: func decode

This is straight forward. You've declared a function and given it a name: decode

Second Bite: <T:Decodable>

This can be strange to understand. You have to study generics to really know what's going on here. If you don't know generics, you may want to stop here and watch @twoStraws videos introducing generics.

In short, this bit of code tells us you have declared a decode function that doesn't work on just Ints, or Strings, or Doubles. In fact if can work on any Swift type with one major condition. Whatever type it works on must conform to the Decodable protocol.

What does that mean? Well, you can pass in an Album struct, or a Person struct, or a Menu struct, or an array of Student structs, or an array of Integers. You can pass in a single DungeonMap struct. But whatever you pass to this function must conform to Decodable!

Third Bite: ( _ type: T.type, xxxxxxxxxxx)

The parentheses tell you that these are the parameters that you pass into the function. We're eating one bite at a time here, so focus on the first parameter.

The underscore is a "don't care". When you call this function, won't need to provide a parameter name for this incoming parameter. But inside your function, you will use a variable named type.

Fourth Bite: T.type

What is the Swift type of the variable named type? Is it an Integer? Is it a String?
Nope! T.type refers back to the second bite. T is the type of item you want to decode. You have to specify the type. For example, if you are decoding an array of Strings, or an array of Albums, or an array of TravelDestinations you specify:

// Examples of specifing the type you want your generic function to decode.
[String].self            // <-- You want to decode an array of String
[Album].self             // <-- You want to decode an array of Album objects
[TravelDestination].self // <-- Decode an array of TravelDestinations.
DungeonMap.self          // <-- Decode a single DungeonMap object.

Of course, the compiler will check that the type you specify conforms to Decodable protocol.

Note!

This is tricky for some new developers. Your JSON file may contain four dozen albums. What is the type you want to decode? Do you decode an Album struct? No! You are looking at an array of Album objects. So you need to declare [Album].self, not Album.self!

Fifth Bite: ( xxxxxxxxxx, _ file: String)

Ok, back into familiar territory. Focus on the second function parameter. Again, when calling this function, Swift won't require you to provide a parameter name. But within your function, you will call this file. This is the name of the file that you pull from your iPhone and decode.

Final Bite: -> T

Read the signature! This tells you that when this function is finished, it will return an object of the type that you previously specified. Did you specify an array of Recipes? [Recipe].self ? That's what you'll get back. Did you specify a single Recipe? Recipe.self ? Your function will (attempt to) return a single Recipe object. Pay attention to the details here.

Good luck and

Keep Coding!

See -> How to eat an elephant
See -> Rubber Ducks 🐤

1      

Swift should be able to infer the return type of decode(_:) based on what you are assigning it to, without needing an explicit T.type parameter. The problem is that introducing shuffled() into the mix confuses the compiler because the return type of shuffled is [Self.Element] but nowhere do you say what type Self is.

Instead of this:

.onAppear {
    hiraganas = Bundle.main.decode("Hiragana.json").shuffled() 
    //decode return [Self.Element] but what is Self?
}

Try this:

.onAppear {
    hiraganas = Bundle.main.decode("Hiragana.json") 
    //decode returns [Japanese]
    hiraganas.shuffle()
    //and we just shuffle in place
}

1      

@obelix thank you so much for your work to help me out! I don't think I have a solid understanding of generics and things like you've shown me there so I'll definitely try to get it sink in... I'll take my time !

   

@roosterboy thank you for your answer ! I tried your solution but I got some errors on other parts of my code so I'll try and figure it out for a while...

   

@schocolate24 - konnichiwa , can you please try below code for decoder, see i made a minor change to parameter , so compiler better understands the type, after that look below for rest of code

extension Bundle {
    func decode<T: Decodable>(_ file: String, _ type: T.Type = T.self) -> T {

        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle.")
        }

        guard let data = try? Data(contentsOf: url) else {
            fatalError("Failed to load \(file) from bundle.")
        }

        let decoder = JSONDecoder()

        guard let loaded = try? decoder.decode(T.self, from: data) else {
            fatalError("Failed to decode \(file) from bundle.")
        }

        return loaded
    }
}
@State private var hiraganas: [Japanese] =  []
    var body: some View {
        ZStack {
                  // body content
                }.onAppear {
                    hiraganas = Bundle.main.decode("Hiragana.json", [Japanese].self).shuffled()
                }

    }

1      

@AmitShrivastava Thank you for you help :) errors are gone and my code works now ! I'll now try to understand how it actually worked ;)

   

Hacking with Swift is sponsored by Play

SPONSORED Play is the first native iOS design tool created for designers and engineers. You can install Play for iOS and iPad today and sign up to check out the Beta of our macOS app with SwiftUI code export. We're also hiring engineers!

Click to learn more about Play!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.