Opaque result type question

Hi,

Why the first part work and not the second ?

import SwiftUI

// Why does this works

enum Blueprint {
  case boeing747
  case concorde
}

protocol Plane { }
struct Boeing747: Plane { }
struct Concorde: Plane { }

func factory(_ blueprint : Blueprint) -> some Plane {
  return Boeing747()
}

// And this does not work ?

enum Screen {
  case homepage
  case setting
  case loggin
}

struct HomePage: View { var body: some View { Text("Homepage") } }
struct Setting: View { var body: some View { Text("Setting") } }
struct Loggin: View { var body: some View { Text("Loggin") } }


func screenBuilder(_ screen : Screen) -> some View {
  switch screen {
  case .homepage:
    return HomePage()
  default: break
  }
  return Text("Not Implemented")
}

I don't understand why I have this error.

error: SwiftUIRouter.playground:37:6: error: function declares an opaque return type, but the return statements in its body do not have matching underlying types
func screenBuilder(_ screen : Screen) -> some View {
     ^

SwiftUIRouter.playground:40:12: note: return statement has underlying type 'HomePage'
    return HomePage()
           ^

SwiftUIRouter.playground:43:10: note: return statement has underlying type 'Text'
  return Text("Not Implemented")

You’re returning two different types of Views, only 1 type of View can be returned.

Ok but opaque result type should allow me to return objects of different type that conform to the same protocol ? No ?

Because this won't work either.

func screenBuilder<T : View>(_ screen : Screen) -> T {
  switch screen {
  case .homepage:
    return HomePage()
  default: break
  }
  return Text("Not Implemented")
}

No.

Opaque result types only hide the type from the caller. The callee still has to honor the contract, which is that a function returning an opaque type always returns the same concrete type.

3 Likes

Okay I will find another way thanks

Use the @ViewBuilder If/Else:

@ViewBuilder
func screenBuilder(_ screen : Screen) -> some View {
    if screen == .homepage: {
        HomePage()
    } else {
        Text("Not Implemented")
    }
}

You can keep adding else if to handle any number of "cases".

Unfortunately the Swift compiler in Xcode 11.3.1 is a little buggy now and the above code may not conpile. See I'm stumped with this @ViewBuilder on var body: some View compile error: Cannot convert return expression of type '_ConditionalContent<Text, Text>' to return type 'some View'

You can get around the bug by first create an variable and use that in the If/Else:

let homePage = HomePage()    // <== work around the bug
let text = Text("Not Implemented")
@ViewBuilder
func screenBuilder(_ screen : Screen) -> some View {
    if screen == .homepage: {
        homePage               // <== use this instead
    } else {
        text
    }
}
1 Like

I have tried what you proposed before it compiled but don't work properly.

It was like the return did not work properly.

I will try it again, thanks.