I have a protocol and two different Extensions. When PoliceSignalsView is opened, when I change the shared variable in SignQuestionProvider with PoliceSignQuestion(), it returns Police related questions. If I replaced it with TrafficSignQuestion() it would return me traffic related questions. This is working properly. However, there is a problem.
Model 1:
struct TrafficSignQuestion: Codable, Hashable {
var trafficQuestions: [TrafficSign]?
}
enum TrafficSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
}
struct TrafficSign: Codable, Hashable {
var id: Int?
var image: String?
var sections: [TrafficSignSectionType.RawValue : String]?
var correct: String?
}
Model 2:
struct PoliceSignQuestion: Codable, Hashable {
var policeQuestions: [PoliceSign]?
}
enum PoliceSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
case C = "C"
}
struct PoliceSign: Codable, Hashable {
var id: Int?
var image: String?
var sections: [PoliceSignSectionType.RawValue : String]?
var correct: String?
}
Protocol:
protocol GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [Self]?
}
**SignQuestionProvider**
class SignQuestionProvider<T: Any>: ObservableObject {
var shared: GetSignQuestions?
@Published var allQuestions: T?
func getQuestions() {
let questions = shared?.getQuestions(jsonService: JSONService())
allQuestions = questions as? T
print("\(allQuestions)")
}
}
Extensions:
extension PoliceSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [PoliceSign]? {
if let questions = jsonService.getQuestion(fileName: "policeQuestions", using: PoliceSignQuestion.self) {
return questions.policeQuestions ?? []
}
return []
}
}
extension TrafficSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [TrafficSign]? {
if let questions = jsonService.getQuestion(fileName: "trafficQuestions", using: TrafficSignQuestion.self) {
return questions.trafficQuestions ?? []
}
return []
}
}
JSON Service:
class JSONService: ObservableObject {
func getQuestion<T: Codable>(fileName: String, using modelType: T.Type) -> T? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(modelType, from: data)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
}
PoliceSignalsView(FirstView):
How can I transfer the data I get from here to QuestionCardView()? I couldn't make the Generic Binding build.
struct PoliceSignalsView: View {
@StateObject var questionProvider = SignQuestionProvider<PoliceSignQuestion>()
var body: some View {
VStack {
QuestionCardView()
}
.onAppear {
questionProvider.shared = PoliceSignQuestion()
// When I change it to "TrafficSignQuestion", the TrafficSign function in the protocol works and returns TrafficSigns questions. I want to pass the result returned from here to QuestionCardView. How can I do that ?
questionProvider.getQuestions()
}
.environmentObject(questionProvider)
}
}
SecondView(QuestionCardView):
I don't want to see the policeQuestions property while showing the data from policeSignalsView here because it can be in trafficQuestions on this page. If I write it like this I get an error. how can i fix this problem?
struct QuestionCardView: View {
@EnvironmentObject var optionConfigure: OptionConfigure
@EnvironmentObject var questionProvider: SignQuestionProvider<PoliceSignQuestion>
var body: some View {
VStack {
ForEach(questionProvider.allQuestions?.policeQuestions?.indices ?? 0..<0, id: \.self) { item in
// here.. I don't want the "policeQuestions" property here. Because it should be a generic construct.
Text("")
.animation(.spring())
}
}
}
}
As I said, I don't want the policeQuestions property in ForEach on the QuestionCardView screen. I followed this way to remove it.
Protocol:
protocol GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [Self] // Array
}
Extensions:
extension PoliceSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [PoliceSign] {
if let questions = jsonService.getQuestion(fileName: "policeQuestions", using: PoliceSignQuestion.self) {
return questions.policeQuestions ?? []
}
return []
}
}
extension TrafficSign: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [TrafficSign] {
if let questions = jsonService.getQuestion(fileName: "trafficQuestions", using: TrafficSignQuestion.self) {
return questions.trafficQuestions ?? []
}
return []
}
}
PoliceSignalsView:
struct PoliceSignalsView: View {
@StateObject var questionProvider = SignQuestionProvider<PoliceSign>()
var body: some View {
VStack {
QuestionCardView()
}
.onAppear {
questionProvider.shared = PoliceSign()
questionProvider.getQuestions()
}
.environmentObject(questionProvider)
}
}
When I make changes in this way, I get an error as in the image.
I've reorganized some of the code and re-added the Question
protocol from my previous answer to you. I've also slightly edited your model to make the TrafficSign
and PoliceSign
have non-optional id
properties. Otherwise, using them in the ForEach
is going to be problematic.
struct TrafficSignQuestion: Codable, Hashable {
var trafficQuestions: [TrafficSign]?
}
enum TrafficSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
}
struct TrafficSign: Codable, Hashable, Question {
var id: Int
var image: String?
var sections: [TrafficSignSectionType.RawValue : String]?
var correct: String?
}
struct PoliceSignQuestion: Codable, Hashable {
var policeQuestions: [PoliceSign]?
}
enum PoliceSignSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
case C = "C"
}
struct PoliceSign: Codable, Hashable, Question {
var id: Int
var image: String?
var sections: [PoliceSignSectionType.RawValue : String]?
var correct: String?
}
protocol Question {
var id: Int { get set }
var image: String? { get set }
}
protocol GetSignQuestions {
associatedtype QuestionType : Question
func getQuestions(jsonService: JSONService) -> [QuestionType]?
}
class SignQuestionProvider<T: GetSignQuestions>: ObservableObject {
@Published var allQuestions : [T.QuestionType] = []
func getQuestions(getter: T) {
if let questions = getter.getQuestions(jsonService: JSONService()) {
self.allQuestions = questions
}
print("\(allQuestions)")
}
}
struct PoliceSignGetter : GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [PoliceSign]? {
if let questions = jsonService.getQuestion(fileName: "policeQuestions", using: PoliceSignQuestion.self) {
return questions.policeQuestions ?? []
}
return []
}
}
struct TrafficSignGetter: GetSignQuestions {
func getQuestions(jsonService: JSONService) -> [TrafficSign]? {
if let questions = jsonService.getQuestion(fileName: "trafficQuestions", using: TrafficSignQuestion.self) {
return questions.trafficQuestions ?? []
}
return []
}
}
class JSONService: ObservableObject {
func getQuestion<T: Codable>(fileName: String, using modelType: T.Type) -> T? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(modelType, from: data)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
}
struct PoliceSignalsView: View {
@StateObject var questionProvider = SignQuestionProvider<PoliceSignGetter>()
var body: some View {
VStack {
QuestionCardView<PoliceSign>(questions: questionProvider.allQuestions)
}
.onAppear {
questionProvider.getQuestions(getter: PoliceSignGetter())
}
}
}
struct QuestionCardView<T: Question>: View {
var questions: [Question]
var body: some View {
VStack {
ForEach(questions, id: \.id) { item in
Text("")
.animation(.spring())
}
}
}
}