-1

I need some kind help in SwiftUI.

I have a model below that takes in JSON data. When I want to initialize it as an empty array, it should be simple, just stating Answers, but how can I initialize it not as an array but an empty single data set.

struct Answers: Identifiable, Decodable  {
    let id: Int
    let questionsUsersId: Int
    let reactions: [Reactions]

enum CodingKeys: String, CodingKey {
        case id = "questions_id"
        case questionsUsersId = "questions_users_id"
        case reactions = "reactions"
    }

init(from decoder: Decoder) throws {

    let container = try decoder.container(keyedBy: CodingKeys.self)

    self.id = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0
    self.questionsUsersId = try container.decodeIfPresent(Int.self, forKey: .questionsUsersId) ?? 0
    self.reactions = try container.decodeIfPresent([Reactions].self, forKey: .reactions) ?? [Reactions]()
}


 }

I've tried var answer: Answers = Answers( ) but got the errors below:

//Missing argument for parameter 'from' in call
//Insert 'from: <#Decoder#>'

The reason I want to initialize it empty is because I need to hand over the default value of it when the data model is nil. (i.e. what should I write in the xxx?)

 @ObservedObject var receiveAnswersViewModel = ReceiveAnswersViewModel()
  //abbreviate
            NavigationLink(destination: AnswerDetailView(answer: receiveAnswersViewModel.answer ?? xxx), isActive: $shouldNavigate) {
                    
                        ForEach(myPageViewModel.answers(forFilter: selectedFilter)?.answers ?? [Answers]()) { answer in
                         
                         AnswerCell(answer: answer)
                                .gesture(TapGesture()
                                                .onEnded({ _ in
                                                    //your action here
                                    self.answersId = answer.answersId
                                    receiveAnswersViewModel.fetchAnswers(answers_id: answersId ?? 0)
                                       }))
                         }
                    }

This is what I'm trying to do above: Show several cells using NavigationLink. (Imagine that the cells are tweets.) Press one of the tweets in the list → Call fetchAnswers func in the View Model which takes the pressed tweet id as the argument → Returns the specific tweet detail info→ Put it in the Answer model → Hand it over to the View → Navigate to the Tweet Detail View.

View Model code is below:

class ReceiveAnswersViewModel: ObservableObject {
@Published var answer: Answers?

func fetchAnswers(answers_id: Int){
//abbreviate
self.answer = try JSONDecoder().decode(Answers.self, from: data)
}

Thanks in advance.

Hiro
  • 155
  • 1
  • 10
  • Sorry, I meant to write "When I want to initialize it as an empty array, it should be simple, just stating [Answers]()" at the beginning. – Hiro Dec 09 '21 at 04:00

1 Answers1

1

A reasonable way is to add a static method to create an empty instance. In this case you have also to add the init method

By the way name the struct in singular form, an instance represents likely one Answer

struct Answer: Identifiable, Decodable  {
    let id: Int
    let questionsUsersId: Int
    let reactions: [Reactions]

    static let example = Answer(id: 0, questionsUsersId: 0, reactions: [])

    init(id: Int, questionsUsersId: Int, reactions: [Reactions]) {
        self.id = id
        self.questionsUsersId = questionsUsersId
        self.reactions = reactions
    }

...

Then you can declare

var answer = Answer.example 
vadian
  • 274,689
  • 30
  • 353
  • 361
  • Vadian, thanks a lot. I added the static let example in the struct but got three errors below. Could you kindly help me out? 1. Extra arguments at positions #1, #2, #3 in call 2. Missing argument for parameter 'from' in call 3. Insert 'from: <#Decoder#>, ' – Hiro Dec 09 '21 at 09:59
  • You have to add the standard `init` method. See the edit. – vadian Dec 09 '21 at 10:10
  • Vadian, thanks. In that case do I have to delete the current init(from decoder: Decoder)? I need to decode the JSON data and put it in the Answers Model so I wouldn't want to delete it. Currently I'm doing it as the following way. "self.answer = try JSONDecoder().decode(Answers.self, from: data)". If I use standard init, I won't be able to parse the JSON data into the Answers Model. (By the way I also initialize this Answers Model as an array in a different function so I named it Answers, though in this question I initialize it as single data set.) – Hiro Dec 09 '21 at 10:24
  • You need both `init` methods, so keep `init(from decoder`. If you have an array, then the first item in the array is an *answer* not an *answers*. The plural form is confusing. Look at Standard Library: An array of strings is `[String]` not `[Strings]`. – vadian Dec 09 '21 at 10:28