I got this long error:
Function opaque return type was inferred as '_ConditionalContent<_ConditionalContent<some View, VStack<ForEach<[Dictionary<String, JSON>.Keys.Element], Dictionary<String, JSON>.Keys.Element, TupleView<(some View, some View)>?>>>, some View>' (aka '_ConditionalContent<_ConditionalContent<some View, VStack<ForEach<Array<String>, String, Optional<TupleView<(some View, some View)>>>>>, some View>'), which defines the opaque type in terms of itself
I think the issue is the "resursive" character of the rendering as the error log itself says also:
which defines the opaque type in terms of itself
If I replace in the second if-else-statement, as in comment you see, and return only a text, then I got no error.
@ViewBuilder
func showNode(json: JSON) -> some View {
if let v = json.get() as? String {
Text("\(v)").padding()
} else if let d = json.get() as? [String: JSON] {
VStack {
ForEach(d.keys.sorted(), id: \.self) { k in
if let v = d[k] {
Text("\(k)").padding()
showNode(json: v)
}
}
}
// Text("").padding()
} else {
Text("").padding()
}
}
Method is a recursive method, which should render a json tree.
And the JSON
public enum JSON {
case string(String)
case number(Float)
case object([String:JSON])
case array([JSON])
case bool(Bool)
func get() -> Any {
switch self {
case .string(let v):
return v
case .number(let v):
return v
case .object(let v):
return v
case .array(let v):
return v
case .bool(let v):
return v
}
}
}
And all I do render it on screen:
struct ContentView: View {
var data: JSON? = nil
init() {
updateValue()
return
}
mutating func updateValue() {
do {
if let jsonURL = Bundle.main.url(forResource: "user", withExtension: "json") {
let jsonData = try Data(contentsOf: jsonURL)
guard let d2 = try JSONSerialization.jsonObject(with: jsonData, options: .mutableLeaves) as? JSON else {
print("Can not convert to d2")
return
}
data = d2
}
} catch {
}
}
var body: some View {
VStack {
if let data = self.data {
showNode(json: data)
}
}
}
CodePudding user response:
some View
is just a language feature that allows you to not write out the whole return type of the method.
If you actually try to write out the whole return type of the method by meticulously following all the language rules, you will find that you run into a problem. Because you need to know what the return type of showNode
is, in order to know the return type of showNode
! This is partly due to its recursive nature, and partly due to @ViewBuilder
.
If you are wondering what on earth is _ConditionalContent
, those come from buildEither
, which is what your if statements translate into when put inside a @ViewBuilder
. TupleView
comes from buildBlock
, and all their type parameters are determined by the types of the expressions you put inside, one of them being the showNode
call, whose type we are in the middle of figuring out.
You can fix this by either using AnyView
, the type-erased view:
func showNode(json: JSON, depth: Int = 1) -> AnyView {
if let v = json.get() as? String {
return AnyView(Text("\(v)").padding())
} else if let d = json.get() as? [String: JSON] {
return AnyView(VStack {
ForEach(d.keys.sorted(), id: \.self) { k in
if let v = d[k] {
Text("\(k)").padding()
showNode(json: v)
}
}
})
} else {
return AnyView(Text("").padding())
}
}
Or make a new View
type to stop the infinite recursion:
struct NodeView: View {
let json: JSON
var body: some View {
if let v = json.get() as? String {
Text("\(v)").padding()
} else if let d = json.get() as? [String: JSON] {
VStack {
ForEach(d.keys.sorted(), id: \.self) { k in
if let v = d[k] {
Text("\(k)").padding()
NodeView(json: v)
}
}
}
} else {
Text("").padding()
}
}
}
A few additional notes:
- The way that you are parsing the JSON right now is incorrect.
JSONSerialization
won't give you your ownJSON
type. You should probably use a customCodable
implementation instead, but exactly how to do that belongs to another question. - The view that this code draws doesn't actually show the "levels" of the JSON. Not sure if that is intended or not
if let v = json.get() as? String {
doesn't handleBool
s orFloat
s or arrays. If you want to handle those, you need to write checks for them as well.