From fbcae5c544c11206b00874fb1edcf986a4933e66 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Wed, 5 Jul 2023 11:22:54 -0700 Subject: [PATCH] Add macOS and tvOS support to EpoxyCore (#143) * Add macOS and tvOS support to EpoxyCore lint * Fix main package build step, and CI job for EpoxyCore * Update typealiases * Remove 'UIView' from type names * Improve project compatibility * Try only running EpoxyCore job on Xcode 14 * Update version to 0.10.0 * Try to fix CI * Fix Swift 5.6 build? * Fix CI job? * Manually specify deployment target, this seems to fix the problem in a clean checkout * Update xcodeproj * Revert rakefile changes to see if this isnt necessary anymore * Fix macOS 13 SDK check * Clean up * Don't try to build for Xcode 13, which fails for some reason --- .github/workflows/main.yml | 14 + CHANGELOG.md | 13 +- ConfigurePodspec.rb | 2 +- Epoxy.xcworkspace/contents.xcworkspacedata | 3 + Rakefile | 7 + .../EpoxyCore.xcodeproj/project.pbxproj | 587 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/EpoxyCore.xcscheme | 66 ++ .../Model/Providers/MakeViewProviding.swift | 4 +- .../Providers/TraitCollectionProviding.swift | 2 + .../Model/Providers/ViewProviding.swift | 4 +- .../EpoxyCore/Model/ViewEpoxyModeled.swift | 4 +- .../EpoxySwiftUIHostingController.swift | 2 + .../SwiftUI/EpoxySwiftUIHostingView.swift | 4 + .../SwiftUI/EpoxyableView+SwiftUIView.swift | 16 +- ...swift => MeasuringViewRepresentable.swift} | 65 +- .../SwiftUIMeasurementContainer.swift | 71 ++- ...{SwiftUIUIView.swift => SwiftUIView.swift} | 42 +- .../SwiftUI/UIView+SwiftUIView.swift | 16 +- .../Views/BehaviorsConfigurableView.swift | 4 +- .../Views/ContentConfigurableView.swift | 4 +- Sources/EpoxyCore/Views/StyledView.swift | 4 +- Sources/EpoxyCore/Views/ViewType.swift | 47 ++ 23 files changed, 914 insertions(+), 75 deletions(-) create mode 100644 Sources/EpoxyCore/EpoxyCore.xcodeproj/project.pbxproj create mode 100644 Sources/EpoxyCore/EpoxyCore.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Sources/EpoxyCore/EpoxyCore.xcodeproj/xcshareddata/xcschemes/EpoxyCore.xcscheme rename Sources/EpoxyCore/SwiftUI/LayoutUtilities/{MeasuringUIViewRepresentable.swift => MeasuringViewRepresentable.swift} (54%) rename Sources/EpoxyCore/SwiftUI/{SwiftUIUIView.swift => SwiftUIView.swift} (79%) create mode 100644 Sources/EpoxyCore/Views/ViewType.swift diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2d0e08eb..eb9c5d1a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,6 +22,20 @@ jobs: - name: Build Package run: bundle exec rake build:package + build-epoxy-core: + runs-on: macos-latest + strategy: + matrix: + xcode: + - '14.0.1' # Swift 5.7 (highest) + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/setup + with: + xcode: ${{ matrix.xcode }} + - name: Build EpoxyCore + run: bundle exec rake build:EpoxyCore + build-example: runs-on: macos-latest strategy: diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ef0eff5..e14c0f37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,21 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased](https://github.com/airbnb/epoxy-ios/compare/0.9.0...HEAD) +## [Unreleased](https://github.com/airbnb/epoxy-ios/compare/0.10.0...HEAD) + +### Changed +- ... + +### Fixed +- ... + +## [0.10.0](https://github.com/airbnb/epoxy-ios/compare/0.9.0...0.10.0) - 2023-06-29 ### Changed - Dropped support for Swift 5.4. +- EpoxyCore now supports macOS and tvOS. +- Renamed EpoxyCore's `SwiftUIUIView` to `SwiftUIView`. +- Renamed EpoxyCore's `MeasuringUIViewRepresentable` to `MeasuringViewRepresentable`. - Added `UIScrollView.keyboardAdjustsBottomBarOffset` escape hatch to disable bottom bar keyboard avoidance for cases where the keyboard is avoided at a higher level (e.g. a `UIPresentationController` subclass). diff --git a/ConfigurePodspec.rb b/ConfigurePodspec.rb index f5f83917..0e4cc1e6 100644 --- a/ConfigurePodspec.rb +++ b/ConfigurePodspec.rb @@ -3,7 +3,7 @@ def configure(spec:, name:, summary:, local_deps: []) # The shared CocoaPods version number for Epoxy. # # Change this constant to increment the Podspec version for all Epoxy Podspecs from a single place. - version = '0.9.0' + version = '0.10.0' spec.name = name spec.summary = summary diff --git a/Epoxy.xcworkspace/contents.xcworkspacedata b/Epoxy.xcworkspace/contents.xcworkspacedata index f8481312..497d7236 100644 --- a/Epoxy.xcworkspace/contents.xcworkspacedata +++ b/Epoxy.xcworkspace/contents.xcworkspacedata @@ -4,6 +4,9 @@ + + diff --git a/Rakefile b/Rakefile index 96639df3..c0631351 100644 --- a/Rakefile +++ b/Rakefile @@ -4,6 +4,13 @@ namespace :build do xcodebuild 'build -scheme Epoxy -destination generic/platform=iOS' end + desc 'Builds the EpoxyCore package for iOS, macOS, and tvOS' + task :EpoxyCore do + xcodebuild 'build -scheme EpoxyCore -destination generic/platform=iOS' + xcodebuild 'build -scheme EpoxyCore -destination generic/platform=tvOS' + xcodebuild 'build -scheme EpoxyCore -destination generic/platform=macOS' + end + desc 'Builds the EpoxyExample app' task :example do xcodebuild 'build -scheme EpoxyExample -destination "platform=iOS Simulator,name=iPhone 12"' diff --git a/Sources/EpoxyCore/EpoxyCore.xcodeproj/project.pbxproj b/Sources/EpoxyCore/EpoxyCore.xcodeproj/project.pbxproj new file mode 100644 index 00000000..300847f4 --- /dev/null +++ b/Sources/EpoxyCore/EpoxyCore.xcodeproj/project.pbxproj @@ -0,0 +1,587 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 08E2E9422A4CD54A00B47A18 /* EpoxyLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9102A4CD54A00B47A18 /* EpoxyLogger.swift */; }; + 08E2E9432A4CD54A00B47A18 /* EpoxyModelStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9122A4CD54A00B47A18 /* EpoxyModelStorage.swift */; }; + 08E2E9442A4CD54A00B47A18 /* CallbackContextEpoxyModeled.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9132A4CD54A00B47A18 /* CallbackContextEpoxyModeled.swift */; }; + 08E2E9452A4CD54A00B47A18 /* ViewDifferentiatorProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9152A4CD54A00B47A18 /* ViewDifferentiatorProviding.swift */; }; + 08E2E9462A4CD54A00B47A18 /* SetContentProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9162A4CD54A00B47A18 /* SetContentProviding.swift */; }; + 08E2E9472A4CD54A00B47A18 /* DidSelectProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9172A4CD54A00B47A18 /* DidSelectProviding.swift */; }; + 08E2E9482A4CD54A00B47A18 /* DidEndDisplayingProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9182A4CD54A00B47A18 /* DidEndDisplayingProviding.swift */; }; + 08E2E9492A4CD54A00B47A18 /* WillDisplayProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9192A4CD54A00B47A18 /* WillDisplayProviding.swift */; }; + 08E2E94A2A4CD54A00B47A18 /* ErasedContentProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E91A2A4CD54A00B47A18 /* ErasedContentProviding.swift */; }; + 08E2E94B2A4CD54A00B47A18 /* MakeViewProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E91B2A4CD54A00B47A18 /* MakeViewProviding.swift */; }; + 08E2E94C2A4CD54A00B47A18 /* TraitCollectionProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E91C2A4CD54A00B47A18 /* TraitCollectionProviding.swift */; }; + 08E2E94D2A4CD54A00B47A18 /* ViewProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E91D2A4CD54A00B47A18 /* ViewProviding.swift */; }; + 08E2E94E2A4CD54A00B47A18 /* SetBehaviorsProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E91E2A4CD54A00B47A18 /* SetBehaviorsProviding.swift */; }; + 08E2E94F2A4CD54A00B47A18 /* StyleIDProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E91F2A4CD54A00B47A18 /* StyleIDProviding.swift */; }; + 08E2E9502A4CD54A00B47A18 /* DidDisplayProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9202A4CD54A00B47A18 /* DidDisplayProviding.swift */; }; + 08E2E9512A4CD54A00B47A18 /* AnimatedProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9212A4CD54A00B47A18 /* AnimatedProviding.swift */; }; + 08E2E9522A4CD54A00B47A18 /* DataIDProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9222A4CD54A00B47A18 /* DataIDProviding.swift */; }; + 08E2E9532A4CD54A00B47A18 /* ViewEpoxyModeled.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9232A4CD54A00B47A18 /* ViewEpoxyModeled.swift */; }; + 08E2E9542A4CD54A00B47A18 /* ClassReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9252A4CD54A00B47A18 /* ClassReference.swift */; }; + 08E2E9552A4CD54A00B47A18 /* AnyEpoxyModelProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9262A4CD54A00B47A18 /* AnyEpoxyModelProperty.swift */; }; + 08E2E9562A4CD54A00B47A18 /* EpoxyModelProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9272A4CD54A00B47A18 /* EpoxyModelProperty.swift */; }; + 08E2E9572A4CD54A00B47A18 /* EpoxyModelArrayBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9282A4CD54A00B47A18 /* EpoxyModelArrayBuilder.swift */; }; + 08E2E9582A4CD54A00B47A18 /* EpoxyModeled.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9292A4CD54A00B47A18 /* EpoxyModeled.swift */; }; + 08E2E9592A4CD54A00B47A18 /* IndexChangeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E92B2A4CD54A00B47A18 /* IndexChangeset.swift */; }; + 08E2E95A2A4CD54A00B47A18 /* SectionedChangeset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E92C2A4CD54A00B47A18 /* SectionedChangeset.swift */; }; + 08E2E95B2A4CD54A00B47A18 /* Collection+Diff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E92D2A4CD54A00B47A18 /* Collection+Diff.swift */; }; + 08E2E95C2A4CD54A00B47A18 /* DiffableSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E92E2A4CD54A00B47A18 /* DiffableSection.swift */; }; + 08E2E95D2A4CD54A00B47A18 /* Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E92F2A4CD54A00B47A18 /* Diffable.swift */; }; + 08E2E95E2A4CD54A00B47A18 /* StyledView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9312A4CD54A00B47A18 /* StyledView.swift */; }; + 08E2E95F2A4CD54A00B47A18 /* ContentConfigurableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9322A4CD54A00B47A18 /* ContentConfigurableView.swift */; }; + 08E2E9602A4CD54A00B47A18 /* EpoxyableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9332A4CD54A00B47A18 /* EpoxyableView.swift */; }; + 08E2E9612A4CD54A00B47A18 /* ViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9342A4CD54A00B47A18 /* ViewType.swift */; }; + 08E2E9622A4CD54A00B47A18 /* BehaviorsConfigurableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9352A4CD54A00B47A18 /* BehaviorsConfigurableView.swift */; }; + 08E2E9632A4CD54A00B47A18 /* UIViewConfiguringSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9372A4CD54A00B47A18 /* UIViewConfiguringSwiftUIView.swift */; }; + 08E2E9642A4CD54A00B47A18 /* EpoxySwiftUILayoutMargins.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9382A4CD54A00B47A18 /* EpoxySwiftUILayoutMargins.swift */; }; + 08E2E9652A4CD54A00B47A18 /* EpoxySwiftUIIntrinsicContentSizeInvalidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9392A4CD54A00B47A18 /* EpoxySwiftUIIntrinsicContentSizeInvalidator.swift */; }; + 08E2E9662A4CD54A00B47A18 /* SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E93A2A4CD54A00B47A18 /* SwiftUIView.swift */; }; + 08E2E9672A4CD54A00B47A18 /* EpoxyableView+SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E93B2A4CD54A00B47A18 /* EpoxyableView+SwiftUIView.swift */; }; + 08E2E9682A4CD54A00B47A18 /* MeasuringViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E93D2A4CD54A00B47A18 /* MeasuringViewRepresentable.swift */; }; + 08E2E9692A4CD54A00B47A18 /* SwiftUIMeasurementContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E93E2A4CD54A00B47A18 /* SwiftUIMeasurementContainer.swift */; }; + 08E2E96A2A4CD54A00B47A18 /* EpoxySwiftUIHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E93F2A4CD54A00B47A18 /* EpoxySwiftUIHostingController.swift */; }; + 08E2E96B2A4CD54A00B47A18 /* UIView+SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9402A4CD54A00B47A18 /* UIView+SwiftUIView.swift */; }; + 08E2E96C2A4CD54A00B47A18 /* EpoxySwiftUIHostingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E2E9412A4CD54A00B47A18 /* EpoxySwiftUIHostingView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 08E2E9052A4CD50700B47A18 /* EpoxyCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = EpoxyCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 08E2E9102A4CD54A00B47A18 /* EpoxyLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpoxyLogger.swift; sourceTree = ""; }; + 08E2E9122A4CD54A00B47A18 /* EpoxyModelStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpoxyModelStorage.swift; sourceTree = ""; }; + 08E2E9132A4CD54A00B47A18 /* CallbackContextEpoxyModeled.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallbackContextEpoxyModeled.swift; sourceTree = ""; }; + 08E2E9152A4CD54A00B47A18 /* ViewDifferentiatorProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewDifferentiatorProviding.swift; sourceTree = ""; }; + 08E2E9162A4CD54A00B47A18 /* SetContentProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetContentProviding.swift; sourceTree = ""; }; + 08E2E9172A4CD54A00B47A18 /* DidSelectProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DidSelectProviding.swift; sourceTree = ""; }; + 08E2E9182A4CD54A00B47A18 /* DidEndDisplayingProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DidEndDisplayingProviding.swift; sourceTree = ""; }; + 08E2E9192A4CD54A00B47A18 /* WillDisplayProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WillDisplayProviding.swift; sourceTree = ""; }; + 08E2E91A2A4CD54A00B47A18 /* ErasedContentProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErasedContentProviding.swift; sourceTree = ""; }; + 08E2E91B2A4CD54A00B47A18 /* MakeViewProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MakeViewProviding.swift; sourceTree = ""; }; + 08E2E91C2A4CD54A00B47A18 /* TraitCollectionProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TraitCollectionProviding.swift; sourceTree = ""; }; + 08E2E91D2A4CD54A00B47A18 /* ViewProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewProviding.swift; sourceTree = ""; }; + 08E2E91E2A4CD54A00B47A18 /* SetBehaviorsProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetBehaviorsProviding.swift; sourceTree = ""; }; + 08E2E91F2A4CD54A00B47A18 /* StyleIDProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyleIDProviding.swift; sourceTree = ""; }; + 08E2E9202A4CD54A00B47A18 /* DidDisplayProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DidDisplayProviding.swift; sourceTree = ""; }; + 08E2E9212A4CD54A00B47A18 /* AnimatedProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedProviding.swift; sourceTree = ""; }; + 08E2E9222A4CD54A00B47A18 /* DataIDProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataIDProviding.swift; sourceTree = ""; }; + 08E2E9232A4CD54A00B47A18 /* ViewEpoxyModeled.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewEpoxyModeled.swift; sourceTree = ""; }; + 08E2E9252A4CD54A00B47A18 /* ClassReference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClassReference.swift; sourceTree = ""; }; + 08E2E9262A4CD54A00B47A18 /* AnyEpoxyModelProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyEpoxyModelProperty.swift; sourceTree = ""; }; + 08E2E9272A4CD54A00B47A18 /* EpoxyModelProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpoxyModelProperty.swift; sourceTree = ""; }; + 08E2E9282A4CD54A00B47A18 /* EpoxyModelArrayBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpoxyModelArrayBuilder.swift; sourceTree = ""; }; + 08E2E9292A4CD54A00B47A18 /* EpoxyModeled.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpoxyModeled.swift; sourceTree = ""; }; + 08E2E92B2A4CD54A00B47A18 /* IndexChangeset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndexChangeset.swift; sourceTree = ""; }; + 08E2E92C2A4CD54A00B47A18 /* SectionedChangeset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionedChangeset.swift; sourceTree = ""; }; + 08E2E92D2A4CD54A00B47A18 /* Collection+Diff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Collection+Diff.swift"; sourceTree = ""; }; + 08E2E92E2A4CD54A00B47A18 /* DiffableSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiffableSection.swift; sourceTree = ""; }; + 08E2E92F2A4CD54A00B47A18 /* Diffable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Diffable.swift; sourceTree = ""; }; + 08E2E9312A4CD54A00B47A18 /* StyledView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StyledView.swift; sourceTree = ""; }; + 08E2E9322A4CD54A00B47A18 /* ContentConfigurableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentConfigurableView.swift; sourceTree = ""; }; + 08E2E9332A4CD54A00B47A18 /* EpoxyableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpoxyableView.swift; sourceTree = ""; }; + 08E2E9342A4CD54A00B47A18 /* ViewType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewType.swift; sourceTree = ""; }; + 08E2E9352A4CD54A00B47A18 /* BehaviorsConfigurableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BehaviorsConfigurableView.swift; sourceTree = ""; }; + 08E2E9372A4CD54A00B47A18 /* UIViewConfiguringSwiftUIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewConfiguringSwiftUIView.swift; sourceTree = ""; }; + 08E2E9382A4CD54A00B47A18 /* EpoxySwiftUILayoutMargins.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpoxySwiftUILayoutMargins.swift; sourceTree = ""; }; + 08E2E9392A4CD54A00B47A18 /* EpoxySwiftUIIntrinsicContentSizeInvalidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpoxySwiftUIIntrinsicContentSizeInvalidator.swift; sourceTree = ""; }; + 08E2E93A2A4CD54A00B47A18 /* SwiftUIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUIView.swift; sourceTree = ""; }; + 08E2E93B2A4CD54A00B47A18 /* EpoxyableView+SwiftUIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EpoxyableView+SwiftUIView.swift"; sourceTree = ""; }; + 08E2E93D2A4CD54A00B47A18 /* MeasuringViewRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeasuringViewRepresentable.swift; sourceTree = ""; }; + 08E2E93E2A4CD54A00B47A18 /* SwiftUIMeasurementContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUIMeasurementContainer.swift; sourceTree = ""; }; + 08E2E93F2A4CD54A00B47A18 /* EpoxySwiftUIHostingController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpoxySwiftUIHostingController.swift; sourceTree = ""; }; + 08E2E9402A4CD54A00B47A18 /* UIView+SwiftUIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+SwiftUIView.swift"; sourceTree = ""; }; + 08E2E9412A4CD54A00B47A18 /* EpoxySwiftUIHostingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpoxySwiftUIHostingView.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 08E2E9022A4CD50700B47A18 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08E2E8FB2A4CD50700B47A18 = { + isa = PBXGroup; + children = ( + 08E2E92A2A4CD54A00B47A18 /* Diffing */, + 08E2E90F2A4CD54A00B47A18 /* Logging */, + 08E2E9112A4CD54A00B47A18 /* Model */, + 08E2E9362A4CD54A00B47A18 /* SwiftUI */, + 08E2E9302A4CD54A00B47A18 /* Views */, + 08E2E9062A4CD50700B47A18 /* Products */, + ); + sourceTree = ""; + }; + 08E2E9062A4CD50700B47A18 /* Products */ = { + isa = PBXGroup; + children = ( + 08E2E9052A4CD50700B47A18 /* EpoxyCore.framework */, + ); + name = Products; + sourceTree = ""; + }; + 08E2E90F2A4CD54A00B47A18 /* Logging */ = { + isa = PBXGroup; + children = ( + 08E2E9102A4CD54A00B47A18 /* EpoxyLogger.swift */, + ); + path = Logging; + sourceTree = ""; + }; + 08E2E9112A4CD54A00B47A18 /* Model */ = { + isa = PBXGroup; + children = ( + 08E2E9122A4CD54A00B47A18 /* EpoxyModelStorage.swift */, + 08E2E9132A4CD54A00B47A18 /* CallbackContextEpoxyModeled.swift */, + 08E2E9142A4CD54A00B47A18 /* Providers */, + 08E2E9232A4CD54A00B47A18 /* ViewEpoxyModeled.swift */, + 08E2E9242A4CD54A00B47A18 /* Internal */, + 08E2E9272A4CD54A00B47A18 /* EpoxyModelProperty.swift */, + 08E2E9282A4CD54A00B47A18 /* EpoxyModelArrayBuilder.swift */, + 08E2E9292A4CD54A00B47A18 /* EpoxyModeled.swift */, + ); + path = Model; + sourceTree = ""; + }; + 08E2E9142A4CD54A00B47A18 /* Providers */ = { + isa = PBXGroup; + children = ( + 08E2E9152A4CD54A00B47A18 /* ViewDifferentiatorProviding.swift */, + 08E2E9162A4CD54A00B47A18 /* SetContentProviding.swift */, + 08E2E9172A4CD54A00B47A18 /* DidSelectProviding.swift */, + 08E2E9182A4CD54A00B47A18 /* DidEndDisplayingProviding.swift */, + 08E2E9192A4CD54A00B47A18 /* WillDisplayProviding.swift */, + 08E2E91A2A4CD54A00B47A18 /* ErasedContentProviding.swift */, + 08E2E91B2A4CD54A00B47A18 /* MakeViewProviding.swift */, + 08E2E91C2A4CD54A00B47A18 /* TraitCollectionProviding.swift */, + 08E2E91D2A4CD54A00B47A18 /* ViewProviding.swift */, + 08E2E91E2A4CD54A00B47A18 /* SetBehaviorsProviding.swift */, + 08E2E91F2A4CD54A00B47A18 /* StyleIDProviding.swift */, + 08E2E9202A4CD54A00B47A18 /* DidDisplayProviding.swift */, + 08E2E9212A4CD54A00B47A18 /* AnimatedProviding.swift */, + 08E2E9222A4CD54A00B47A18 /* DataIDProviding.swift */, + ); + path = Providers; + sourceTree = ""; + }; + 08E2E9242A4CD54A00B47A18 /* Internal */ = { + isa = PBXGroup; + children = ( + 08E2E9252A4CD54A00B47A18 /* ClassReference.swift */, + 08E2E9262A4CD54A00B47A18 /* AnyEpoxyModelProperty.swift */, + ); + path = Internal; + sourceTree = ""; + }; + 08E2E92A2A4CD54A00B47A18 /* Diffing */ = { + isa = PBXGroup; + children = ( + 08E2E92B2A4CD54A00B47A18 /* IndexChangeset.swift */, + 08E2E92C2A4CD54A00B47A18 /* SectionedChangeset.swift */, + 08E2E92D2A4CD54A00B47A18 /* Collection+Diff.swift */, + 08E2E92E2A4CD54A00B47A18 /* DiffableSection.swift */, + 08E2E92F2A4CD54A00B47A18 /* Diffable.swift */, + ); + path = Diffing; + sourceTree = ""; + }; + 08E2E9302A4CD54A00B47A18 /* Views */ = { + isa = PBXGroup; + children = ( + 08E2E9312A4CD54A00B47A18 /* StyledView.swift */, + 08E2E9322A4CD54A00B47A18 /* ContentConfigurableView.swift */, + 08E2E9332A4CD54A00B47A18 /* EpoxyableView.swift */, + 08E2E9342A4CD54A00B47A18 /* ViewType.swift */, + 08E2E9352A4CD54A00B47A18 /* BehaviorsConfigurableView.swift */, + ); + path = Views; + sourceTree = ""; + }; + 08E2E9362A4CD54A00B47A18 /* SwiftUI */ = { + isa = PBXGroup; + children = ( + 08E2E9372A4CD54A00B47A18 /* UIViewConfiguringSwiftUIView.swift */, + 08E2E9382A4CD54A00B47A18 /* EpoxySwiftUILayoutMargins.swift */, + 08E2E9392A4CD54A00B47A18 /* EpoxySwiftUIIntrinsicContentSizeInvalidator.swift */, + 08E2E93A2A4CD54A00B47A18 /* SwiftUIView.swift */, + 08E2E93B2A4CD54A00B47A18 /* EpoxyableView+SwiftUIView.swift */, + 08E2E93C2A4CD54A00B47A18 /* LayoutUtilities */, + 08E2E93F2A4CD54A00B47A18 /* EpoxySwiftUIHostingController.swift */, + 08E2E9402A4CD54A00B47A18 /* UIView+SwiftUIView.swift */, + 08E2E9412A4CD54A00B47A18 /* EpoxySwiftUIHostingView.swift */, + ); + path = SwiftUI; + sourceTree = ""; + }; + 08E2E93C2A4CD54A00B47A18 /* LayoutUtilities */ = { + isa = PBXGroup; + children = ( + 08E2E93D2A4CD54A00B47A18 /* MeasuringViewRepresentable.swift */, + 08E2E93E2A4CD54A00B47A18 /* SwiftUIMeasurementContainer.swift */, + ); + path = LayoutUtilities; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 08E2E9002A4CD50700B47A18 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 08E2E9042A4CD50700B47A18 /* EpoxyCore */ = { + isa = PBXNativeTarget; + buildConfigurationList = 08E2E90C2A4CD50700B47A18 /* Build configuration list for PBXNativeTarget "EpoxyCore" */; + buildPhases = ( + 08E2E9002A4CD50700B47A18 /* Headers */, + 08E2E9012A4CD50700B47A18 /* Sources */, + 08E2E9022A4CD50700B47A18 /* Frameworks */, + 08E2E9032A4CD50700B47A18 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = EpoxyCore; + productName = EpoxyCore; + productReference = 08E2E9052A4CD50700B47A18 /* EpoxyCore.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08E2E8FC2A4CD50700B47A18 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1430; + TargetAttributes = { + 08E2E9042A4CD50700B47A18 = { + CreatedOnToolsVersion = 14.3; + }; + }; + }; + buildConfigurationList = 08E2E8FF2A4CD50700B47A18 /* Build configuration list for PBXProject "EpoxyCore" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 08E2E8FB2A4CD50700B47A18; + productRefGroup = 08E2E9062A4CD50700B47A18 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 08E2E9042A4CD50700B47A18 /* EpoxyCore */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 08E2E9032A4CD50700B47A18 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 08E2E9012A4CD50700B47A18 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 08E2E9472A4CD54A00B47A18 /* DidSelectProviding.swift in Sources */, + 08E2E9592A4CD54A00B47A18 /* IndexChangeset.swift in Sources */, + 08E2E95A2A4CD54A00B47A18 /* SectionedChangeset.swift in Sources */, + 08E2E9422A4CD54A00B47A18 /* EpoxyLogger.swift in Sources */, + 08E2E95B2A4CD54A00B47A18 /* Collection+Diff.swift in Sources */, + 08E2E9462A4CD54A00B47A18 /* SetContentProviding.swift in Sources */, + 08E2E9452A4CD54A00B47A18 /* ViewDifferentiatorProviding.swift in Sources */, + 08E2E9522A4CD54A00B47A18 /* DataIDProviding.swift in Sources */, + 08E2E9552A4CD54A00B47A18 /* AnyEpoxyModelProperty.swift in Sources */, + 08E2E95F2A4CD54A00B47A18 /* ContentConfigurableView.swift in Sources */, + 08E2E94E2A4CD54A00B47A18 /* SetBehaviorsProviding.swift in Sources */, + 08E2E95D2A4CD54A00B47A18 /* Diffable.swift in Sources */, + 08E2E95E2A4CD54A00B47A18 /* StyledView.swift in Sources */, + 08E2E9682A4CD54A00B47A18 /* MeasuringViewRepresentable.swift in Sources */, + 08E2E9612A4CD54A00B47A18 /* ViewType.swift in Sources */, + 08E2E96C2A4CD54A00B47A18 /* EpoxySwiftUIHostingView.swift in Sources */, + 08E2E9432A4CD54A00B47A18 /* EpoxyModelStorage.swift in Sources */, + 08E2E9492A4CD54A00B47A18 /* WillDisplayProviding.swift in Sources */, + 08E2E95C2A4CD54A00B47A18 /* DiffableSection.swift in Sources */, + 08E2E9512A4CD54A00B47A18 /* AnimatedProviding.swift in Sources */, + 08E2E9652A4CD54A00B47A18 /* EpoxySwiftUIIntrinsicContentSizeInvalidator.swift in Sources */, + 08E2E9602A4CD54A00B47A18 /* EpoxyableView.swift in Sources */, + 08E2E9532A4CD54A00B47A18 /* ViewEpoxyModeled.swift in Sources */, + 08E2E9622A4CD54A00B47A18 /* BehaviorsConfigurableView.swift in Sources */, + 08E2E9482A4CD54A00B47A18 /* DidEndDisplayingProviding.swift in Sources */, + 08E2E9692A4CD54A00B47A18 /* SwiftUIMeasurementContainer.swift in Sources */, + 08E2E9642A4CD54A00B47A18 /* EpoxySwiftUILayoutMargins.swift in Sources */, + 08E2E94D2A4CD54A00B47A18 /* ViewProviding.swift in Sources */, + 08E2E9632A4CD54A00B47A18 /* UIViewConfiguringSwiftUIView.swift in Sources */, + 08E2E9662A4CD54A00B47A18 /* SwiftUIView.swift in Sources */, + 08E2E96A2A4CD54A00B47A18 /* EpoxySwiftUIHostingController.swift in Sources */, + 08E2E94F2A4CD54A00B47A18 /* StyleIDProviding.swift in Sources */, + 08E2E9502A4CD54A00B47A18 /* DidDisplayProviding.swift in Sources */, + 08E2E9542A4CD54A00B47A18 /* ClassReference.swift in Sources */, + 08E2E9562A4CD54A00B47A18 /* EpoxyModelProperty.swift in Sources */, + 08E2E9672A4CD54A00B47A18 /* EpoxyableView+SwiftUIView.swift in Sources */, + 08E2E94A2A4CD54A00B47A18 /* ErasedContentProviding.swift in Sources */, + 08E2E9582A4CD54A00B47A18 /* EpoxyModeled.swift in Sources */, + 08E2E96B2A4CD54A00B47A18 /* UIView+SwiftUIView.swift in Sources */, + 08E2E9442A4CD54A00B47A18 /* CallbackContextEpoxyModeled.swift in Sources */, + 08E2E94B2A4CD54A00B47A18 /* MakeViewProviding.swift in Sources */, + 08E2E94C2A4CD54A00B47A18 /* TraitCollectionProviding.swift in Sources */, + 08E2E9572A4CD54A00B47A18 /* EpoxyModelArrayBuilder.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 08E2E90A2A4CD50700B47A18 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TVOS_DEPLOYMENT_TARGET = 13.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 08E2E90B2A4CD50700B47A18 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TVOS_DEPLOYMENT_TARGET = 13.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 08E2E90D2A4CD50700B47A18 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.EpoxyCore; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 13.0; + }; + name = Debug; + }; + 08E2E90E2A4CD50700B47A18 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.EpoxyCore; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 13.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 08E2E8FF2A4CD50700B47A18 /* Build configuration list for PBXProject "EpoxyCore" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 08E2E90A2A4CD50700B47A18 /* Debug */, + 08E2E90B2A4CD50700B47A18 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 08E2E90C2A4CD50700B47A18 /* Build configuration list for PBXNativeTarget "EpoxyCore" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 08E2E90D2A4CD50700B47A18 /* Debug */, + 08E2E90E2A4CD50700B47A18 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08E2E8FC2A4CD50700B47A18 /* Project object */; +} diff --git a/Sources/EpoxyCore/EpoxyCore.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Sources/EpoxyCore/EpoxyCore.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Sources/EpoxyCore/EpoxyCore.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Sources/EpoxyCore/EpoxyCore.xcodeproj/xcshareddata/xcschemes/EpoxyCore.xcscheme b/Sources/EpoxyCore/EpoxyCore.xcodeproj/xcshareddata/xcschemes/EpoxyCore.xcscheme new file mode 100644 index 00000000..58fbf78e --- /dev/null +++ b/Sources/EpoxyCore/EpoxyCore.xcodeproj/xcshareddata/xcschemes/EpoxyCore.xcscheme @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/EpoxyCore/Model/Providers/MakeViewProviding.swift b/Sources/EpoxyCore/Model/Providers/MakeViewProviding.swift index da2d2cea..0cfadf6f 100644 --- a/Sources/EpoxyCore/Model/Providers/MakeViewProviding.swift +++ b/Sources/EpoxyCore/Model/Providers/MakeViewProviding.swift @@ -1,14 +1,12 @@ // Created by eric_horacek on 12/1/20. // Copyright © 2020 Airbnb Inc. All rights reserved. -import UIKit - // MARK: - MakeViewProviding /// The capability of constructing a `UIView`. public protocol MakeViewProviding { /// The view constructed when the `MakeView` closure is called. - associatedtype View: UIView + associatedtype View: ViewType /// A closure that's called to construct an instance of `View`. typealias MakeView = () -> View diff --git a/Sources/EpoxyCore/Model/Providers/TraitCollectionProviding.swift b/Sources/EpoxyCore/Model/Providers/TraitCollectionProviding.swift index eeb5978e..c3a23ccc 100644 --- a/Sources/EpoxyCore/Model/Providers/TraitCollectionProviding.swift +++ b/Sources/EpoxyCore/Model/Providers/TraitCollectionProviding.swift @@ -1,6 +1,7 @@ // Created by eric_horacek on 12/16/20. // Copyright © 2020 Airbnb Inc. All rights reserved. +#if !os(macOS) import UIKit /// The capability of providing a `UITraitCollection` instance. @@ -10,3 +11,4 @@ public protocol TraitCollectionProviding { /// The `UITraitCollection` instance provided by this type. var traitCollection: UITraitCollection { get } } +#endif diff --git a/Sources/EpoxyCore/Model/Providers/ViewProviding.swift b/Sources/EpoxyCore/Model/Providers/ViewProviding.swift index eaa7ab46..28532731 100644 --- a/Sources/EpoxyCore/Model/Providers/ViewProviding.swift +++ b/Sources/EpoxyCore/Model/Providers/ViewProviding.swift @@ -1,14 +1,12 @@ // Created by eric_horacek on 12/16/20. // Copyright © 2020 Airbnb Inc. All rights reserved. -import UIKit - /// The capability of providing an `View` instance /// /// Typically conformed to by the `CallbackContext` of a `CallbackContextEpoxyModeled`. public protocol ViewProviding { /// The `UIView` view of this type. - associatedtype View: UIView + associatedtype View: ViewType /// The `UIView` view instance provided by this type. var view: View { get } diff --git a/Sources/EpoxyCore/Model/ViewEpoxyModeled.swift b/Sources/EpoxyCore/Model/ViewEpoxyModeled.swift index c1887946..ef56b354 100644 --- a/Sources/EpoxyCore/Model/ViewEpoxyModeled.swift +++ b/Sources/EpoxyCore/Model/ViewEpoxyModeled.swift @@ -1,12 +1,10 @@ // Created by eric_horacek on 12/4/20. // Copyright © 2020 Airbnb Inc. All rights reserved. -import UIKit - /// An Epoxy model with an associated `UIView` type. public protocol ViewEpoxyModeled: EpoxyModeled { /// The view type associated with this model. /// /// An instance of this view is typically configured by this model. - associatedtype View: UIView + associatedtype View: ViewType } diff --git a/Sources/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift b/Sources/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift index 675cc7ff..3203775a 100644 --- a/Sources/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift +++ b/Sources/EpoxyCore/SwiftUI/EpoxySwiftUIHostingController.swift @@ -3,6 +3,7 @@ import SwiftUI +#if !os(macOS) // MARK: - EpoxySwiftUIUIHostingController /// A `UIHostingController` that hosts SwiftUI views within an Epoxy container, e.g. an Epoxy @@ -41,3 +42,4 @@ open class EpoxySwiftUIHostingController: UIHostingController: View { .environment(\.epoxyIntrinsicContentSizeInvalidator, environment.intrinsicContentSizeInvalidator) } } + +#endif diff --git a/Sources/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift b/Sources/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift index 45ccec3e..800c8d95 100644 --- a/Sources/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift +++ b/Sources/EpoxyCore/SwiftUI/EpoxyableView+SwiftUIView.swift @@ -26,9 +26,9 @@ extension StyledView where Self: ContentConfigurableView & BehaviorsConfigurable content: Content, style: Style, behaviors: Behaviors? = nil) - -> SwiftUIUIView + -> SwiftUIView { - SwiftUIUIView(storage: (content: content, style: style)) { + SwiftUIView(storage: (content: content, style: style)) { let view = Self(style: style) view.setContent(content, animated: false) return view @@ -74,9 +74,9 @@ extension StyledView public static func swiftUIView( content: Content, behaviors: Behaviors? = nil) - -> SwiftUIUIView + -> SwiftUIView { - SwiftUIUIView(storage: content) { + SwiftUIView(storage: content) { let view = Self() view.setContent(content, animated: false) return view @@ -118,9 +118,9 @@ extension StyledView public static func swiftUIView( style: Style, behaviors: Behaviors? = nil) - -> SwiftUIUIView + -> SwiftUIView { - SwiftUIUIView(storage: style) { + SwiftUIView(storage: style) { Self(style: style) } .configure { context in @@ -157,8 +157,8 @@ extension StyledView /// MyView.swiftUIView(…).sizing(.intrinsicSize) /// ``` /// The sizing defaults to `.automatic`. - public static func swiftUIView(behaviors: Behaviors? = nil) -> SwiftUIUIView { - SwiftUIUIView { + public static func swiftUIView(behaviors: Behaviors? = nil) -> SwiftUIView { + SwiftUIView { Self() } .configure { context in diff --git a/Sources/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringUIViewRepresentable.swift b/Sources/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift similarity index 54% rename from Sources/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringUIViewRepresentable.swift rename to Sources/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift index 823cc3fc..ed4fdc7c 100644 --- a/Sources/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringUIViewRepresentable.swift +++ b/Sources/EpoxyCore/SwiftUI/LayoutUtilities/MeasuringViewRepresentable.swift @@ -3,7 +3,7 @@ import SwiftUI -// MARK: - MeasuringUIViewRepresentable +// MARK: - MeasuringViewRepresentable /// A `UIViewRepresentable` that uses a `SwiftUIMeasurementContainer` wrapping its represented /// `UIView` to report its size that fits a proposed size to SwiftUI. @@ -12,12 +12,12 @@ import SwiftUI /// `sizeThatFits(…)` method. /// /// - SeeAlso: ``SwiftUIMeasurementContainer`` -public protocol MeasuringUIViewRepresentable: UIViewRepresentable +public protocol MeasuringViewRepresentable: ViewRepresentableType where - UIViewType == SwiftUIMeasurementContainer + RepresentableViewType == SwiftUIMeasurementContainer { /// The `UIView` content that's being measured by the enclosing `SwiftUIMeasurementContainer`. - associatedtype Content: UIView + associatedtype Content: ViewType /// The sizing strategy of the represented view. /// @@ -30,7 +30,7 @@ public protocol MeasuringUIViewRepresentable: UIViewRepresentable // MARK: Extensions -extension MeasuringUIViewRepresentable { +extension MeasuringViewRepresentable { /// Returns a copy of this view with its sizing strategy updated to the given `sizing` value. public func sizing(_ strategy: SwiftUIMeasurementContainerStrategy) -> Self { var copy = self @@ -41,7 +41,8 @@ extension MeasuringUIViewRepresentable { // MARK: Defaults -extension MeasuringUIViewRepresentable { +#if os(iOS) || os(tvOS) +extension MeasuringViewRepresentable { public func _overrideSizeThatFits( _ size: inout CGSize, in proposedSize: _ProposedSize, @@ -55,14 +56,14 @@ extension MeasuringUIViewRepresentable { // Creates a `CGSize` by replacing `nil`s with `UIView.noIntrinsicMetric` uiView.proposedSize = .init( - width: children.first { $0.label == "width" }?.value as? CGFloat ?? UIView.noIntrinsicMetric, - height: children.first { $0.label == "height" }?.value as? CGFloat ?? UIView.noIntrinsicMetric) + width: children.first { $0.label == "width" }?.value as? CGFloat ?? ViewType.noIntrinsicMetric, + height: children.first { $0.label == "height" }?.value as? CGFloat ?? ViewType.noIntrinsicMetric) size = uiView.measuredFittingSize } #if swift(>=5.7) // Proxy check for being built with the iOS 15 SDK - @available(iOS 16.0, *) + @available(iOS 16.0, tvOS 16.0, macOS 13.0, *) public func sizeThatFits( _ proposal: ProposedViewSize, uiView: UIViewType, @@ -73,10 +74,52 @@ extension MeasuringUIViewRepresentable { // Creates a size by replacing `nil`s with `UIView.noIntrinsicMetric` uiView.proposedSize = .init( - width: proposal.width ?? UIView.noIntrinsicMetric, - height: proposal.height ?? UIView.noIntrinsicMetric) + width: proposal.width ?? ViewType.noIntrinsicMetric, + height: proposal.height ?? ViewType.noIntrinsicMetric) return uiView.measuredFittingSize } #endif } + +#elseif os(macOS) +@available(macOS 10.15, *) +extension MeasuringViewRepresentable { + public func _overrideSizeThatFits( + _ size: inout CGSize, + in proposedSize: _ProposedSize, + nsView: NSViewType) + { + nsView.strategy = sizing + + let children = Mirror(reflecting: proposedSize).children + + // Creates a `CGSize` by replacing `nil`s with `UIView.noIntrinsicMetric` + nsView.proposedSize = .init( + width: children.first { $0.label == "width" }?.value as? CGFloat ?? ViewType.noIntrinsicMetric, + height: children.first { $0.label == "height" }?.value as? CGFloat ?? ViewType.noIntrinsicMetric) + + size = nsView.measuredFittingSize + } + + // Proxy check for being built with the macOS 13 SDK. + #if swift(>=5.7.1) + @available(macOS 13.0, *) + public func sizeThatFits( + _ proposal: ProposedViewSize, + nsView: NSViewType, + context _: Context) + -> CGSize? + { + nsView.strategy = sizing + + // Creates a size by replacing `nil`s with `UIView.noIntrinsicMetric` + nsView.proposedSize = .init( + width: proposal.width ?? ViewType.noIntrinsicMetric, + height: proposal.height ?? ViewType.noIntrinsicMetric) + + return nsView.measuredFittingSize + } + #endif +} +#endif diff --git a/Sources/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift b/Sources/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift index 2bdc9782..8730410f 100644 --- a/Sources/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift +++ b/Sources/EpoxyCore/SwiftUI/LayoutUtilities/SwiftUIMeasurementContainer.swift @@ -11,8 +11,8 @@ import SwiftUI /// This container view uses an injected proposed width to measure the view and return its ideal /// height through the `SwiftUISizingContext` binding. /// -/// - SeeAlso: ``MeasuringUIViewRepresentable`` -public final class SwiftUIMeasurementContainer: UIView { +/// - SeeAlso: ``MeasuringViewRepresentable`` +public final class SwiftUIMeasurementContainer: ViewType { // MARK: Lifecycle @@ -24,7 +24,7 @@ public final class SwiftUIMeasurementContainer: UIView { // is displayed, but the system gracefully recovers afterwards. On iOS 16, it's fine to pass // zero. let initialSize: CGSize - if #available(iOS 16, *) { + if #available(iOS 16, tvOS 16, macOS 13, *) { initialSize = .zero } else { initialSize = .init(width: 375, height: 150) @@ -95,6 +95,18 @@ public final class SwiftUIMeasurementContainer: UIView { _intrinsicContentSize } + #if os(macOS) + public override func layout() { + super.layout() + + // We need to re-measure the view whenever the size of the bounds changes, as the previous size + // may now be incorrect. + if latestMeasurementBoundsSize != nil, bounds.size != latestMeasurementBoundsSize { + // This will trigger SwiftUI to re-measure the view. + super.invalidateIntrinsicContentSize() + } + } + #else public override func layoutSubviews() { super.layoutSubviews() @@ -105,6 +117,7 @@ public final class SwiftUIMeasurementContainer: UIView { super.invalidateIntrinsicContentSize() } } + #endif public override func invalidateIntrinsicContentSize() { super.invalidateIntrinsicContentSize() @@ -219,6 +232,14 @@ public final class SwiftUIMeasurementContainer: UIView { constraints[.trailing]?.priority = .almostRequired constraints[.bottom]?.priority = .almostRequired } + + #if os(macOS) + // On macOS, views default to having required constraints setting their height / width + // equal to their intrinsic content size. These have to be disabled in favor of the constraints + // we create here. + content.isVerticalContentSizeConstraintActive = false + content.isHorizontalContentSizeConstraintActive = false + #endif } /// Measures the `uiView`, storing the resulting size in `measuredIntrinsicContentSize`. @@ -235,11 +256,11 @@ public final class SwiftUIMeasurementContainer: UIView { case .intrinsicHeightProposedWidth: measuredSize = content.systemLayoutFittingIntrinsicHeightFixedWidth(proposedSizeElseBounds.width) - measuredSize.width = UIView.noIntrinsicMetric + measuredSize.width = ViewType.noIntrinsicMetric case .intrinsicWidthProposedHeight: measuredSize = content.systemLayoutFittingIntrinsicWidthFixedHeight(proposedSizeElseBounds.height) - measuredSize.height = UIView.noIntrinsicMetric + measuredSize.height = ViewType.noIntrinsicMetric case .intrinsic(let size): measuredSize = size @@ -252,18 +273,18 @@ public final class SwiftUIMeasurementContainer: UIView { // we don't want that scenario to prevent size changes when there is actually more space // available. if - proposedSize.width != UIView.noIntrinsicMetric, + proposedSize.width != ViewType.noIntrinsicMetric, measuredSize.width > proposedSizeElseBounds.width, _intrinsicContentSize.width != proposedSize.width { - measuredSize.width = UIView.noIntrinsicMetric + measuredSize.width = ViewType.noIntrinsicMetric } if - proposedSize.height != UIView.noIntrinsicMetric, + proposedSize.height != ViewType.noIntrinsicMetric, measuredSize.height > proposedSizeElseBounds.height, _intrinsicContentSize.height != proposedSize.height { - measuredSize.height = UIView.noIntrinsicMetric + measuredSize.height = ViewType.noIntrinsicMetric } } @@ -329,23 +350,27 @@ private enum ResolvedSwiftUIMeasurementContainerStrategy { // MARK: - UILayoutPriority -extension UILayoutPriority { +extension LayoutPriorityType { /// An "almost required" constraint, useful for creating near-required constraints that don't /// error when unable to be satisfied. @nonobjc - fileprivate static var almostRequired: UILayoutPriority { .init(rawValue: required.rawValue - 1) } + fileprivate static var almostRequired: LayoutPriorityType { .init(rawValue: required.rawValue - 1) } } // MARK: - UIView -extension UIView { +extension ViewType { /// The `systemLayoutSizeFitting(…)` of this view with a compressed size and fitting priorities. @nonobjc fileprivate func systemLayoutFittingIntrinsicSize() -> CGSize { + #if os(macOS) + intrinsicContentSize + #else systemLayoutSizeFitting( UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: .fittingSizeLevel, verticalFittingPriority: .fittingSizeLevel) + #endif } /// The `systemLayoutSizeFitting(…)` of this view with a compressed height with a fitting size @@ -353,15 +378,19 @@ extension UIView { @nonobjc fileprivate func systemLayoutFittingIntrinsicHeightFixedWidth( _ width: CGFloat, - priority: UILayoutPriority = .almostRequired) + priority: LayoutPriorityType = .almostRequired) -> CGSize { + #if os(macOS) + return CGSize(width: width, height: intrinsicContentSize.height) + #else let targetSize = CGSize(width: width, height: UIView.layoutFittingCompressedSize.height) return systemLayoutSizeFitting( targetSize, withHorizontalFittingPriority: priority, verticalFittingPriority: .fittingSizeLevel) + #endif } /// The `systemLayoutSizeFitting(…)` of this view with a compressed width with a fitting size @@ -369,15 +398,19 @@ extension UIView { @nonobjc fileprivate func systemLayoutFittingIntrinsicWidthFixedHeight( _ height: CGFloat, - priority: UILayoutPriority = .almostRequired) + priority: LayoutPriorityType = .almostRequired) -> CGSize { + #if os(macOS) + return CGSize(width: intrinsicContentSize.width, height: height) + #else let targetSize = CGSize(width: UIView.layoutFittingCompressedSize.width, height: height) return systemLayoutSizeFitting( targetSize, withHorizontalFittingPriority: .fittingSizeLevel, verticalFittingPriority: priority) + #endif } /// Whether this view or any of its subviews has a subview that has a double layout pass `UILabel` @@ -385,6 +418,9 @@ extension UIView { /// `intrinsicHeightProposedWidth` sizing strategy to allow the label to wrap and grow. @nonobjc fileprivate func containsDoubleLayoutPassSubviews() -> Bool { + #if os(macOS) + return false + #else var contains = false if let label = self as? UILabel, label.preferredMaxLayoutWidth > 0 { contains = true @@ -393,6 +429,7 @@ extension UIView { contains = contains || subview.containsDoubleLayoutPassSubviews() } return contains + #endif } } @@ -401,14 +438,14 @@ extension UIView { extension CGSize { /// A `CGSize` with `noIntrinsicMetric` for both its width and height. fileprivate static var noIntrinsicMetric: CGSize { - .init(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric) + .init(width: ViewType.noIntrinsicMetric, height: ViewType.noIntrinsicMetric) } /// Returns a `CGSize` with its width and/or height replaced with the corresponding field of the /// provided `fallback` size if they are `UIView.noIntrinsicMetric`. fileprivate func replacingNoIntrinsicMetric(with fallback: CGSize) -> CGSize { .init( - width: width == UIView.noIntrinsicMetric ? fallback.width : width, - height: height == UIView.noIntrinsicMetric ? fallback.height : height) + width: width == ViewType.noIntrinsicMetric ? fallback.width : width, + height: height == ViewType.noIntrinsicMetric ? fallback.height : height) } } diff --git a/Sources/EpoxyCore/SwiftUI/SwiftUIUIView.swift b/Sources/EpoxyCore/SwiftUI/SwiftUIView.swift similarity index 79% rename from Sources/EpoxyCore/SwiftUI/SwiftUIUIView.swift rename to Sources/EpoxyCore/SwiftUI/SwiftUIView.swift index 6448e1be..6454e1e5 100644 --- a/Sources/EpoxyCore/SwiftUI/SwiftUIUIView.swift +++ b/Sources/EpoxyCore/SwiftUI/SwiftUIView.swift @@ -3,7 +3,7 @@ import SwiftUI -// MARK: - SwiftUIUIView +// MARK: - SwiftUIView /// A `UIViewRepresentable` SwiftUI `View` that wraps its `Content` `UIView` within a /// `SwiftUIMeasurementContainer`, used to size a UIKit view correctly within a SwiftUI view @@ -11,7 +11,7 @@ import SwiftUI /// /// Includes an optional generic `Storage` value, which can be used to compare old and new values /// across state changes to prevent redundant view updates. -public struct SwiftUIUIView: MeasuringUIViewRepresentable, +public struct SwiftUIView: MeasuringViewRepresentable, UIViewConfiguringSwiftUIView { @@ -49,13 +49,32 @@ public struct SwiftUIUIView: MeasuringUIViewRepresenta // MARK: UIViewRepresentable -extension SwiftUIUIView { - public func makeUIView(context _: Context) -> SwiftUIMeasurementContainer { +extension SwiftUIView { + public func makeCoordinator() -> Coordinator { + Coordinator(storage: storage) + } + + #if os(macOS) + public func makeNSView(context _: Context) -> SwiftUIMeasurementContainer { SwiftUIMeasurementContainer(content: makeContent(), strategy: sizing) } - public func makeCoordinator() -> Coordinator { - Coordinator(storage: storage) + public func updateNSView(_ uiView: SwiftUIMeasurementContainer, context: Context) { + let oldStorage = context.coordinator.storage + context.coordinator.storage = storage + + let configurationContext = ConfigurationContext( + oldStorage: oldStorage, + viewRepresentableContext: context, + container: uiView) + + for configuration in configurations { + configuration(configurationContext) + } + } + #else + public func makeUIView(context _: Context) -> SwiftUIMeasurementContainer { + SwiftUIMeasurementContainer(content: makeContent(), strategy: sizing) } public func updateUIView(_ uiView: SwiftUIMeasurementContainer, context: Context) { @@ -71,15 +90,16 @@ extension SwiftUIUIView { configuration(configurationContext) } } + #endif } -// MARK: SwiftUIUIView.ConfigurationContext +// MARK: SwiftUIView.ConfigurationContext -extension SwiftUIUIView { +extension SwiftUIView { /// The configuration context that's available to configure the `Content` view whenever the /// `updateUIView()` method is invoked via a configuration closure. public struct ConfigurationContext: ViewProviding { - /// The previous value for the `Storage` of this `SwiftUIUIView`, which can be used to store + /// The previous value for the `Storage` of this `SwiftUIView`, which can be used to store /// values across state changes to prevent redundant view updates. public var oldStorage: Storage @@ -104,9 +124,9 @@ extension SwiftUIUIView { } } -// MARK: SwiftUIUIView.Coordinator +// MARK: SwiftUIView.Coordinator -extension SwiftUIUIView { +extension SwiftUIView { /// A coordinator that stores the `storage` associated with this view, enabling the old storage /// value to be accessed during the `updateUIView(…)`. public final class Coordinator { diff --git a/Sources/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift b/Sources/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift index 4ecdb669..4307a7ce 100644 --- a/Sources/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift +++ b/Sources/EpoxyCore/SwiftUI/UIView+SwiftUIView.swift @@ -3,9 +3,9 @@ import SwiftUI -// MARK: - UIViewProtocol + swiftUIView +// MARK: - ViewTypeProtocol + swiftUIView -extension UIViewProtocol { +extension ViewTypeProtocol { /// Returns a SwiftUI `View` representing this `UIView`, constructed with the given `makeView` /// closure and sized with the given sizing configuration. /// @@ -24,16 +24,16 @@ extension UIViewProtocol { /// MyView.swiftUIView(…).sizing(.intrinsicSize) /// ``` /// The sizing defaults to `.automatic`. - public static func swiftUIView(makeView: @escaping () -> Self) -> SwiftUIUIView { - SwiftUIUIView(makeContent: makeView) + public static func swiftUIView(makeView: @escaping () -> Self) -> SwiftUIView { + SwiftUIView(makeContent: makeView) } } -// MARK: - UIViewProtocol +// MARK: - ViewTypeProtocol /// A protocol that all `UIView`s conform to, enabling extensions that have a `Self` reference. -public protocol UIViewProtocol: UIView { } +public protocol ViewTypeProtocol: ViewType { } -// MARK: - UIView + UIViewProtocol +// MARK: - ViewType + ViewTypeProtocol -extension UIView: UIViewProtocol { } +extension ViewType: ViewTypeProtocol { } diff --git a/Sources/EpoxyCore/Views/BehaviorsConfigurableView.swift b/Sources/EpoxyCore/Views/BehaviorsConfigurableView.swift index d9c6d36b..a6ca8c4c 100644 --- a/Sources/EpoxyCore/Views/BehaviorsConfigurableView.swift +++ b/Sources/EpoxyCore/Views/BehaviorsConfigurableView.swift @@ -1,8 +1,6 @@ // Created by Tyler Hedrick on 5/26/20. // Copyright © 2020 Airbnb Inc. All rights reserved. -import UIKit - // MARK: - BehaviorsConfigurableView /// A view that can be configured with a `Behaviors` instance that contains the view's non- @@ -20,7 +18,7 @@ import UIKit /// - SeeAlso: `ContentConfigurableView` /// - SeeAlso: `StyledView` /// - SeeAlso: `EpoxyableView` -public protocol BehaviorsConfigurableView: UIView { +public protocol BehaviorsConfigurableView: ViewType { /// The non-`Equatable` properties that can be changed over of the lifecycle this View's /// instances, e.g. callback closures or delegates. /// diff --git a/Sources/EpoxyCore/Views/ContentConfigurableView.swift b/Sources/EpoxyCore/Views/ContentConfigurableView.swift index 94f9d157..1c77ade4 100644 --- a/Sources/EpoxyCore/Views/ContentConfigurableView.swift +++ b/Sources/EpoxyCore/Views/ContentConfigurableView.swift @@ -1,8 +1,6 @@ // Created by Laura Skelton on 5/30/17. // Copyright © 2017 Airbnb. All rights reserved. -import UIKit - // MARK: - ContentConfigurableView /// A view that can be configured with a `Content` instance that contains the view's `Equatable` @@ -19,7 +17,7 @@ import UIKit /// - SeeAlso: `BehaviorsConfigurableView` /// - SeeAlso: `StyledView` /// - SeeAlso: `EpoxyableView` -public protocol ContentConfigurableView: UIView { +public protocol ContentConfigurableView: ViewType { /// The `Equatable` properties that can be updated on instances of this view, e.g. text `String`s /// or image `URL`s. /// diff --git a/Sources/EpoxyCore/Views/StyledView.swift b/Sources/EpoxyCore/Views/StyledView.swift index e34883e4..7a3bba2e 100644 --- a/Sources/EpoxyCore/Views/StyledView.swift +++ b/Sources/EpoxyCore/Views/StyledView.swift @@ -1,8 +1,6 @@ // Created by Laura Skelton on 4/14/16. // Copyright © 2016 Airbnb. All rights reserved. -import UIKit - // MARK: - StyledView /// A view that can be initialized with a `Style` instance that contains the view's invariant @@ -23,7 +21,7 @@ import UIKit /// - SeeAlso: `ContentConfigurableView` /// - SeeAlso: `BehaviorsConfigurableView` /// - SeeAlso: `EpoxyableView` -public protocol StyledView: UIView { +public protocol StyledView: ViewType { /// The style type of this view, passed into its initializer to configure the resulting instance. /// /// Defaults to `Never` for views that do not have a `Style`. diff --git a/Sources/EpoxyCore/Views/ViewType.swift b/Sources/EpoxyCore/Views/ViewType.swift new file mode 100644 index 00000000..1a0dbe01 --- /dev/null +++ b/Sources/EpoxyCore/Views/ViewType.swift @@ -0,0 +1,47 @@ +// Created by Cal Stephens on 6/26/23. +// Copyright © 2023 Airbnb Inc. All rights reserved. + +import SwiftUI + +#if os(iOS) || os(tvOS) +import UIKit + +/// The platform's main view type. +/// Either `UIView` on iOS/tvOS or `NSView` on macOS. +public typealias ViewType = UIView + +/// The platform's SwiftUI view representable type. +/// Either `UIViewRepresentable` on iOS/tvOS or `NSViewRepresentable` on macOS. +public typealias ViewRepresentableType = UIViewRepresentable + +/// The platform's layout constraint priority type. +/// Either `UILayoutPriority` on iOS/tvOS or `NSLayoutConstraint.Priority` on macOS. +public typealias LayoutPriorityType = UILayoutPriority + +extension ViewRepresentableType { + /// The platform's view type for `ViewRepresentableType`. + /// Either `UIViewType` on iOS/tvOS or `NSViewType` on macOS. + public typealias RepresentableViewType = UIViewType +} + +#elseif os(macOS) +import AppKit + +/// The platform's main view type. +/// Either `UIView` on iOS/tvOS, or `NSView` on macOS. +public typealias ViewType = NSView + +/// The platform's SwiftUI view representable type. +/// Either `UIViewRepresentable` on iOS/tvOS, or `NSViewRepresentable` on macOS. +public typealias ViewRepresentableType = NSViewRepresentable + +/// The platform's layout constraint priority type. +/// Either `UILayoutPriority` on iOS/tvOS, or `NSLayoutConstraint.Priority` on macOS. +public typealias LayoutPriorityType = NSLayoutConstraint.Priority + +extension ViewRepresentableType { + /// The platform's view type for `ViewRepresentableType`. + /// Either `UIViewType` on iOS/tvOS or `NSViewType` on macOS. + public typealias RepresentableViewType = NSViewType +} +#endif