external macro implementation type 'PreviewsMacros.SwiftUIView' could not be found for macro 'Preview(_:body:)' #Preview

1.2k views Asked by At

I am using macOS Ventura and Xcode 15.0.1. I have reached out to the original developer, but he does not get the same errors as I get. The code is supposed to create a button. I have tried googling of course, but to no avail. I don't know much about new Swift macros, so I am a bit lost as to what the problem might be. I should also say I am fairly new to SwiftUI, but I am learning every day.

When I run the code, I get the following error:

error: MyPlayground.playground:191:1: error: external macro implementation type 'PreviewsMacros.SwiftUIView' could not be found for macro 'Preview(_:body:)' #Preview {

import Foundation
import SwiftUI

// MARK: - Custom Button Style

struct MobileMeButtonStyle: ButtonStyle {
   // MARK: Metrics
   @ScaledMetric private var cornerRadius = 12
   @ScaledMetric private var horizontalLabelPadding = 12
   @ScaledMetric private var verticalLabelPadding = 8
   @ScaledMetric private var shadowRadius = 2
   @ScaledMetric private var shadowVerticalOffset = 1

   // MARK: Immutable Properties
   private let strokeLineWidth = 1.0

   func makeBody(configuration: Configuration) -> some View {
      configuration.label
         .labelStyle(.mobileMe)
         .padding(.horizontal, horizontalLabelPadding)
         .padding(.vertical, verticalLabelPadding)
         .background(
            RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
               .foregroundStyle(
                  LinearGradient(
                     colors: [
                        Color(red: 58/255, green: 63/255, blue: 66/255),
                        Color(red: 58/255, green: 63/255, blue: 66/255),
                        Color(red: 73/255, green: 76/255, blue: 80/255)
                     ],
                     startPoint: .top,
                     endPoint: .bottom
                  )
               )
               .overlay(
                  // Top reflection
                  ReflectionContainer {
                     UnevenRoundedRectangle(
                        cornerRadii: .init(
                           topLeading: cornerRadius,
                           bottomLeading: (cornerRadius * 0.43).rounded(.down),
                           bottomTrailing: (cornerRadius * 0.43).rounded(.down),
                           topTrailing: cornerRadius
                        ),
                        style: .continuous
                     )
                     .foregroundStyle(
                        LinearGradient(
                           colors: [
                              Color.white,
                              Color.white.opacity(0.24)
                           ],
                           startPoint: .top,
                           endPoint: .bottom
                        )
                     )
                     .blendMode(.plusLighter)
                     .opacity(0.24)
                  }
               )
               .overlay(
                  // Inner light stroke
                  RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
                     .strokeBorder(
                        LinearGradient(
                           colors: [
                              Color.white,
                              Color.white.opacity(0.2),
                              Color.white.opacity(0.24)
                           ],
                           startPoint: .top,
                           endPoint: .bottom
                        ),
                        lineWidth: strokeLineWidth
                     )
                     .blendMode(.plusLighter)
                     .opacity(0.3)
               )
               .overlay(
                  // Outer shadow stroke
                  RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
                     .inset(by: -strokeLineWidth)
                     .strokeBorder(
                        LinearGradient(
                           colors: [
                              Color.black.opacity(0.8),
                              Color.black
                           ],
                           startPoint: .top,
                           endPoint: .bottom
                        ),
                        lineWidth: strokeLineWidth
                     )
                     .opacity(0.34)
               )
         )
         .shadow(
            color: .black.opacity(0.2),
            radius: shadowRadius,
            x: 0,
            y: shadowVerticalOffset
         )
         .padding(.leading, 2)
         .environment(\.buttonRole, configuration.role)
   }
}

// MARK: - Custom Layout

private struct ReflectionContainer: Layout {
   func sizeThatFits(
      proposal: ProposedViewSize,
      subviews: Subviews,
      cache: inout ()
   ) -> CGSize {
      let safeProposal = proposal.replacingUnspecifiedDimensions()
      return CGSize(width: safeProposal.width, height: safeProposal.height)
   }

   func placeSubviews(
      in bounds: CGRect,
      proposal: ProposedViewSize,
      subviews: Subviews,
      cache: inout ()
   ) {
      subviews.first!.place(
         at: CGPoint(x: bounds.minX, y: bounds.minY),
         proposal: .init(
            width: proposal.width ?? 0,
            height: (proposal.height ?? 0) / 1.85
         )
      )
   }
}

// MARK: - Custom Label Style

private struct MobileMeLabelStyle: LabelStyle {
   // MARK: Environment
   @Environment(\.buttonRole) private var role

   // MARK: Metrics
   @ScaledMetric private var shadowRadius = 2
   @ScaledMetric private var shadowVerticalOffset = 1

   func makeBody(configuration: Configuration) -> some View {
      HStack {
         configuration.icon
         configuration.title
      }
      .font(.callout.weight(.medium))
      .foregroundStyle(
         role == .destructive
         ? AnyShapeStyle(Color.red.gradient)
         : AnyShapeStyle(Color.white.gradient.opacity(0.9))
      )
      .shadow(
         color: .black.opacity(role == .destructive ? 0.3 : 0.6),
         radius: shadowRadius,
         x: 0,
         y: shadowVerticalOffset
      )
   }
}

// MARK: - Quality Of Life

extension ButtonStyle where Self == MobileMeButtonStyle {
   static var mobileMe: Self { MobileMeButtonStyle() }
}

extension LabelStyle where Self == MobileMeLabelStyle {
   static var mobileMe: Self { MobileMeLabelStyle() }
}

// MARK: - Environment Extensions

private enum ButtonRoleEnvironmentKey: EnvironmentKey {
   static let defaultValue: ButtonRole? = nil
}

extension EnvironmentValues {
   var buttonRole: ButtonRole? {
      get { self[ButtonRoleEnvironmentKey.self] }
      set { self[ButtonRoleEnvironmentKey.self] = newValue }
   }
}

// MARK: - Preview

#Preview {
   HStack {
      Button(action: {}) {
         Label(
            title: { Text("Today") },
            icon: { EmptyView() }
         )
      }
      Button(role: .destructive, action: {}) {
         Label(
            title: { EmptyView() },
            icon: { Image(systemName: "trash.fill") }
         )
      }
   }
   .padding(.horizontal, 16)
   .padding(.vertical, 16)
   .background(
      RoundedRectangle(cornerRadius: 30, style: .continuous)
         .foregroundStyle(
            LinearGradient(
               colors: [
                  Color(red: 72/255, green: 77/255, blue: 81/255),
                  Color(red: 46/255, green: 48/255, blue: 54/255)
               ],
               startPoint: .top,
               endPoint: .bottom
            )
         )
   )
   .buttonStyle(.mobileMe)
}
2

There are 2 answers

3
Sweeper On

You seem to be using an Xcode Playground. Xcode Playgrounds don't support SwiftUI previews. (See also)

Xcode Playgrounds have live views, which also allow you to "preview" a SwiftUI view. This is slightly different to SwiftUI Previews, e.g. you cannot choose different devices - the view is sort of just displayed "in a vacuum".

Rather than using Xcode Playgrounds, you can create a new Xcode project, and paste the code there. Open the "Canvas" if it doesn't appear automatically:

enter image description here

Alternatively, if you do want to preview this in an Xcode playground, import PlaygroundSupport and change

#Preview {
    ...
}

to

PlaygroundPage.current.setLiveView(
    ...
)

Manually open the live view if it is not open already. It'd look something like this:

enter image description here


Note that there is a similarly-named app called Swift Playgrounds, which does support SwiftUI Previews.

0
akonoplev On

Looks like a bug of Xcode version lower 15.2, I've resolved this problem just added .macOS(.v12) to platforms array in Package.swift of module where I use #Preview

platforms: [ .macOS(.v12), .iOS(.v15) ]

just update Xcode to the latest version, it has to help