diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..938cccf7 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,196 @@ +name: "SDWebImageSwiftUI CI" + +on: + push: + branches: + - master + pull_request: + branches: + - '*' + +permissions: + contents: read + +jobs: + Pods: + name: Cocoapods Lint + runs-on: macos-14 + env: + DEVELOPER_DIR: /Applications/Xcode_15.2.app + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Cocoapods + run: gem install cocoapods --no-document --quiet + + - name: Install Xcpretty + run: gem install xcpretty --no-document --quiet + + - name: Pod Update + run: pod repo update --silent + + - name: Pod Install + run: pod install + + - name: Run SDWebImageSwiftUI podspec lint + run: | + set -o pipefail + pod lib lint SDWebImageSwiftUI.podspec --allow-warnings --skip-tests + + Demo: + name: Run Demo + runs-on: macos-14 + env: + DEVELOPER_DIR: /Applications/Xcode_15.2.app + WORKSPACE_NAME: SDWebImageSwiftUI.xcworkspace + OSXSCHEME: SDWebImageSwiftUIDemo-macOS + iOSSCHEME: SDWebImageSwiftUIDemo + TVSCHEME: SDWebImageSwiftUIDemo-tvOS + WATCHSCHEME: SDWebImageSwiftUIDemo-watchOS WatchKit App + iosDestination: platform=iOS Simulator,name=iPhone 15 Pro + macOSDestination: platform=macOS,arch=x86_64 + macCatalystDestination: platform=macOS,arch=x86_64,variant=Mac Catalyst + tvOSDestination: platform=tvOS Simulator,name=Apple TV 4K (3rd generation) + watchOSDestination: platform=watchOS Simulator,name=Apple Watch Series 9 (45mm) + visionOSDestination: platform=visionOS Simulator,name=Apple Vision Pro + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Clean DerivedData + run: | + rm -rf ~/Library/Developer/Xcode/DerivedData/ + mkdir DerivedData + + - name: Install Cocoapods + run: gem install cocoapods --no-document --quiet + + - name: Install Xcpretty + run: gem install xcpretty --no-document --quiet + + - name: Pod Update + run: pod repo update --silent + + - name: Pod Install + run: pod install + + - name: Run demo for OSX + run: | + set -o pipefail + xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.OSXSCHEME }}" -destination "${{ env.macOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + + - name: Run demo for iOS + run: | + set -o pipefail + xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.iOSSCHEME }}" -destination "${{ env.iosDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + + - name: Run demo for TV + run: | + set -o pipefail + xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.TVSCHEME }}" -destination "${{ env.tvOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + + - name: Run demo for Watch + run: | + set -o pipefail + xcodebuild build -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ env.WATCHSCHEME }}" -destination "${{ env.watchOSDestination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + + Test: + name: Unit Test + runs-on: macos-14 + env: + DEVELOPER_DIR: /Applications/Xcode_15.2.app + WORKSPACE_NAME: SDWebImageSwiftUI.xcworkspace + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + # use matrix to generate jobs for each platform + strategy: + fail-fast: false + matrix: + platform: [iOS, macOS, tvOS] + include: + - platform: iOS + destination: platform=iOS Simulator,name=iPhone 15 Pro + scheme: SDWebImageSwiftUITests + flag: ios + - platform: macOS + destination: platform=macOS,arch=x86_64 + scheme: SDWebImageSwiftUITests macOS + flag: macos + - platform: tvOS + destination: platform=tvOS Simulator,name=Apple TV 4K (3rd generation) + scheme: SDWebImageSwiftUITests tvOS + flag: tvos + # - platform: visionOS + # destination: platform=visionOS Simulator,name=Apple Vision Pro + # scheme: Vision + # flag: visionos + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install Cocoapods + run: gem install cocoapods --no-document --quiet + + - name: Install Xcpretty + run: gem install xcpretty --no-document --quiet + + - name: Pod Update + run: pod repo update --silent + + - name: Pod Install + run: pod install + + - name: Clean DerivedData + run: | + rm -rf ~/Library/Developer/Xcode/DerivedData/ + mkdir DerivedData + + - name: Run test + run: | + set -o pipefail + xcodebuild build-for-testing -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c + xcodebuild test-without-building -workspace "${{ env.WORKSPACE_NAME }}" -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" -configuration Debug CODE_SIGNING_ALLOWED=NO + mv ~/Library/Developer/Xcode/DerivedData/ "./DerivedData/${{ matrix.platform }}" + + - name: Code Coverage + run: | + set -o pipefail + export PATH="/usr/local/opt/curl/bin:$PATH" + curl --version + bash <(curl -s https://codecov.io/bash) -v -D "./DerivedData/${{ matrix.platform }}" -J '^SDWebImageSwiftUI$' -c -X gcov -F "${{ matrix.flag }}" + + Build: + name: Build Library + runs-on: macos-14 + env: + DEVELOPER_DIR: /Applications/Xcode_15.2.app + PROJECT_NAME: SDWebImageSwiftUI.xcodeproj + OSXSCHEME: SDWebImageSwiftUI macOS + iOSSCHEME: SDWebImageSwiftUI + TVSCHEME: SDWebImageSwiftUI tvOS + WATCHSCHEME: SDWebImageSwiftUI watchOS + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Build the SwiftPM + run: | + set -o pipefail + swift build + rm -rf ~/.build + + - name: Install Carthage + run: brew install carthage + + - name: Carthage Update + run: ./carthage.sh update --platform "iOS, tvOS, macOS, watchOS" + + - name: Build as dynamic frameworks + run: | + set -o pipefail + xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.OSXSCHEME }}" -sdk macosx -configuration Release | xcpretty -c + xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.iOSSCHEME }}" -sdk iphoneos -configuration Release | xcpretty -c + xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.TVSCHEME }}" -sdk appletvos -configuration Release | xcpretty -c + xcodebuild build -project "${{ env.PROJECT_NAME }}" -scheme "${{ env.WATCHSCHEME }}" -sdk watchos -configuration Release | xcpretty -c diff --git a/.gitignore b/.gitignore index 2b4d8724..832611ef 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ Podfile.lock # SwiftPM .build .swiftpm +Package.resolved diff --git a/CHANGELOG.md b/CHANGELOG.md index 2894a3bf..05a05072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,113 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.1.3] - 2024-11-06 +- Fixed old version compiler does not support automatic self capture in Xcode 14.2 and Swift 5.7.2 #340 +- Fix the data race because progress block is called in non-main queue #341 + +## [3.1.2] - 2024-08-29 +- Allows easy to use WebImage with isAnimating default to false and change to true later #333 +- Note: This changes WebImage's internal loaded image from `UIImage/NSImage` to `SDAnimatedImage`, which is compatible for `UIImageView/NSImageView` + +## [3.1.1] - 2024-07-01 +- Fix the transition visual jump between placeholderImage and final image for AnimatedImage #326 + +## [3.1.0] - 2024-06-27 +- Re-implements the aspectRatio support on AnimatedImage, fix issue like cornerRadius #324 +- Add Image scale support in WebImage init #323 +- Update platform names in `available` attributes #321 +- - This is source compatible but binary incompatible version + +## [3.0.4] - 2024-04-30 +- Trying to move the initial state setup before onAppear to fix the watchOS switching url or any other state issue #316 +- This solve a issue in history when sometimes SwiftUI does not trigger the `onAppear` and cause state error, like #312 #314 + +## [3.0.3] - 2024-04-29 +- Added totally empty privacy manifest #315 +- People who facing the issue because of Privacy Manifest declaration during ITC validation can try this version + +## [3.0.2] - 2024-03-27 +- Fix the assert crash then when using Data/Name in AnimatedImage #309 + +## [3.0.1] - 2024-03-18 +- Fix the issue for WebImage/AnimatedImage when url is nil will not cause the reloading #304 + +## [3.0.0] - 2024-03-09 +- This is the first release for 3.x version. Bump the min deplouyment from SwiftUI 1.0 to 2.0 (means iOS 14/macOS 11/tvOS 14/watchOS 7/visionOS 1) +- Fix AnimatedImage aspectRatio issue when ratio is nil #301 +- Upgrade to support visionOS on CocoaPods #298 + +## [3.0.0-beta.3] - 2023-12-04 + +### Changed +- Update the AnimatedImage API to expose the SDAnimatedImageView #285 +- Fix the AnimatedImgae rendering mode about compatible with SDWebImage 5.18+ + +## [3.0.0-beta.2] - 2023-10-21 + +### Changed +- Update the WebImage API to match SwiftUI.AsyncImage #275 @Kyle-Ye +- Allows to use UIImage/NSImage as defaults when init the AnimatedImage with JPEG data #277 + +### Removed +- `WebImage.placeholder(@ViewBuilder content: () -> T) -> WebImage` +- `WebImage.placeholder(_ image: Image) -> WebImage` +- `AnimatedImage.placeholder(@ViewBuilder content: () -> T) -> AnimatedImage` +- `AnimatedImage.placeholder(_ image: PlatformImage) -> AnimatedImage` + +## [3.0.0-beta] - 2023-09-02 + +### Added +- (Part 1) Support compile for visionOS (no package manager support) #267 + +### Changed + +- Drop iOS 13/macOS 10.15/tvOS 13/watchOS 6 support #250 +- ProgressIndicator and ActivityIndicator is removed. Use `ProgressView` instead +- Availability is changed to iOS 14/macOS 11/tvOS 11/watchOS 7 +- Embed `SwiftUIBackports` dependency is removed. + +## [2.2.3] - 2023-04-32 +- Fix the issue that Static Library + Library Evolution cause the build issue on Swift 5.8 #263 + +## [2.2.2] - 2022-12-27 + +### Fixed +- Fix the bug that isAnimating control does not works on WebImage #251 +- Note you should upgrade the SDWebImage 5.14.3+, or this may cause extra Xcode 14's runtime warning (function is unaffected) + +## [2.2.1] - 2022-09-23 + +### Fixed + +- Fix the nil url always returns Error will cause infinity onAppear call and image manager to load, which waste CPU #235 +- Fix the case which sometimes the player does not stop when WebImage it out of screen #236 +- Al v2.2.0 users are recommended to update + +## [2.2.0] - 2022-09-22 + +### Fixed + +- Fix iOS 13 compatibility #232 +- Fix WebImage/Animated using @State to publish changes +- Al v2.1.0 users are recommended to update + +### Changed +- ImageManager API changes. The init method has no args, use `load(url:options:context:)` instead + +## [2.1.0] - 2022-09-15 + +### Fixed + +- Refactor WebImage/AnimatedImage using SwiftUIBackports and StateObject #227 +- Fix iOS 16 undefined behavior warnings because of Publishing changes from within view updates. +- Fix iOS 14+ WebImage behavior using `@StateObject` (and backport on iOS 13) + +### Changed + +- The `IndicatorReportable` is misused and removed. Use `IndicatorStatus` instead. +- Deprecate iOS 13 support, this may be the last version to support iOS 13. + ## [2.0.2] - 2021-03-10 ### Fixed diff --git a/Cartfile b/Cartfile index dcfaf022..54618334 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "SDWebImage/SDWebImage" ~> 5.10 \ No newline at end of file +github "SDWebImage/SDWebImage" ~> 5.21.1 diff --git a/Example/Podfile b/Example/Podfile deleted file mode 100644 index d570628d..00000000 --- a/Example/Podfile +++ /dev/null @@ -1,28 +0,0 @@ -use_frameworks! - -def all_pods - pod 'SDWebImageSwiftUI', :path => '../' - pod 'SDWebImageWebPCoder' - pod 'SDWebImageSVGCoder' - pod 'SDWebImagePDFCoder' -end - -target 'SDWebImageSwiftUIDemo' do - platform :ios, '13.0' - all_pods -end - -target 'SDWebImageSwiftUIDemo-macOS' do - platform :osx, '10.15' - all_pods -end - -target 'SDWebImageSwiftUIDemo-tvOS' do - platform :tvos, '13.0' - all_pods -end - -target 'SDWebImageSwiftUIDemo-watchOS WatchKit Extension' do - platform :watchos, '7.0' - all_pods -end \ No newline at end of file diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 9261c3f8..2a26b45d 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -3,18 +3,60 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ - 1794840F9DF6D50ADA448C9B /* Pods_SDWebImageSwiftUIDemo_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 473D7886C23B6FC5AFE35842 /* Pods_SDWebImageSwiftUIDemo_macOS.framework */; }; 320CDC2C22FADB44007CF858 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2B22FADB44007CF858 /* AppDelegate.swift */; }; 320CDC2E22FADB44007CF858 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2D22FADB44007CF858 /* SceneDelegate.swift */; }; 320CDC3022FADB44007CF858 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; 320CDC3222FADB45007CF858 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3122FADB45007CF858 /* Assets.xcassets */; }; 320CDC3522FADB45007CF858 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3422FADB45007CF858 /* Preview Assets.xcassets */; }; 320CDC3822FADB45007CF858 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */; }; + 322E0DF828D331A20003A55F /* WebImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF128D331A20003A55F /* WebImageTests.swift */; }; + 322E0DF928D331A20003A55F /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 322E0DF228D331A20003A55F /* Images.bundle */; }; + 322E0DFA28D331A20003A55F /* ImageManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF328D331A20003A55F /* ImageManagerTests.swift */; }; + 322E0DFB28D331A20003A55F /* AnimatedImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF428D331A20003A55F /* AnimatedImageTests.swift */; }; + 322E0DFD28D331A20003A55F /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF628D331A20003A55F /* TestUtils.swift */; }; + 322E0E1828D3320D0003A55F /* ImageManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF328D331A20003A55F /* ImageManagerTests.swift */; }; + 322E0E1A28D3320D0003A55F /* WebImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF128D331A20003A55F /* WebImageTests.swift */; }; + 322E0E1B28D3320D0003A55F /* AnimatedImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF428D331A20003A55F /* AnimatedImageTests.swift */; }; + 322E0E1C28D3320D0003A55F /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF628D331A20003A55F /* TestUtils.swift */; }; + 322E0E1D28D3320D0003A55F /* ImageManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF328D331A20003A55F /* ImageManagerTests.swift */; }; + 322E0E1F28D3320D0003A55F /* WebImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF128D331A20003A55F /* WebImageTests.swift */; }; + 322E0E2028D3320D0003A55F /* AnimatedImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF428D331A20003A55F /* AnimatedImageTests.swift */; }; + 322E0E2128D3320D0003A55F /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0DF628D331A20003A55F /* TestUtils.swift */; }; + 322E0E2228D332130003A55F /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 322E0DF228D331A20003A55F /* Images.bundle */; }; + 322E0E2328D332130003A55F /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 322E0DF228D331A20003A55F /* Images.bundle */; }; 326B0D712345C01900D28269 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; + 32ABE4D92AA3753300331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4D82AA3753300331406 /* SDWebImageWebPCoder */; }; + 32ABE4DC2AA3755D00331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4DB2AA3755D00331406 /* SDWebImageSVGCoder */; }; + 32ABE4DF2AA3756A00331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4DE2AA3756A00331406 /* SDWebImagePDFCoder */; }; + 32ABE4E12AA3757B00331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4E02AA3757B00331406 /* SDWebImageWebPCoder */; }; + 32ABE4E32AA3757B00331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4E22AA3757B00331406 /* SDWebImageSVGCoder */; }; + 32ABE4E52AA3757B00331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4E42AA3757B00331406 /* SDWebImagePDFCoder */; }; + 32ABE4E72AA3758400331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4E62AA3758400331406 /* SDWebImageWebPCoder */; }; + 32ABE4E92AA3758400331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4E82AA3758400331406 /* SDWebImageSVGCoder */; }; + 32ABE4EB2AA3758400331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4EA2AA3758400331406 /* SDWebImagePDFCoder */; }; + 32ABE4F32AA3759900331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F22AA3759900331406 /* SDWebImageWebPCoder */; }; + 32ABE4F52AA3759900331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F42AA3759900331406 /* SDWebImageSVGCoder */; }; + 32ABE4F72AA3759900331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4F62AA3759900331406 /* SDWebImagePDFCoder */; }; + 32ABE4FD2AA375A500331406 /* SDWebImageWebPCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4FC2AA375A500331406 /* SDWebImageWebPCoder */; }; + 32ABE4FF2AA375A500331406 /* SDWebImageSVGCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE4FE2AA375A500331406 /* SDWebImageSVGCoder */; }; + 32ABE5012AA375A500331406 /* SDWebImagePDFCoder in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE5002AA375A500331406 /* SDWebImagePDFCoder */; }; + 32ABE5032AA375B400331406 /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32ABE5022AA375B400331406 /* SDWebImageSwiftUI */; }; + 32B13E812AA368B700BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */; }; + 32B13E852AA368C600BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */; }; + 32B13E8F2AA368E100BE9B5B /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */; }; + 32D5D1672A445B260098BDFC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D5D1662A445B260098BDFC /* AppDelegate.swift */; }; + 32D5D16B2A445B260098BDFC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32D5D16A2A445B260098BDFC /* Assets.xcassets */; }; + 32D5D16E2A445B260098BDFC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32D5D16D2A445B260098BDFC /* Preview Assets.xcassets */; }; + 32D5D1722A445BF00098BDFC /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; + 32D5D1732A445BF00098BDFC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; + 32D5D1762A445C8F0098BDFC /* SDWebImageSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */; }; + 32DAC2392AA3784800A085AE /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 32DAC2382AA3784800A085AE /* ViewInspector */; }; + 32DAC23B2AA3784D00A085AE /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 32DAC23A2AA3784D00A085AE /* ViewInspector */; }; + 32DAC23D2AA3785100A085AE /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 32DAC23C2AA3785100A085AE /* ViewInspector */; }; 32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E5290B2348A0C700EA46FF /* AppDelegate.swift */; }; 32E529102348A0C900EA46FF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E5290F2348A0C900EA46FF /* Assets.xcassets */; }; 32E529132348A0C900EA46FF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E529122348A0C900EA46FF /* Preview Assets.xcassets */; }; @@ -37,12 +79,30 @@ 32E529662348A10B00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; 32E529682348A10C00EA46FF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; 32E529692348A10C00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; - 68543C9252A5BD46E9573195 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79C3538209F8065DCCFBE205 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework */; }; - 8E29022B4DCBF0EFF9CF82F9 /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E25DB0256669F3B7EE7C566D /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework */; }; - E61581A5A1063B0E6795157D /* Pods_SDWebImageSwiftUIDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0FCDD95C695D2F914DC9B3B /* Pods_SDWebImageSwiftUIDemo.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 322E0DEA28D3318B0003A55F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 607FACC81AFB9204008FA782 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 320CDC2822FADB44007CF858; + remoteInfo = SDWebImageSwiftUIDemo; + }; + 322E0E0628D331F00003A55F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 607FACC81AFB9204008FA782 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 32E529082348A0C700EA46FF; + remoteInfo = "SDWebImageSwiftUIDemo-macOS"; + }; + 322E0E1328D332050003A55F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 607FACC81AFB9204008FA782 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 32E5291F2348A0D300EA46FF; + remoteInfo = "SDWebImageSwiftUIDemo-tvOS"; + }; 32E529392348A0DD00EA46FF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 607FACC81AFB9204008FA782 /* Project object */; @@ -94,7 +154,22 @@ 320CDC3422FADB45007CF858 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 320CDC3722FADB45007CF858 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 320CDC3922FADB45007CF858 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 322E0DE628D3318B0003A55F /* SDWebImageSwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SDWebImageSwiftUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 322E0DF128D331A20003A55F /* WebImageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebImageTests.swift; sourceTree = ""; }; + 322E0DF228D331A20003A55F /* Images.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Images.bundle; sourceTree = ""; }; + 322E0DF328D331A20003A55F /* ImageManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageManagerTests.swift; sourceTree = ""; }; + 322E0DF428D331A20003A55F /* AnimatedImageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedImageTests.swift; sourceTree = ""; }; + 322E0DF528D331A20003A55F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 322E0DF628D331A20003A55F /* TestUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = ""; }; + 322E0E0228D331F00003A55F /* SDWebImageSwiftUITests macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SDWebImageSwiftUITests macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 322E0E0F28D332050003A55F /* SDWebImageSwiftUITests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SDWebImageSwiftUITests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 326B0D702345C01900D28269 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = ""; }; + 3294617E2AA36761009E391B /* SDWebImageSwiftUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SDWebImageSwiftUI; path = ..; sourceTree = ""; }; + 32D5D1602A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDWebImageSwiftUIDemo-visionOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 32D5D1662A445B260098BDFC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; wrapsLines = 0; }; + 32D5D16A2A445B260098BDFC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 32D5D16D2A445B260098BDFC /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 32D5D17F2A4463170098BDFC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 32E529092348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDWebImageSwiftUIDemo-macOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 32E5290B2348A0C700EA46FF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 32E5290F2348A0C900EA46FF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -120,19 +195,7 @@ 32E529542348A0DF00EA46FF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 32E529562348A0DF00EA46FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3E9F8B5F06960FFFBD1A5F99 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; - 473D7886C23B6FC5AFE35842 /* Pods_SDWebImageSwiftUIDemo_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54859B427E0A79E823713963 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; - 5864FFEDE62A0630EDF26A56 /* Pods-SDWebImageSwiftUIDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo.release.xcconfig"; path = "Target Support Files/Pods-SDWebImageSwiftUIDemo/Pods-SDWebImageSwiftUIDemo.release.xcconfig"; sourceTree = ""; }; - 746AF60263F54FD7E16AA7D5 /* Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-SDWebImageSwiftUIDemo-tvOS/Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig"; sourceTree = ""; }; - 79C3538209F8065DCCFBE205 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7B0D9182CAD02B73E6F208F3 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.release.xcconfig"; path = "Target Support Files/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.release.xcconfig"; sourceTree = ""; }; - 89B11BBDBAA86F760DF1EE2D /* Pods-SDWebImageSwiftUIDemo-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-macOS.debug.xcconfig"; path = "Target Support Files/Pods-SDWebImageSwiftUIDemo-macOS/Pods-SDWebImageSwiftUIDemo-macOS.debug.xcconfig"; sourceTree = ""; }; - 95C9E0D9CE4113E5A82480B9 /* Pods-SDWebImageSwiftUIDemo-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-tvOS.release.xcconfig"; path = "Target Support Files/Pods-SDWebImageSwiftUIDemo-tvOS/Pods-SDWebImageSwiftUIDemo-tvOS.release.xcconfig"; sourceTree = ""; }; - A78BA7FB5AFF53CBDD4C4CBD /* Pods-SDWebImageSwiftUIDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo.debug.xcconfig"; path = "Target Support Files/Pods-SDWebImageSwiftUIDemo/Pods-SDWebImageSwiftUIDemo.debug.xcconfig"; sourceTree = ""; }; - E25DB0256669F3B7EE7C566D /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F0FCDD95C695D2F914DC9B3B /* Pods_SDWebImageSwiftUIDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F3AACDC116F5598BC39A8573 /* Pods-SDWebImageSwiftUIDemo-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-macOS.release.xcconfig"; path = "Target Support Files/Pods-SDWebImageSwiftUIDemo-macOS/Pods-SDWebImageSwiftUIDemo-macOS.release.xcconfig"; sourceTree = ""; }; - FEED4964309E241D2FD8A544 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.debug.xcconfig"; path = "Target Support Files/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -140,7 +203,45 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E61581A5A1063B0E6795157D /* Pods_SDWebImageSwiftUIDemo.framework in Frameworks */, + 32D5D1762A445C8F0098BDFC /* SDWebImageSwiftUI in Frameworks */, + 32ABE4D92AA3753300331406 /* SDWebImageWebPCoder in Frameworks */, + 32ABE4DF2AA3756A00331406 /* SDWebImagePDFCoder in Frameworks */, + 32ABE4DC2AA3755D00331406 /* SDWebImageSVGCoder in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 322E0DE328D3318B0003A55F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 32DAC2392AA3784800A085AE /* ViewInspector in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 322E0DFF28D331F00003A55F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 32DAC23B2AA3784D00A085AE /* ViewInspector in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 322E0E0C28D332050003A55F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 32DAC23D2AA3785100A085AE /* ViewInspector in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 32D5D15D2A445B250098BDFC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 32B13E812AA368B700BE9B5B /* SDWebImageSwiftUI in Frameworks */, + 32ABE4F32AA3759900331406 /* SDWebImageWebPCoder in Frameworks */, + 32ABE4F72AA3759900331406 /* SDWebImagePDFCoder in Frameworks */, + 32ABE4F52AA3759900331406 /* SDWebImageSVGCoder in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -148,7 +249,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1794840F9DF6D50ADA448C9B /* Pods_SDWebImageSwiftUIDemo_macOS.framework in Frameworks */, + 32B13E852AA368C600BE9B5B /* SDWebImageSwiftUI in Frameworks */, + 32ABE4E12AA3757B00331406 /* SDWebImageWebPCoder in Frameworks */, + 32ABE4E52AA3757B00331406 /* SDWebImagePDFCoder in Frameworks */, + 32ABE4E32AA3757B00331406 /* SDWebImageSVGCoder in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -156,7 +260,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 68543C9252A5BD46E9573195 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework in Frameworks */, + 32B13E8F2AA368E100BE9B5B /* SDWebImageSwiftUI in Frameworks */, + 32ABE4E72AA3758400331406 /* SDWebImageWebPCoder in Frameworks */, + 32ABE4EB2AA3758400331406 /* SDWebImagePDFCoder in Frameworks */, + 32ABE4E92AA3758400331406 /* SDWebImageSVGCoder in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -164,7 +271,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8E29022B4DCBF0EFF9CF82F9 /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework in Frameworks */, + 32ABE5032AA375B400331406 /* SDWebImageSwiftUI in Frameworks */, + 32ABE4FD2AA375A500331406 /* SDWebImageWebPCoder in Frameworks */, + 32ABE5012AA375A500331406 /* SDWebImagePDFCoder in Frameworks */, + 32ABE4FF2AA375A500331406 /* SDWebImageSVGCoder in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -185,21 +295,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 1DEE67F18F512F2F2F78E283 /* Pods */ = { - isa = PBXGroup; - children = ( - A78BA7FB5AFF53CBDD4C4CBD /* Pods-SDWebImageSwiftUIDemo.debug.xcconfig */, - 5864FFEDE62A0630EDF26A56 /* Pods-SDWebImageSwiftUIDemo.release.xcconfig */, - 89B11BBDBAA86F760DF1EE2D /* Pods-SDWebImageSwiftUIDemo-macOS.debug.xcconfig */, - F3AACDC116F5598BC39A8573 /* Pods-SDWebImageSwiftUIDemo-macOS.release.xcconfig */, - 746AF60263F54FD7E16AA7D5 /* Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig */, - 95C9E0D9CE4113E5A82480B9 /* Pods-SDWebImageSwiftUIDemo-tvOS.release.xcconfig */, - FEED4964309E241D2FD8A544 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.debug.xcconfig */, - 7B0D9182CAD02B73E6F208F3 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; 320CDC2A22FADB44007CF858 /* SDWebImageSwiftUIDemo */ = { isa = PBXGroup; children = ( @@ -223,6 +318,46 @@ path = "Preview Content"; sourceTree = ""; }; + 322E0DEF28D331A20003A55F /* Tests */ = { + isa = PBXGroup; + children = ( + 322E0DF128D331A20003A55F /* WebImageTests.swift */, + 322E0DF228D331A20003A55F /* Images.bundle */, + 322E0DF328D331A20003A55F /* ImageManagerTests.swift */, + 322E0DF428D331A20003A55F /* AnimatedImageTests.swift */, + 322E0DF528D331A20003A55F /* Info.plist */, + 322E0DF628D331A20003A55F /* TestUtils.swift */, + ); + name = Tests; + path = ../Tests; + sourceTree = ""; + }; + 32D5D1612A445B260098BDFC /* SDWebImageSwiftUIDemo-visionOS */ = { + isa = PBXGroup; + children = ( + 32D5D17F2A4463170098BDFC /* Info.plist */, + 32D5D1662A445B260098BDFC /* AppDelegate.swift */, + 32D5D16A2A445B260098BDFC /* Assets.xcassets */, + 32D5D16C2A445B260098BDFC /* Preview Content */, + ); + path = "SDWebImageSwiftUIDemo-visionOS"; + sourceTree = ""; + }; + 32D5D16C2A445B260098BDFC /* Preview Content */ = { + isa = PBXGroup; + children = ( + 32D5D16D2A445B260098BDFC /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 32D5D17A2A445D220098BDFC /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; 32E5290A2348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS */ = { isa = PBXGroup; children = ( @@ -297,15 +432,17 @@ 607FACC71AFB9204008FA782 = { isa = PBXGroup; children = ( + 3294617E2AA36761009E391B /* SDWebImageSwiftUI */, 607FACF51AFB993E008FA782 /* Podspec Metadata */, 320CDC2A22FADB44007CF858 /* SDWebImageSwiftUIDemo */, 32E5290A2348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS */, 32E529212348A0D300EA46FF /* SDWebImageSwiftUIDemo-tvOS */, 32E5293B2348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit App */, 32E5294A2348A0DE00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit Extension */, + 32D5D1612A445B260098BDFC /* SDWebImageSwiftUIDemo-visionOS */, + 322E0DEF28D331A20003A55F /* Tests */, 607FACD11AFB9204008FA782 /* Products */, - 1DEE67F18F512F2F2F78E283 /* Pods */, - F1EB66AFCE0A1C6D551D02DD /* Frameworks */, + 32D5D17A2A445D220098BDFC /* Frameworks */, ); sourceTree = ""; }; @@ -318,6 +455,10 @@ 32E529342348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS.app */, 32E529372348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit App.app */, 32E529462348A0DE00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit Extension.appex */, + 322E0DE628D3318B0003A55F /* SDWebImageSwiftUITests.xctest */, + 322E0E0228D331F00003A55F /* SDWebImageSwiftUITests macOS.xctest */, + 322E0E0F28D332050003A55F /* SDWebImageSwiftUITests tvOS.xctest */, + 32D5D1602A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS.app */, ); name = Products; sourceTree = ""; @@ -332,17 +473,6 @@ name = "Podspec Metadata"; sourceTree = ""; }; - F1EB66AFCE0A1C6D551D02DD /* Frameworks */ = { - isa = PBXGroup; - children = ( - F0FCDD95C695D2F914DC9B3B /* Pods_SDWebImageSwiftUIDemo.framework */, - 473D7886C23B6FC5AFE35842 /* Pods_SDWebImageSwiftUIDemo_macOS.framework */, - 79C3538209F8065DCCFBE205 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework */, - E25DB0256669F3B7EE7C566D /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -350,36 +480,133 @@ isa = PBXNativeTarget; buildConfigurationList = 320CDC3C22FADB45007CF858 /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo" */; buildPhases = ( - 58D483FABAB44B4EC2E538D0 /* [CP] Check Pods Manifest.lock */, 320CDC2522FADB44007CF858 /* Sources */, 320CDC2622FADB44007CF858 /* Frameworks */, 320CDC2722FADB44007CF858 /* Resources */, - 0B5ABDA8213E875CE5FCC890 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = SDWebImageSwiftUIDemo; + packageProductDependencies = ( + 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */, + 32ABE4D82AA3753300331406 /* SDWebImageWebPCoder */, + 32ABE4DB2AA3755D00331406 /* SDWebImageSVGCoder */, + 32ABE4DE2AA3756A00331406 /* SDWebImagePDFCoder */, + ); productName = SDWebImageSwiftUIDemo; productReference = 320CDC2922FADB44007CF858 /* SDWebImageSwiftUIDemo.app */; productType = "com.apple.product-type.application"; }; + 322E0DE528D3318B0003A55F /* SDWebImageSwiftUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 322E0DEE28D3318B0003A55F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests" */; + buildPhases = ( + 322E0DE228D3318B0003A55F /* Sources */, + 322E0DE328D3318B0003A55F /* Frameworks */, + 322E0DE428D3318B0003A55F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 32DCFE9728D333F1001A17BF /* PBXTargetDependency */, + 322E0DEB28D3318B0003A55F /* PBXTargetDependency */, + ); + name = SDWebImageSwiftUITests; + packageProductDependencies = ( + 32DAC2382AA3784800A085AE /* ViewInspector */, + ); + productName = SDWebImageSwiftUITests; + productReference = 322E0DE628D3318B0003A55F /* SDWebImageSwiftUITests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 322E0E0128D331F00003A55F /* SDWebImageSwiftUITests macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 322E0E0828D331F00003A55F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests macOS" */; + buildPhases = ( + 322E0DFE28D331F00003A55F /* Sources */, + 322E0DFF28D331F00003A55F /* Frameworks */, + 322E0E0028D331F00003A55F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 327B90EE28DC4EAA003E8BD9 /* PBXTargetDependency */, + 322E0E0728D331F00003A55F /* PBXTargetDependency */, + ); + name = "SDWebImageSwiftUITests macOS"; + packageProductDependencies = ( + 32DAC23A2AA3784D00A085AE /* ViewInspector */, + ); + productName = "SDWebImageSwiftUITests macOS"; + productReference = 322E0E0228D331F00003A55F /* SDWebImageSwiftUITests macOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 322E0E0E28D332050003A55F /* SDWebImageSwiftUITests tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 322E0E1528D332050003A55F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests tvOS" */; + buildPhases = ( + 322E0E0B28D332050003A55F /* Sources */, + 322E0E0C28D332050003A55F /* Frameworks */, + 322E0E0D28D332050003A55F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 327B90F028DC4EAE003E8BD9 /* PBXTargetDependency */, + 322E0E1428D332050003A55F /* PBXTargetDependency */, + ); + name = "SDWebImageSwiftUITests tvOS"; + packageProductDependencies = ( + 32DAC23C2AA3785100A085AE /* ViewInspector */, + ); + productName = "SDWebImageSwiftUITests tvOS"; + productReference = 322E0E0F28D332050003A55F /* SDWebImageSwiftUITests tvOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 32D5D15F2A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 32D5D1712A445B260098BDFC /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-visionOS" */; + buildPhases = ( + 32D5D15C2A445B250098BDFC /* Sources */, + 32D5D15D2A445B250098BDFC /* Frameworks */, + 32D5D15E2A445B250098BDFC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SDWebImageSwiftUIDemo-visionOS"; + packageProductDependencies = ( + 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */, + 32ABE4F22AA3759900331406 /* SDWebImageWebPCoder */, + 32ABE4F42AA3759900331406 /* SDWebImageSVGCoder */, + 32ABE4F62AA3759900331406 /* SDWebImagePDFCoder */, + ); + productName = "SDWebImageSwiftUIDemo-visionOS"; + productReference = 32D5D1602A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS.app */; + productType = "com.apple.product-type.application"; + }; 32E529082348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS */ = { isa = PBXNativeTarget; buildConfigurationList = 32E5291B2348A0C900EA46FF /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-macOS" */; buildPhases = ( - 78426DFA5212E5496802AC58 /* [CP] Check Pods Manifest.lock */, 32E529052348A0C700EA46FF /* Sources */, 32E529062348A0C700EA46FF /* Frameworks */, 32E529072348A0C700EA46FF /* Resources */, - 4577A2F4A5DEBBDBB766F1CF /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "SDWebImageSwiftUIDemo-macOS"; + packageProductDependencies = ( + 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */, + 32ABE4E02AA3757B00331406 /* SDWebImageWebPCoder */, + 32ABE4E22AA3757B00331406 /* SDWebImageSVGCoder */, + 32ABE4E42AA3757B00331406 /* SDWebImagePDFCoder */, + ); productName = "SDWebImageSwiftUIDemo-macOS"; productReference = 32E529092348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS.app */; productType = "com.apple.product-type.application"; @@ -388,17 +615,21 @@ isa = PBXNativeTarget; buildConfigurationList = 32E5292F2348A0D400EA46FF /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-tvOS" */; buildPhases = ( - FB46C9F77AA45C7DA1D71F7B /* [CP] Check Pods Manifest.lock */, 32E5291C2348A0D300EA46FF /* Sources */, 32E5291D2348A0D300EA46FF /* Frameworks */, 32E5291E2348A0D300EA46FF /* Resources */, - A6F5B1BDEE1460B7F20E55C6 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "SDWebImageSwiftUIDemo-tvOS"; + packageProductDependencies = ( + 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */, + 32ABE4E62AA3758400331406 /* SDWebImageWebPCoder */, + 32ABE4E82AA3758400331406 /* SDWebImageSVGCoder */, + 32ABE4EA2AA3758400331406 /* SDWebImagePDFCoder */, + ); productName = "SDWebImageSwiftUIDemo-tvOS"; productReference = 32E529202348A0D300EA46FF /* SDWebImageSwiftUIDemo-tvOS.app */; productType = "com.apple.product-type.application"; @@ -435,6 +666,8 @@ 32E529492348A0DE00EA46FF /* PBXTargetDependency */, ); name = "SDWebImageSwiftUIDemo-watchOS WatchKit App"; + packageProductDependencies = ( + ); productName = "SDWebImageSwiftUIDemo-watchOS WatchKit App"; productReference = 32E529372348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit App.app */; productType = "com.apple.product-type.application.watchapp2"; @@ -443,17 +676,21 @@ isa = PBXNativeTarget; buildConfigurationList = 32E529572348A0DF00EA46FF /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-watchOS WatchKit Extension" */; buildPhases = ( - B9B631F1DB90E98FCAE3E196 /* [CP] Check Pods Manifest.lock */, 32E529422348A0DE00EA46FF /* Sources */, 32E529432348A0DE00EA46FF /* Frameworks */, 32E529442348A0DE00EA46FF /* Resources */, - 756F0513F095D448FCCD78A2 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "SDWebImageSwiftUIDemo-watchOS WatchKit Extension"; + packageProductDependencies = ( + 32ABE4FC2AA375A500331406 /* SDWebImageWebPCoder */, + 32ABE4FE2AA375A500331406 /* SDWebImageSVGCoder */, + 32ABE5002AA375A500331406 /* SDWebImagePDFCoder */, + 32ABE5022AA375B400331406 /* SDWebImageSwiftUI */, + ); productName = "SDWebImageSwiftUIDemo-watchOS WatchKit Extension"; productReference = 32E529462348A0DE00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit Extension.appex */; productType = "com.apple.product-type.watchkit2-extension"; @@ -464,13 +701,31 @@ 607FACC81AFB9204008FA782 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1100; + LastSwiftUpdateCheck = 1500; LastUpgradeCheck = 0830; ORGANIZATIONNAME = CocoaPods; TargetAttributes = { 320CDC2822FADB44007CF858 = { CreatedOnToolsVersion = 11.0; }; + 322E0DE528D3318B0003A55F = { + CreatedOnToolsVersion = 13.4.1; + ProvisioningStyle = Automatic; + TestTargetID = 320CDC2822FADB44007CF858; + }; + 322E0E0128D331F00003A55F = { + CreatedOnToolsVersion = 13.4.1; + ProvisioningStyle = Automatic; + TestTargetID = 32E529082348A0C700EA46FF; + }; + 322E0E0E28D332050003A55F = { + CreatedOnToolsVersion = 13.4.1; + ProvisioningStyle = Automatic; + TestTargetID = 32E5291F2348A0D300EA46FF; + }; + 32D5D15F2A445B250098BDFC = { + CreatedOnToolsVersion = 15.0; + }; 32E529082348A0C700EA46FF = { CreatedOnToolsVersion = 11.0; ProvisioningStyle = Automatic; @@ -503,6 +758,13 @@ Base, ); mainGroup = 607FACC71AFB9204008FA782; + packageReferences = ( + 3294617A2AA36734009E391B /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */, + 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */, + 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */, + 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */, + 32B13EB12AA377F600BE9B5B /* XCRemoteSwiftPackageReference "ViewInspector" */, + ); productRefGroup = 607FACD11AFB9204008FA782 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -513,6 +775,10 @@ 32E529332348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS */, 32E529362348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit App */, 32E529452348A0DE00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit Extension */, + 32D5D15F2A445B250098BDFC /* SDWebImageSwiftUIDemo-visionOS */, + 322E0DE528D3318B0003A55F /* SDWebImageSwiftUITests */, + 322E0E0128D331F00003A55F /* SDWebImageSwiftUITests macOS */, + 322E0E0E28D332050003A55F /* SDWebImageSwiftUITests tvOS */, ); }; /* End PBXProject section */ @@ -528,265 +794,138 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 32E529072348A0C700EA46FF /* Resources */ = { + 322E0DE428D3318B0003A55F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32E529162348A0C900EA46FF /* Main.storyboard in Resources */, - 32E529132348A0C900EA46FF /* Preview Assets.xcassets in Resources */, - 32E529102348A0C900EA46FF /* Assets.xcassets in Resources */, + 322E0DF928D331A20003A55F /* Images.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 32E5291E2348A0D300EA46FF /* Resources */ = { + 322E0E0028D331F00003A55F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32E5292D2348A0D400EA46FF /* LaunchScreen.storyboard in Resources */, - 32E5292A2348A0D400EA46FF /* Preview Assets.xcassets in Resources */, - 32E529272348A0D400EA46FF /* Assets.xcassets in Resources */, + 322E0E2228D332130003A55F /* Images.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 32E529322348A0DD00EA46FF /* Resources */ = { + 322E0E0D28D332050003A55F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 322E0E2328D332130003A55F /* Images.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 32E529352348A0DD00EA46FF /* Resources */ = { + 32D5D15E2A445B250098BDFC /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32E529402348A0DE00EA46FF /* Assets.xcassets in Resources */, - 32E5293E2348A0DD00EA46FF /* Interface.storyboard in Resources */, + 32D5D16E2A445B260098BDFC /* Preview Assets.xcassets in Resources */, + 32D5D16B2A445B260098BDFC /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 32E529442348A0DE00EA46FF /* Resources */ = { + 32E529072348A0C700EA46FF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 32E529552348A0DF00EA46FF /* Preview Assets.xcassets in Resources */, - 32E529522348A0DF00EA46FF /* Assets.xcassets in Resources */, + 32E529162348A0C900EA46FF /* Main.storyboard in Resources */, + 32E529132348A0C900EA46FF /* Preview Assets.xcassets in Resources */, + 32E529102348A0C900EA46FF /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 0B5ABDA8213E875CE5FCC890 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; + 32E5291E2348A0D300EA46FF /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo/Pods-SDWebImageSwiftUIDemo-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-iOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImagePDFCoder-iOS/SDWebImagePDFCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSVGCoder-iOS/SDWebImageSVGCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-iOS/SDWebImageSwiftUI.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder-iOS/SDWebImageWebPCoder.framework", - "${BUILT_PRODUCTS_DIR}/libwebp-iOS/libwebp.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImagePDFCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSVGCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", + 32E5292D2348A0D400EA46FF /* LaunchScreen.storyboard in Resources */, + 32E5292A2348A0D400EA46FF /* Preview Assets.xcassets in Resources */, + 32E529272348A0D400EA46FF /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo/Pods-SDWebImageSwiftUIDemo-frameworks.sh\"\n"; - showEnvVarsInLog = 0; }; - 4577A2F4A5DEBBDBB766F1CF /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; + 32E529322348A0DD00EA46FF /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-macOS/Pods-SDWebImageSwiftUIDemo-macOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-macOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImagePDFCoder-macOS/SDWebImagePDFCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSVGCoder-macOS/SDWebImageSVGCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-macOS/SDWebImageSwiftUI.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder-macOS/SDWebImageWebPCoder.framework", - "${BUILT_PRODUCTS_DIR}/libwebp-macOS/libwebp.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImagePDFCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSVGCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", - ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-macOS/Pods-SDWebImageSwiftUIDemo-macOS-frameworks.sh\"\n"; - showEnvVarsInLog = 0; }; - 58D483FABAB44B4EC2E538D0 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; + 32E529352348A0DD00EA46FF /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUIDemo-checkManifestLockResult.txt", + 32E529402348A0DE00EA46FF /* Assets.xcassets in Resources */, + 32E5293E2348A0DD00EA46FF /* Interface.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; }; - 756F0513F095D448FCCD78A2 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; + 32E529442348A0DE00EA46FF /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-watchOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImagePDFCoder-watchOS/SDWebImagePDFCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSVGCoder-watchOS/SDWebImageSVGCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-watchOS/SDWebImageSwiftUI.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder-watchOS/SDWebImageWebPCoder.framework", - "${BUILT_PRODUCTS_DIR}/libwebp-watchOS/libwebp.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImagePDFCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSVGCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", + 32E529552348A0DF00EA46FF /* Preview Assets.xcassets in Resources */, + 32E529522348A0DF00EA46FF /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension-frameworks.sh\"\n"; - showEnvVarsInLog = 0; }; - 78426DFA5212E5496802AC58 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 320CDC2522FADB44007CF858 /* Sources */ = { + isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUIDemo-macOS-checkManifestLockResult.txt", + 320CDC2C22FADB44007CF858 /* AppDelegate.swift in Sources */, + 326B0D712345C01900D28269 /* DetailView.swift in Sources */, + 320CDC2E22FADB44007CF858 /* SceneDelegate.swift in Sources */, + 320CDC3022FADB44007CF858 /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; }; - A6F5B1BDEE1460B7F20E55C6 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; + 322E0DE228D3318B0003A55F /* Sources */ = { + isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-tvOS/Pods-SDWebImageSwiftUIDemo-tvOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SDWebImage-tvOS/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImagePDFCoder-tvOS/SDWebImagePDFCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSVGCoder-tvOS/SDWebImageSVGCoder.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageSwiftUI-tvOS/SDWebImageSwiftUI.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder-tvOS/SDWebImageWebPCoder.framework", - "${BUILT_PRODUCTS_DIR}/libwebp-tvOS/libwebp.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImagePDFCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSVGCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageSwiftUI.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageWebPCoder.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", + 322E0DF828D331A20003A55F /* WebImageTests.swift in Sources */, + 322E0DFD28D331A20003A55F /* TestUtils.swift in Sources */, + 322E0DFB28D331A20003A55F /* AnimatedImageTests.swift in Sources */, + 322E0DFA28D331A20003A55F /* ImageManagerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageSwiftUIDemo-tvOS/Pods-SDWebImageSwiftUIDemo-tvOS-frameworks.sh\"\n"; - showEnvVarsInLog = 0; }; - B9B631F1DB90E98FCAE3E196 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; + 322E0DFE28D331F00003A55F /* Sources */ = { + isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension-checkManifestLockResult.txt", + 322E0E1B28D3320D0003A55F /* AnimatedImageTests.swift in Sources */, + 322E0E1A28D3320D0003A55F /* WebImageTests.swift in Sources */, + 322E0E1828D3320D0003A55F /* ImageManagerTests.swift in Sources */, + 322E0E1C28D3320D0003A55F /* TestUtils.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; }; - FB46C9F77AA45C7DA1D71F7B /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; + 322E0E0B28D332050003A55F /* Sources */ = { + isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SDWebImageSwiftUIDemo-tvOS-checkManifestLockResult.txt", + 322E0E2028D3320D0003A55F /* AnimatedImageTests.swift in Sources */, + 322E0E1F28D3320D0003A55F /* WebImageTests.swift in Sources */, + 322E0E1D28D3320D0003A55F /* ImageManagerTests.swift in Sources */, + 322E0E2128D3320D0003A55F /* TestUtils.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 320CDC2522FADB44007CF858 /* Sources */ = { + 32D5D15C2A445B250098BDFC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 320CDC2C22FADB44007CF858 /* AppDelegate.swift in Sources */, - 326B0D712345C01900D28269 /* DetailView.swift in Sources */, - 320CDC2E22FADB44007CF858 /* SceneDelegate.swift in Sources */, - 320CDC3022FADB44007CF858 /* ContentView.swift in Sources */, + 32D5D1672A445B260098BDFC /* AppDelegate.swift in Sources */, + 32D5D1722A445BF00098BDFC /* DetailView.swift in Sources */, + 32D5D1732A445BF00098BDFC /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -824,6 +963,33 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 322E0DEB28D3318B0003A55F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 320CDC2822FADB44007CF858 /* SDWebImageSwiftUIDemo */; + targetProxy = 322E0DEA28D3318B0003A55F /* PBXContainerItemProxy */; + }; + 322E0E0728D331F00003A55F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 32E529082348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS */; + targetProxy = 322E0E0628D331F00003A55F /* PBXContainerItemProxy */; + }; + 322E0E1428D332050003A55F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 32E5291F2348A0D300EA46FF /* SDWebImageSwiftUIDemo-tvOS */; + targetProxy = 322E0E1328D332050003A55F /* PBXContainerItemProxy */; + }; + 327B90EE28DC4EAA003E8BD9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = 327B90ED28DC4EAA003E8BD9 /* ViewInspector */; + }; + 327B90F028DC4EAE003E8BD9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = 327B90EF28DC4EAE003E8BD9 /* ViewInspector */; + }; + 32DCFE9728D333F1001A17BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = 32DCFE9628D333F1001A17BF /* ViewInspector */; + }; 32E5293A2348A0DD00EA46FF /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 32E529362348A0DD00EA46FF /* SDWebImageSwiftUIDemo-watchOS WatchKit App */; @@ -874,7 +1040,6 @@ /* Begin XCBuildConfiguration section */ 320CDC3A22FADB45007CF858 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A78BA7FB5AFF53CBDD4C4CBD /* Pods-SDWebImageSwiftUIDemo.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -892,8 +1057,11 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = SDWebImageSwiftUIDemo/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.dreampiggy.SDWebImageSwiftUIDemo; @@ -909,7 +1077,6 @@ }; 320CDC3B22FADB45007CF858 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5864FFEDE62A0630EDF26A56 /* Pods-SDWebImageSwiftUIDemo.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -926,8 +1093,11 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = SDWebImageSwiftUIDemo/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.dreampiggy.SDWebImageSwiftUIDemo; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -939,9 +1109,274 @@ }; name = Release; }; + 322E0DEC28D3318B0003A55F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.SDWebImageSwiftUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDWebImageSwiftUIDemo.app/SDWebImageSwiftUIDemo"; + }; + name = Debug; + }; + 322E0DED28D3318B0003A55F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.SDWebImageSwiftUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDWebImageSwiftUIDemo.app/SDWebImageSwiftUIDemo"; + }; + name = Release; + }; + 322E0E0928D331F00003A55F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.SDWebImageSwiftUITests-macOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDWebImageSwiftUIDemo-macOS.app/Contents/MacOS/SDWebImageSwiftUIDemo-macOS"; + }; + name = Debug; + }; + 322E0E0A28D331F00003A55F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.SDWebImageSwiftUITests-macOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDWebImageSwiftUIDemo-macOS.app/Contents/MacOS/SDWebImageSwiftUIDemo-macOS"; + }; + name = Release; + }; + 322E0E1628D332050003A55F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.SDWebImageSwiftUITests-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDWebImageSwiftUIDemo-tvOS.app/SDWebImageSwiftUIDemo-tvOS"; + TVOS_DEPLOYMENT_TARGET = 14.0; + }; + name = Debug; + }; + 322E0E1728D332050003A55F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.SDWebImageSwiftUITests-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SDWebImageSwiftUIDemo-tvOS.app/SDWebImageSwiftUIDemo-tvOS"; + TVOS_DEPLOYMENT_TARGET = 14.0; + }; + name = Release; + }; + 32D5D16F2A445B260098BDFC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_ASSET_PATHS = "\"SDWebImageSwiftUIDemo-visionOS/Preview Content\""; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "SDWebImageSwiftUIDemo-visionOS/Info.plist"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.SDWebImageSwiftUIDemo-visionOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = xros; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Debug; + }; + 32D5D1702A445B260098BDFC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"SDWebImageSwiftUIDemo-visionOS/Preview Content\""; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "SDWebImageSwiftUIDemo-visionOS/Info.plist"; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.SDWebImageSwiftUIDemo-visionOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = xros; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Release; + }; 32E529192348A0C900EA46FF /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 89B11BBDBAA86F760DF1EE2D /* Pods-SDWebImageSwiftUIDemo-macOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -960,8 +1395,11 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "SDWebImageSwiftUIDemo-macOS/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.15; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUIDemo-macOS"; @@ -974,7 +1412,6 @@ }; 32E5291A2348A0C900EA46FF /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F3AACDC116F5598BC39A8573 /* Pods-SDWebImageSwiftUIDemo-macOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -992,8 +1429,11 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "SDWebImageSwiftUIDemo-macOS/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.15; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUIDemo-macOS"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1004,7 +1444,6 @@ }; 32E529302348A0D400EA46FF /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 746AF60263F54FD7E16AA7D5 /* Pods-SDWebImageSwiftUIDemo-tvOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; CLANG_ANALYZER_NONNULL = YES; @@ -1021,7 +1460,10 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "SDWebImageSwiftUIDemo-tvOS/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUIDemo-tvOS"; @@ -1030,13 +1472,12 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 14.0; }; name = Debug; }; 32E529312348A0D400EA46FF /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 95C9E0D9CE4113E5A82480B9 /* Pods-SDWebImageSwiftUIDemo-tvOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; CLANG_ANALYZER_NONNULL = YES; @@ -1052,20 +1493,22 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "SDWebImageSwiftUIDemo-tvOS/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUIDemo-tvOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 14.0; }; name = Release; }; 32E529582348A0DF00EA46FF /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FEED4964309E241D2FD8A544 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CLANG_ANALYZER_NONNULL = YES; @@ -1082,7 +1525,11 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "SDWebImageSwiftUIDemo-watchOS WatchKit Extension/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUIDemo-watchOS.watchkitapp.watchkitextension"; @@ -1098,7 +1545,6 @@ }; 32E529592348A0DF00EA46FF /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7B0D9182CAD02B73E6F208F3 /* Pods-SDWebImageSwiftUIDemo-watchOS WatchKit Extension.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; CLANG_ANALYZER_NONNULL = YES; @@ -1114,7 +1560,11 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "SDWebImageSwiftUIDemo-watchOS WatchKit Extension/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUIDemo-watchOS.watchkitapp.watchkitextension"; PRODUCT_NAME = "${TARGET_NAME}"; @@ -1326,7 +1776,8 @@ GCC_WARN_UNUSED_VARIABLE = YES; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; @@ -1343,6 +1794,42 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 322E0DEE28D3318B0003A55F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 322E0DEC28D3318B0003A55F /* Debug */, + 322E0DED28D3318B0003A55F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 322E0E0828D331F00003A55F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 322E0E0928D331F00003A55F /* Debug */, + 322E0E0A28D331F00003A55F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 322E0E1528D332050003A55F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 322E0E1628D332050003A55F /* Debug */, + 322E0E1728D332050003A55F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 32D5D1712A445B260098BDFC /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-visionOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 32D5D16F2A445B260098BDFC /* Debug */, + 32D5D1702A445B260098BDFC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 32E5291B2348A0C900EA46FF /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUIDemo-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1398,6 +1885,185 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 3294617A2AA36734009E391B /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SDWebImage/SDWebImageSwiftUI.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.2.3; + }; + }; + 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SDWebImage/SDWebImageWebPCoder.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.9.1; + }; + }; + 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SDWebImage/SDWebImageSVGCoder"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; + 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SDWebImage/SDWebImagePDFCoder"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; + 32B13EB12AA377F600BE9B5B /* XCRemoteSwiftPackageReference "ViewInspector" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/nalexn/ViewInspector.git"; + requirement = { + kind = exactVersion; + version = 0.9.2; + }; + }; + 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/nalexn/ViewInspector.git"; + requirement = { + kind = exactVersion; + version = 0.9.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 327B90ED28DC4EAA003E8BD9 /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 327B90EF28DC4EAE003E8BD9 /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 32ABE4D82AA3753300331406 /* SDWebImageWebPCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; + productName = SDWebImageWebPCoder; + }; + 32ABE4DB2AA3755D00331406 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; + 32ABE4DE2AA3756A00331406 /* SDWebImagePDFCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; + productName = SDWebImagePDFCoder; + }; + 32ABE4E02AA3757B00331406 /* SDWebImageWebPCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; + productName = SDWebImageWebPCoder; + }; + 32ABE4E22AA3757B00331406 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; + 32ABE4E42AA3757B00331406 /* SDWebImagePDFCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; + productName = SDWebImagePDFCoder; + }; + 32ABE4E62AA3758400331406 /* SDWebImageWebPCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; + productName = SDWebImageWebPCoder; + }; + 32ABE4E82AA3758400331406 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; + 32ABE4EA2AA3758400331406 /* SDWebImagePDFCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; + productName = SDWebImagePDFCoder; + }; + 32ABE4F22AA3759900331406 /* SDWebImageWebPCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; + productName = SDWebImageWebPCoder; + }; + 32ABE4F42AA3759900331406 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; + 32ABE4F62AA3759900331406 /* SDWebImagePDFCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; + productName = SDWebImagePDFCoder; + }; + 32ABE4FC2AA375A500331406 /* SDWebImageWebPCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4D72AA3753300331406 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */; + productName = SDWebImageWebPCoder; + }; + 32ABE4FE2AA375A500331406 /* SDWebImageSVGCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DA2AA3755D00331406 /* XCRemoteSwiftPackageReference "SDWebImageSVGCoder" */; + productName = SDWebImageSVGCoder; + }; + 32ABE5002AA375A500331406 /* SDWebImagePDFCoder */ = { + isa = XCSwiftPackageProductDependency; + package = 32ABE4DD2AA3756A00331406 /* XCRemoteSwiftPackageReference "SDWebImagePDFCoder" */; + productName = SDWebImagePDFCoder; + }; + 32ABE5022AA375B400331406 /* SDWebImageSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImageSwiftUI; + }; + 32B13E802AA368B700BE9B5B /* SDWebImageSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImageSwiftUI; + }; + 32B13E842AA368C600BE9B5B /* SDWebImageSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImageSwiftUI; + }; + 32B13E8E2AA368E100BE9B5B /* SDWebImageSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImageSwiftUI; + }; + 32D5D1752A445C8F0098BDFC /* SDWebImageSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = SDWebImageSwiftUI; + }; + 32DAC2382AA3784800A085AE /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32B13EB12AA377F600BE9B5B /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 32DAC23A2AA3784D00A085AE /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32B13EB12AA377F600BE9B5B /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 32DAC23C2AA3785100A085AE /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32B13EB12AA377F600BE9B5B /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 32DCFE9628D333F1001A17BF /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 32DCFE8D28D333B0001A17BF /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 607FACC81AFB9204008FA782 /* Project object */; } diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 8c78f63a..919434a6 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-macOS.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-macOS.xcscheme index 0ee7f25f..349a278d 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-macOS.xcscheme +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-macOS.xcscheme @@ -28,6 +28,16 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + + + + + LastUpgradeVersion = "1500" + version = "1.7"> @@ -14,9 +14,9 @@ buildForAnalyzing = "YES"> @@ -27,19 +27,7 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> - - - - - - + shouldAutocreateTestPlan = "YES"> + + + + - + - + diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme index 3d1f081f..65df20bc 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo-watchOS WatchKit App.xcscheme @@ -54,10 +54,8 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> - + - + - + - - - - - + diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo.xcscheme index 436cb5ee..59e7aa58 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo.xcscheme +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUIDemo.xcscheme @@ -28,6 +28,16 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + - - - - - - + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -58,15 +41,6 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES"> - - - - diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUITests tvOS.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUITests tvOS.xcscheme new file mode 100644 index 00000000..7438a572 --- /dev/null +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUITests tvOS.xcscheme @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUITests.xcscheme b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUITests.xcscheme new file mode 100644 index 00000000..d947c680 --- /dev/null +++ b/Example/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUITests.xcscheme @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/SDWebImageSwiftUI.xcworkspace/contents.xcworkspacedata b/Example/SDWebImageSwiftUI.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1fcda644..00000000 --- a/Example/SDWebImageSwiftUI.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift b/Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift new file mode 100644 index 00000000..82035803 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/AppDelegate.swift @@ -0,0 +1,46 @@ +/* + * This file is part of the SDWebImage package. + * (c) DreamPiggy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import SwiftUI +import UIKit +import SDWebImage +import SDWebImageWebPCoder +import SDWebImageSVGCoder +import SDWebImagePDFCoder + +// no changes in your AppDelegate class +class AppDelegate: NSObject, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + // Add WebP/SVG/PDF support + SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared) + SDImageCodersManager.shared.addCoder(SDImageSVGCoder.shared) + SDImageCodersManager.shared.addCoder(SDImagePDFCoder.shared) + // Dynamic check to support vector format for both WebImage/AnimatedImage + SDWebImageManager.shared.optionsProcessor = SDWebImageOptionsProcessor { url, options, context in + var options = options + if let _ = context?[.animatedImageClass] as? SDAnimatedImage.Type { + // AnimatedImage supports vector rendering, should not force decode + options.insert(.avoidDecodeImage) + } + return SDWebImageOptionsResult(options: options, context: context) + } + return true + } +} + +@main +struct SDWebImageSwiftUIDemo: App { + // inject into SwiftUI life-cycle via adaptor + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..0c7eecb9 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json new file mode 100644 index 00000000..950af4d8 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.solidimagestacklayer" + }, + { + "filename" : "Middle.solidimagestacklayer" + }, + { + "filename" : "Back.solidimagestacklayer" + } + ] +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..0c7eecb9 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..0c7eecb9 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "reality", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Info.plist b/Example/SDWebImageSwiftUIDemo-visionOS/Info.plist new file mode 100644 index 00000000..6a6654d9 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Info.plist @@ -0,0 +1,11 @@ + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + + diff --git a/Example/SDWebImageSwiftUIDemo-visionOS/Preview Content/Preview Assets.xcassets/Contents.json b/Example/SDWebImageSwiftUIDemo-visionOS/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo-visionOS/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/SDWebImageSwiftUIDemo/AppDelegate.swift b/Example/SDWebImageSwiftUIDemo/AppDelegate.swift index 4b1f1fc3..f08a8c8e 100644 --- a/Example/SDWebImageSwiftUIDemo/AppDelegate.swift +++ b/Example/SDWebImageSwiftUIDemo/AppDelegate.swift @@ -9,6 +9,9 @@ import UIKit import SDWebImage import SDWebImageWebPCoder +#if canImport(SDWebImageAVIFCoder) +import SDWebImageAVIFCoder +#endif import SDWebImageSVGCoder import SDWebImagePDFCoder @@ -21,6 +24,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Override point for customization after application launch. // Add WebP/SVG/PDF support SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared) + #if canImport(SDWebImageAVIFCoder) + SDImageCodersManager.shared.addCoder(SDImageAVIFCoder.shared) + #endif SDImageCodersManager.shared.addCoder(SDImageSVGCoder.shared) SDImageCodersManager.shared.addCoder(SDImagePDFCoder.shared) // Dynamic check to support vector format for both WebImage/AnimatedImage diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index e7855248..17e43e7e 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -17,23 +17,127 @@ class UserSettings: ObservableObject { #endif } -#if os(watchOS) -@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *) -extension Indicator where T == ProgressView { - static var activity: Indicator { - Indicator { isAnimating, progress in - ProgressView() +struct ContentView5: View { + let url: URL = URL(string: "http://assets.sbnation.com/assets/2512203/dogflops.gif")! + + @State private var isAnimating = false + + var body: some View { + ZStack { + WebImage(url: url, isAnimating: $isAnimating) + .pausable(false) + Button { + isAnimating.toggle() + } label: { + Text(isAnimating ? "Stop" : "Start") + } } } - - static var progress: Indicator { - Indicator { isAnimating, progress in - ProgressView(value: progress.wrappedValue) - } +} + +#if !os(watchOS) +struct ContentView4: View { + var url = URL(string: "https://github.com/SDWebImage/SDWebImageSwiftUI/assets/97430818/72d27f90-e9d8-48d7-b144-82ada828a027")! + var body: some View { + AnimatedImage(url: url) + .resizable() + .scaledToFit() +// .aspectRatio(nil, contentMode: .fit) + .clipShape(RoundedRectangle(cornerRadius: 50, style: .continuous)) } } #endif +// Test Switching nil url +struct ContentView3: View { + @State var isOn = false + @State var animated: Bool = false // You can change between WebImage/AnimatedImage + + var url: URL? { + if isOn { + .init(string: "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c1/Google_%22G%22_logo.svg/1024px-Google_%22G%22_logo.svg.png") + } else { + nil + } + } + + var body: some View { + VStack { + Text("\(animated ? "AnimatedImage" : "WebImage")") + Spacer() + #if os(watchOS) + WebImage(url: url) + .resizable() + .scaledToFit() + .frame(width: 100, height: 100) + #else + if animated { + AnimatedImage(url: url) + .resizable() + .scaledToFit() + .frame(width: 100, height: 100) + } else { + WebImage(url: url) + .resizable() + .scaledToFit() + .frame(width: 100, height: 100) + } + #endif + Button("Toggle \(isOn ? "nil" : "valid") URL") { + isOn.toggle() + } + Spacer() + Toggle("Switch", isOn: $animated) + } + } +} + +// Test Switching url using @State +struct ContentView2: View { + @State var imageURLs = [ + "https://raw.githubusercontent.com/recurser/exif-orientation-examples/master/Landscape_1.jpg", + "https://raw.githubusercontent.com/recurser/exif-orientation-examples/master/Landscape_2.jpg", + "http://assets.sbnation.com/assets/2512203/dogflops.gif", + "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif" + ] + @State var animated: Bool = false // You can change between WebImage/AnimatedImage + @State var imageIndex : Int = 0 + var body: some View { + Group { + Text("\(animated ? "AnimatedImage" : "WebImage") - \((imageURLs[imageIndex] as NSString).lastPathComponent)") + Spacer() + #if os(watchOS) + WebImage(url:URL(string: imageURLs[imageIndex])) + .resizable() + .aspectRatio(contentMode: .fit) + #else + if self.animated { + AnimatedImage(url:URL(string: imageURLs[imageIndex])) + .resizable() + .aspectRatio(contentMode: .fit) + } else { + WebImage(url:URL(string: imageURLs[imageIndex])) + .resizable() + .aspectRatio(contentMode: .fit) + } + #endif + Spacer() + Button("Next") { + if imageIndex + 1 >= imageURLs.count { + imageIndex = 0 + } else { + imageIndex += 1 + } + } + Button("Reload") { + SDImageCache.shared.clearMemory() + SDImageCache.shared.clearDisk(onCompletion: nil) + } + Toggle("Switch", isOn: $animated) + } + } +} + struct ContentView: View { @State var imageURLs = [ "http://assets.sbnation.com/assets/2512203/dogflops.gif", @@ -44,9 +148,10 @@ struct ContentView: View { "http://littlesvr.ca/apng/images/SteamEngine.webp", "http://littlesvr.ca/apng/images/world-cup-2014-42.webp", "https://isparta.github.io/compare-webp/image/gif_webp/webp/2.webp", + "https://raw.githubusercontent.com/link-u/avif-sample-images/master/fox.profile0.8bpc.yuv420.avif", + "https://raw.githubusercontent.com/link-u/avif-sample-images/master/star-12bpc-with-alpha.avifs", "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic", "https://nokiatech.github.io/heif/content/image_sequences/starfield_animation.heic", - "https://www.sample-videos.com/img/Sample-png-image-1mb.png", "https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png", "https://raw.githubusercontent.com/ibireme/YYImage/master/Demo/YYImageDemo/mew_baseline.jpg", "https://via.placeholder.com/200x200.jpg", @@ -56,10 +161,68 @@ struct ContentView: View { "https://raw.githubusercontent.com/icons8/flat-color-icons/master/pdf/stack_of_photos.pdf", "https://raw.githubusercontent.com/icons8/flat-color-icons/master/pdf/smartphone_tablet.pdf" ] - @State var animated: Bool = true // You can change between WebImage/AnimatedImage + @State var animated: Bool = false // You can change between WebImage/AnimatedImage @EnvironmentObject var settings: UserSettings + // Used to avoid https://twitter.com/fatbobman/status/1572507700436807683?s=20&t=5rfj6BUza5Jii-ynQatCFA + struct ItemView: View { + @Binding var animated: Bool + @State var url: String + var body: some View { + NavigationLink(destination: DetailView(url: url, animated: self.animated)) { + HStack { + if self.animated { + #if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) + AnimatedImage(url: URL(string:url)) + .onViewUpdate { view, context in + #if os(macOS) + view.toolTip = url + #endif + } + .indicator(.activity) + .transition(.fade) + .resizable() + .scaledToFit() + .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) + #else + WebImage(url: URL(string:url)) + .resizable() + .indicator(.activity) + .transition(.fade(duration: 0.5)) + .scaledToFit() + .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) + #endif + } else { + WebImage(url: URL(string:url)) + .resizable() + .indicator(.activity) + .transition(.fade(duration: 0.5)) + .scaledToFit() + .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) + } + Text((url as NSString).lastPathComponent) + } + } + .buttonStyle(PlainButtonStyle()) + } + } + + var body: some View { + #if os(visionOS) + return NavigationView { + contentView() + .navigationBarTitle(animated ? "AnimatedImage" : "WebImage") + .navigationBarItems(leading: + Button(action: { self.reloadCache() }) { + Text("Reload") + }, trailing: + Button(action: { self.switchView() }) { + Text("Switch") + } + ) + } + #endif #if os(iOS) return NavigationView { contentView() @@ -105,64 +268,24 @@ struct ContentView: View { } #endif #if os(watchOS) - return contentView() - .contextMenu { - Button(action: { self.reloadCache() }) { - Text("Reload") - } - Button(action: { self.switchView() }) { - Text("Switch") + return NavigationView { + contentView() + .navigationTitle("WebImage") + .toolbar { + Button(action: { self.reloadCache() }) { + Text("Reload") + } } - } + + } #endif } func contentView() -> some View { List { ForEach(imageURLs, id: \.self) { url in - NavigationLink(destination: DetailView(url: url, animated: self.animated)) { - HStack { - if self.animated { - #if os(macOS) || os(iOS) || os(tvOS) - AnimatedImage(url: URL(string:url), isAnimating: .constant(true)) - .onViewUpdate { view, context in - #if os(macOS) - view.toolTip = url - #endif - } - .indicator(SDWebImageActivityIndicator.medium) - /** - .placeholder(UIImage(systemName: "photo")) - */ - .transition(.fade) - .resizable() - .scaledToFit() - .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) - #else - WebImage(url: URL(string:url), isAnimating: self.$animated) - .resizable() - .indicator(.activity) - .transition(.fade(duration: 0.5)) - .scaledToFit() - .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) - #endif - } else { - WebImage(url: URL(string:url), isAnimating: .constant(true)) - .resizable() - /** - .placeholder { - Image(systemName: "photo") - } - */ - .indicator(.activity) - .transition(.fade(duration: 0.5)) - .scaledToFit() - .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) - } - Text((url as NSString).lastPathComponent) - } - } - .buttonStyle(PlainButtonStyle()) + // Must use top level view instead of inlined view structure + ItemView(animated: $animated, url: url) } .onDelete { indexSet in indexSet.forEach { index in diff --git a/Example/SDWebImageSwiftUIDemo/DetailView.swift b/Example/SDWebImageSwiftUIDemo/DetailView.swift index f9e7dea0..a56d3511 100644 --- a/Example/SDWebImageSwiftUIDemo/DetailView.swift +++ b/Example/SDWebImageSwiftUIDemo/DetailView.swift @@ -44,7 +44,7 @@ struct DetailView: View { var body: some View { VStack { - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) zoomView() .navigationBarItems(trailing: Button(isAnimating ? "Stop" : "Start") { self.isAnimating.toggle() @@ -52,17 +52,15 @@ struct DetailView: View { #endif #if os(macOS) || os(watchOS) zoomView() - .contextMenu { - Button(isAnimating ? "Stop" : "Start") { - self.isAnimating.toggle() - } + .onTapGesture { + self.isAnimating.toggle() } #endif } } func zoomView() -> some View { - #if os(macOS) || os(iOS) + #if os(macOS) || os(iOS) || os(visionOS) return contentView() .scaleEffect(self.scale) .gesture(MagnificationGesture(minimumScaleDelta: 0.1).onChanged { value in @@ -94,25 +92,32 @@ struct DetailView: View { func contentView() -> some View { HStack { if animated { - #if os(macOS) || os(iOS) || os(tvOS) - AnimatedImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) + #if os(macOS) || os(iOS) || os(tvOS) || os(visionOS) + AnimatedImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating, placeholderImage: .wifiExclamationmark) + .indicator(.progress) .resizable() - .placeholder(.wifiExclamationmark) - .indicator(SDWebImageProgressIndicator.default) .scaledToFit() #else - WebImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) - .resizable() - .placeholder(.wifiExclamationmark) + WebImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) { image in + image.resizable() + .scaledToFit() + } placeholder: { + Image.wifiExclamationmark + .resizable() + .scaledToFit() + } .indicator(.progress) - .scaledToFit() #endif } else { - WebImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) - .resizable() - .placeholder(.wifiExclamationmark) - .indicator(.progress) - .scaledToFit() + WebImage(url: URL(string:url), options: [.progressiveLoad, .delayPlaceholder], isAnimating: $isAnimating) { image in + image.resizable() + .scaledToFit() + } placeholder: { + Image.wifiExclamationmark + .resizable() + .scaledToFit() + } + .indicator(.progress(style: .circular)) } } } diff --git a/Package.resolved b/Package.resolved index 4c1bce37..2c5b6821 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", "state": { "branch": null, - "revision": "e2285181a62daf4d1d3caf66d6d776b667092303", - "version": "5.7.0" + "revision": "b62cb63bf4ed1f04c961a56c9c6c9d5ab8524ec6", + "version": "5.21.1" } } ] diff --git a/Package.swift b/Package.swift index e7c307fc..8c48349f 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.2 +// swift-tools-version:5.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "SDWebImageSwiftUI", platforms: [ - .macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6) + .macOS(.v11), .iOS(.v14), .tvOS(.v14), .watchOS(.v7) ], products: [ // Products define the executables and libraries produced by a package, and make them visible to other packages. @@ -17,7 +17,7 @@ let package = Package( dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), - .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.10.0") + .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.21.1") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -25,7 +25,9 @@ let package = Package( .target( name: "SDWebImageSwiftUI", dependencies: ["SDWebImage"], - path: "SDWebImageSwiftUI/Classes" + path: "SDWebImageSwiftUI", + sources: ["Classes"], + resources: [.copy("Resources/PrivacyInfo.xcprivacy")] ), ] ) diff --git a/Podfile b/Podfile new file mode 100644 index 00000000..e7e7ce47 --- /dev/null +++ b/Podfile @@ -0,0 +1,85 @@ +install! 'cocoapods', :warn_for_unused_master_specs_repo => false +use_frameworks! + +def all_pods + pod 'SDWebImageSwiftUI', :path => './' + pod 'SDWebImageWebPCoder' + pod 'SDWebImageSVGCoder' + pod 'SDWebImagePDFCoder' + pod 'SDWebImageAVIFCoder' + pod 'libavif', :subspecs => ['libdav1d'] +end + +def all_test_pods + pod 'SDWebImageSwiftUI', :path => './' +end + +example_project_path = 'Example/SDWebImageSwiftUI' +test_project_path = 'Example/SDWebImageSwiftUI' +workspace 'SDWebImageSwiftUI.xcworkspace' + +target 'SDWebImageSwiftUIDemo' do + project example_project_path + platform :ios, '14.0' + all_pods +end + +target 'SDWebImageSwiftUIDemo-macOS' do + project example_project_path + platform :osx, '11.0' + all_pods +end + +target 'SDWebImageSwiftUIDemo-tvOS' do + project example_project_path + platform :tvos, '14.0' + all_pods +end + +target 'SDWebImageSwiftUIDemo-watchOS WatchKit Extension' do + project example_project_path + platform :watchos, '7.0' + all_pods +end + +# Test Project +target 'SDWebImageSwiftUITests' do + project test_project_path + platform :ios, '14.0' + all_test_pods +end + +target 'SDWebImageSwiftUITests macOS' do + project test_project_path + platform :osx, '11.0' + all_test_pods +end + +target 'SDWebImageSwiftUITests tvOS' do + project test_project_path + platform :tvos, '14.0' + all_test_pods +end + + +# Inject macro during SDWebImage Demo and Tests +post_install do |installer_representation| + installer_representation.pods_project.targets.each do |target| + if target.product_name == 'SDWebImage' + target.build_configurations.each do |config| + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) SD_CHECK_CGIMAGE_RETAIN_SOURCE=1' + end + elsif target.product_name == 'SDWebImageSwiftUI' + # Do nothing + else + target.build_configurations.each do |config| + # Override the min deployment target for some test specs to workaround `libarclite.a` missing issue + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0' + config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.11' + config.build_settings['TVOS_DEPLOYMENT_TARGET'] = '9.0' + config.build_settings['WATCHOS_DEPLOYMENT_TARGET'] = '2.0' + config.build_settings['XROS_DEPLOYMENT_TARGET'] = '1.0' + end + end + end +end diff --git a/README.md b/README.md index 457c5b72..b092b5c5 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ [![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-brightgreen.svg)](https://swift.org/package-manager/) [![codecov](https://codecov.io/gh/SDWebImage/SDWebImageSwiftUI/branch/master/graph/badge.svg)](https://codecov.io/gh/SDWebImage/SDWebImageSwiftUI) +> If you support iOS 15+/macOS 12+ only and don't care about animated image format, try SwiftUI's [AsyncImage](https://developer.apple.com/documentation/swiftui/asyncimage) + ## What's for SDWebImageSwiftUI is a SwiftUI image loading framework, which based on [SDWebImage](https://github.com/SDWebImage/SDWebImage). @@ -16,6 +18,20 @@ It brings all your favorite features from SDWebImage, like async image loading, The framework provide the different View structs, which API match the SwiftUI framework guideline. If you're familiar with `Image`, you'll find it easy to use `WebImage` and `AnimatedImage`. +## Apple VisionOS + +From v3.0.0, SDWebImageSwiftUI can be compiled for visionOS platform. However, due to the lacking package manager support (need tools update), we don't support CocoaPods/SPM yet. + +You can only use the Xcode's built-in package manager dependency to build on visionOS. + +To run the visionOS example, you need to clone and add both `SDWebImage` and `SDWebImageSwiftUI`, open the `SDWebImageSwiftUI.xcworkspace` and drag those folders to become local package dependency, see: [Editing a package dependency as a local package](https://developer.apple.com/documentation/xcode/editing-a-package-dependency-as-a-local-package) + +If you really want to build framework instead of using Xcode's package dependency, following the manual steps below: + +1. Clone SDWebImage, open `SDWebImage.xcodeproj` and build `SDWebImage` target for visionOS platform (Change `MACH_O_TYPE` to static library if you need) +2. Clone SDWebImageSwiftUI, create directory at `Carthage/Build/visionOS` and copy `SDWebImage.framework` into it +3. Open `SDWebImageSwiftUI.xcodeproj` and build `SDWebImageSwiftUI visionOS` target + ## Features Since SDWebImageSwiftUI is built on top of SDWebImage, it provide both the out-of-box features as well as advanced powerful features you may want in real world Apps. Check our [Wiki](https://github.com/SDWebImage/SDWebImage/wiki/Advanced-Usage) when you need: @@ -48,33 +64,41 @@ All issue reports, feature requests, contributions, and GitHub stars are welcome ## Requirements -+ Xcode 12+ -+ iOS 13+ -+ macOS 10.15+ -+ tvOS 13+ -+ watchOS 6+ -+ Swift 5.2+ ++ Xcode 14+ ++ iOS 14+ ++ macOS 11+ ++ tvOS 14+ ++ watchOS 7+ ++ visionOS 1+ -## SwiftUI 2.0 Compatibility +## for SwiftUI 1.0 (iOS 13) iOS 14(macOS 11) introduce the SwiftUI 2.0, which keep the most API compatible, but changes many internal behaviors, which breaks the SDWebImageSwiftUI's function. -From v2.0.0, we adopt SwiftUI 2.0 and iOS 14(macOS 11)'s behavior. You can use `WebImage` and `AnimatedImage` inside the new `LazyVStack`. +From v3.0.0, SDWebImageSwiftUI drop iOS 13 support. To use on iOS 13, checkout the latest v2.x version (or using [2.x](https://github.com/SDWebImage/SDWebImageSwiftUI/tree/2.x) branch) instead. + +## for future transition + +Since SDWebImage 6.0 will introduce mixed Swift/Objc codebase, this repo will migrate into [SDWebImage Core Repo](https://github.com/SDWebImage/SDWebImage). + +But don't worry, we will use the automatic cross module overlay, whic means, you can use: ```swift -var body: some View { - ScrollView { - LazyVStack { - ForEach(urls, id: \.self) { url in - AnimatedImage(url: url) - } - } - } -} +import SwiftUI +import SDWebImage ``` -Note: However, many differences behavior between iOS 13/14's is hard to fixup. Due to maintain issue, in the future release, we will drop the iOS 13 supports and always match SwiftUI 2.0's behavior. +to works like: +``` +import SwiftUI +import SDWebImage +import SDWebImageSwiftUI // <-- Automatic infer this +``` + +You will automatically link the `SDWebImageSwiftUI`, and this library's naming will still be preserved in SPM target. So the transition is smooth for most of you, I don't want to bump another major version. **The 3.x is the final version for SDWebImageSwiftUI dedicated repo** + +Note: For super advanced user, if you using some custom Swift toolchain, be sure to pass `-Xfrontend -enable-cross-import-overlays` ## Installation @@ -93,7 +117,7 @@ For downstream framework author, you should create a `Package.swift` file into y ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/SDWebImage/SDWebImageSwiftUI.git", from: "2.0.0") + .package(url: "https://github.com/SDWebImage/SDWebImageSwiftUI.git", from: "3.0.0") ], ) ``` @@ -128,18 +152,16 @@ github "SDWebImage/SDWebImageSwiftUI" ```swift var body: some View { - WebImage(url: URL(string: "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic")) + WebImage(url: URL(string: "https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic")) { image in + image.resizable() // Control layout like SwiftUI.AsyncImage, you must use this modifier or the view will use the image bitmap size + } placeholder: { + Rectangle().foregroundColor(.gray) + } // Supports options and context, like `.delayPlaceholder` to show placeholder only when error .onSuccess { image, data, cacheType in // Success // Note: Data exist only when queried from disk cache or network. Use `.queryMemoryData` if you really need data } - .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size - .placeholder(Image(systemName: "photo")) // Placeholder Image - // Supports ViewBuilder as well - .placeholder { - Rectangle().foregroundColor(.gray) - } .indicator(.activity) // Activity Indicator .transition(.fade(duration: 0.5)) // Fade Transition with duration .scaledToFit() @@ -163,7 +185,14 @@ var body: some View { } ``` -Note: For indicator, you can custom your own as well. For example, iOS 14/watchOS 7 introduce the new `ProgressView`, which can replace our built-in `ProgressIndicator/ActivityIndicator` (where watchOS does not provide). +Note: For indicator, you can custom your own as well. For example, iOS 14/watchOS 7 introduce the new `ProgressView`, which can be easily used via: + +```swift +WebImage(url: url) +.indicator(.activity) +``` + +or you can just write like: ```swift WebImage(url: url) @@ -187,21 +216,21 @@ WebImage(url: url) ```swift var body: some View { Group { - AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif")) + AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif"), placeholderImage: .init(systemName: "photo")) // Placeholder Image // Supports options and context, like `.progressiveLoad` for progressive animation loading .onFailure { error in // Error } .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size - .placeholder(UIImage(systemName: "photo")) // Placeholder Image - // Supports ViewBuilder as well - .placeholder { - Circle().foregroundColor(.gray) - } - .indicator(SDWebImageActivityIndicator.medium) // Activity Indicator + .indicator(.activity) // Activity Indicator .transition(.fade) // Fade Transition .scaledToFit() // Attention to call it on AnimatedImage, but not `some View` after View Modifier (Swift Protocol Extension method is static dispatched) + // Supports SwiftUI ViewBuilder placeholder as well + AnimatedImage(url: url) { + Circle().foregroundColor(.gray) + } + // Data AnimatedImage(data: try! Data(contentsOf: URL(fileURLWithPath: "/tmp/foo.webp"))) .customLoopCount(1) // Custom loop count @@ -226,7 +255,7 @@ Note: `AnimatedImage` supports both image url or image data for animated image f Note: `AnimatedImage` some methods like `.transition`, `.indicator` and `.aspectRatio` have the same naming as `SwiftUI.View` protocol methods. But the args receive the different type. This is because `AnimatedImage` supports to be used with UIKit/AppKit component and animation. If you find ambiguity, use full type declaration instead of the dot expression syntax. -Note: some of methods on `AnimatedImage` will return `some View`, a new Modified Content. You'll lose the type related modifier method. For this case, you can either reorder the method call, or use Native View in `.onViewUpdate` for rescue. +Note: some of methods on `AnimatedImage` will return `some View`, a new Modified Content. You'll lose the type related modifier method. For this case, you can either reorder the method call, or use native view (actually `SDAnimatedImageView`) in `.onViewUpdate`, use UIKIt/AppKit API for rescue. ```swift @@ -267,7 +296,7 @@ It looks familiar like `SDWebImageManager`, but it's built for SwiftUI world, wh ```swift struct MyView : View { - @ObservedObject var imageManager: ImageManager + @ObservedObject var imageManager = ImageManager() var body: some View { // Your custom complicated view graph Group { @@ -278,17 +307,11 @@ struct MyView : View { } } // Trigger image loading when appear - .onAppear { self.imageManager.load() } + .onAppear { self.imageManager.load(url: url) } // Cancel image loading when disappear .onDisappear { self.imageManager.cancel() } } } - -struct MyView_Previews: PreviewProvider { - static var previews: some View { - MyView(imageManager: ImageManager(url: URL(string: "https://via.placeholder.com/200x200.jpg")) - } -} ``` ### Customization and configuration setup @@ -303,6 +326,7 @@ The best place to put these setup code for SwiftUI App, it's the `AppDelegate.sw func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Add WebP/SVG/PDF support SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared) + SDImageCodersManager.shared.addCoder(SDImageAVIFCoder.shared) SDImageCodersManager.shared.addCoder(SDImageSVGCoder.shared) SDImageCodersManager.shared.addCoder(SDImagePDFCoder.shared) @@ -327,13 +351,61 @@ For more information, it's really recommended to check our demo, to learn detail ## Documentation -+ [SDWebImageSwiftUI API documentation](https://sdwebimage.github.io/SDWebImageSwiftUI/) ++ [SDWebImageSwiftUI API documentation](https://sdwebimage.github.io/documentation/sdwebimageswiftui/) + [SDWebImage API documentation](https://sdwebimage.github.io/) ## FAQ ### Common Problems +#### Using WebImage/AnimatedImage in List/LazyStack/LazyGrid and ForEach + +SwiftUI has a known behavior(bug?) when using stateful view in `List/LazyStack/LazyGrid`. +Only the **Top Level** view can hold its own `@State/@StateObject`, but the sub structure will lose state when scroll out of screen. +However, WebImage/Animated is both stateful. To ensure the state keep in sync even when scroll out of screen. you may use some tricks. + +See more: https://twitter.com/fatbobman/status/1572507700436807683?s=21&t=z4FkAWTMvjsgL-wKdJGreQ + +In short, it's not recommanded to do so: + +```swift +struct ContentView { + @State var imageURLs: [String] + var body: some View { + List { + ForEach(imageURLs, id: \.self) { url in + VStack { + WebImage(url) // The top level is `VStack` + } + } + } + } +} +``` + +instead, using this approach: + +```swift +struct ContentView { + struct BodyView { + @State var url: String + var body: some View { + VStack { + WebImage(url) + } + } + } + @State var imageURLs: [String] + var body: some View { + List { + ForEach(imageURLs, id: \.self) { url in + BodyView(url: url) + } + } + } +} +``` + #### Using Image/WebImage/AnimatedImage in Button/NavigationLink SwiftUI's `Button` apply overlay to its content (except `Text`) by default, this is common mistake to write code like this, which cause strange behavior: @@ -372,6 +444,42 @@ NavigationView { } ``` +#### Render vector image (SVG/PDF) with tint color + +Both `WebImage/AnimatedImage` supports to render the vector image, by using the `SVG/PDF` external coders. However they are different internally. + ++ `AnimatedImage`: use tech from Apple's symbol image and vector drawing, supports dynamic size changes without lossing details. And it use UIKit/AppKit based implementation and APIs. If you want, pass `.context(.imageThumbnailPixelSize: size)` to use bitmap rendering and get more pixels. ++ `WebImage`: draws vector image into a bitmap version. Which just like normal PNG. By default, we use vector image content size (SVG canvas size or PDF media box size). If you want, pass `.context(.imageThumbnailPixelSize: size)` to get more pixels. + +For bitmap rendering, you can also tint the SVG/PDF icons with custom colors (like symbol images), use the `.renderingMode(.template)` and `.tint(:)` or `.foregroundColor(:)` modifier, which matches `SwiftUI.Image` behavior. + ++ WebImage + +```swift +var body: some View { + WebImage(url: URL(string: "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/w3c.svg")) + .resizable() + .renderingMode(.template) + .foregroundColor(.red) // or `.tint(:)`, `.accentColor(:)` + .scaledToFit() +} +``` + ++ AnimatedImage + +```swift +var body: some View { + AnimatedImage(url: URL(string: "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/w3c.svg"), context: [.imageThumbnailPixelSize : CGSize(width: 100, height: 100)]) + .resizable() + .renderingMode(.template) + // seems `.foregroundColor(:)` does effect `UIView.tintColor`, use `tint(:)` or `.accentColor(:)` instead. + // Or you can use `onViewCreate(:)` to get native `SDAnimatedImageView` and set `tintColor` (AppKit use `contentTintColor`) + .tint(.red) + .scaledToFit() +} +``` + +See more: [Configuring and displaying symbol images in your UI](https://developer.apple.com/documentation/uikit/uiimage/configuring_and_displaying_symbol_images_in_your_ui?language=objc) #### Using with external loaders/caches/coders @@ -394,6 +502,8 @@ struct MyApp: App { SDWebImageManager.defaultImageLoader = SDImageLoadersManager.shared // WebP support SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared) + // AVIF support + SDImageCodersManager.shared.addCoder(SDImageAVIFCoder.shared) } var body: some Scene { @@ -469,7 +579,7 @@ For caches, you actually don't need to worry about anything. It just works after #### Using for backward deployment and weak linking SwiftUI -SDWebImageSwiftUI supports to use when your App Target has a deployment target version less than iOS 13/macOS 10.15/tvOS 13/watchOS 6. Which will weak linking of SwiftUI(Combine) to allows writing code with available check at runtime. +SDWebImageSwiftUI supports to use when your App Target has a deployment target version less than iOS 14/macOS 11/tvOS 14/watchOS 7. Which will weak linking of SwiftUI(Combine) to allows writing code with available check at runtime. To use backward deployment, you have to do the follow things: @@ -483,7 +593,7 @@ You should notice that all the third party SwiftUI frameworks should have this b For deployment target version below iOS 12.2 (The first version which Swift 5 Runtime bundled in iOS system), you have to change the min deployment target version of SDWebImageSwiftUI. This may take some side effect on compiler's optimization and trigger massive warnings for some frameworks. -However, for iOS 12.2+, you can still keep the min deployment target version to iOS 13, no extra warnings or performance slow down for iOS 13 client. +However, for iOS 12.2+, you can still keep the min deployment target version to iOS 14, no extra warnings or performance slow down for iOS 14 client. Because Swift use the min deployment target version to detect whether to link the App bundled Swift runtime, or the System built-in one (`/usr/lib/swift/libswiftCore.dylib`). @@ -510,7 +620,7 @@ end + For CocoaPods user, you can skip the platform version validation in Podfile with: ```ruby -platform :ios, '13.0' # This does not effect your App Target's deployment target version, just a hint for CocoaPods +platform :ios, '14.0' # This does not effect your App Target's deployment target version, just a hint for CocoaPods ``` + For SwiftPM user, SwiftPM does not support weak linking nor Library Evolution, so it can not deployment to iOS 12+ user without changing the min deployment target. @@ -523,7 +633,7 @@ Add **all the SwiftUI code** with the available annotation and runtime check, li // AppDelegate.swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // ... - if #available(iOS 13, *) { + if #available(iOS 14, *) { window.rootViewController = UIHostingController(rootView: ContentView()) } else { window.rootViewController = ViewController() @@ -545,11 +655,11 @@ class ViewController: UIViewController { } // ContentView.swift -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) struct ContentView : View { var body: some View { Group { - Text("Hello World iOS 13!") + Text("Hello World iOS 14!") WebImage(url: URL(string: "https://i.loli.net/2019/09/24/rX2RkVWeGKIuJvc.jpg")) } } @@ -560,12 +670,10 @@ struct ContentView : View { To run the example using SwiftUI, following the steps: -``` -cd Example -pod install -``` +1. Open `SDWebImageSwiftUI.xcworkspace`, wait for SwiftPM finishing downloading the test dependency. +2. Choose `SDWebImageSwiftUIDemo` (or other platforms) scheme and run the demo application. -Then open the Xcode Workspace to run the demo application. +Note: The `Podfile` here is because history we use CocoaPods to integrate libs into Demo, but now we use SPM. Since SwiftUI is aimed to support all Apple platforms, our demo does this as well, one codebase including: @@ -573,11 +681,12 @@ Since SwiftUI is aimed to support all Apple platforms, our demo does this as wel + macOS + tvOS + watchOS ++ visionOS Demo Tips: -1. Use `Switch` (right-click on macOS/force press on watchOS) to switch between `WebImage` and `AnimatedImage`. -2. Use `Reload` (right-click on macOS/force press on watchOS) to clear cache. +1. Use `Switch` (right-click on macOS/tap on watchOS) to switch between `WebImage` and `AnimatedImage`. +2. Use `Reload` (right-click on macOS/button on watchOS) to clear cache. 3. Use `Swipe Left` (menu button on tvOS) to delete one image url from list. 4. Pinch gesture (Digital Crown on watchOS, play button on tvOS) to zoom-in detail page image. 5. Clear cache and go to detail page to see progressive loading. @@ -588,12 +697,12 @@ SDWebImageSwiftUI has Unit Test to increase code quality. For SwiftUI, there are However, since SwiftUI is State-Based and Attributed-Implemented layout system, there are open source projects who provide the solution: -+ [ViewInspector](https://github.com/nalexn/ViewInspector): Inspect View's runtime attribute value (like `.frame` modifier, `.image` value). We use this to test `AnimatedImage` and `WebImage`. It also allows the inspect to native UIView/NSView, which we use to test `ActivityIndicator` and `ProgressIndicator`. ++ [ViewInspector](https://github.com/nalexn/ViewInspector): Inspect View's runtime attribute value (like `.frame` modifier, `.image` value). We use this to test `AnimatedImage` and `WebImage`. It also allows the inspect to native UIView/NSView. To run the test: -1. Run `carthage build` on root directory to install the dependency. -2. Open `SDWebImageSwiftUI.xcodeproj`, wait for SwiftPM finishing downloading the test dependency. +1. Run `pod install` on root directory to install the dependency. +2. Open `SDWebImageSwiftUI.xcworkspace`, wait for SwiftPM finishing downloading the test dependency. 3. Choose `SDWebImageSwiftUITests` scheme and start testing. We've already setup the CI pipeline, each PR will run the test case and upload the test report to [codecov](https://codecov.io/gh/SDWebImage/SDWebImageSwiftUI). @@ -634,11 +743,13 @@ Which means, this project is one core use case and downstream dependency, which - [SDWebImage](https://github.com/SDWebImage/SDWebImage) - [libwebp](https://github.com/SDWebImage/libwebp-Xcode) +- [libavif](https://github.com/SDWebImage/libavif-Xcode) - [Kingfisher](https://github.com/onevcat/Kingfisher) - [SwiftUIX](https://github.com/SwiftUIX/SwiftUIX) - [Espera](https://github.com/JagCesar/Espera) - [SwiftUI-Introspect](https://github.com/siteline/SwiftUI-Introspect) - [ViewInspector](https://github.com/nalexn/ViewInspector) +- [SwiftUIBackports](https://github.com/shaps80/SwiftUIBackports) ## License diff --git a/SDWebImageSwiftUI.podspec b/SDWebImageSwiftUI.podspec index 02f9b80d..67c3835f 100644 --- a/SDWebImageSwiftUI.podspec +++ b/SDWebImageSwiftUI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'SDWebImageSwiftUI' - s.version = '2.0.2' + s.version = '3.1.3' s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage' s.description = <<-DESC @@ -21,19 +21,22 @@ It brings all your favorite features from SDWebImage, like async image loading, s.author = { 'DreamPiggy' => 'lizhuoli1126@126.com' } s.source = { :git => 'https://github.com/SDWebImage/SDWebImageSwiftUI.git', :tag => s.version.to_s } - s.ios.deployment_target = '13.0' - s.osx.deployment_target = '10.15' - s.tvos.deployment_target = '13.0' - s.watchos.deployment_target = '6.0' + s.ios.deployment_target = '14.0' + s.osx.deployment_target = '11.0' + s.tvos.deployment_target = '14.0' + s.watchos.deployment_target = '7.0' + s.visionos.deployment_target = '1.0' s.source_files = 'SDWebImageSwiftUI/Classes/**/*', 'SDWebImageSwiftUI/Module/*.h' s.pod_target_xcconfig = { 'SUPPORTS_MACCATALYST' => 'YES', 'DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER' => 'NO', - 'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES' + } + s.resource_bundles = { + 'SDWebImageSwiftUI' => ['SDWebImageSwiftUI/Resources/PrivacyInfo.xcprivacy'], } s.weak_frameworks = 'SwiftUI', 'Combine' - s.dependency 'SDWebImage', '~> 5.10' - s.swift_version = '5.2' + s.dependency 'SDWebImage', '~> 5.21.1' + s.swift_version = '5.3' end diff --git a/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/SDWebImageSwiftUI.xcodeproj/project.pbxproj index e3c34dce..61bba7e6 100644 --- a/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -3,54 +3,32 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ - 3211F84723DE984D00FC757F /* AnimatedImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3211F84623DE984D00FC757F /* AnimatedImageTests.swift */; }; - 3211F84923DE984D00FC757F /* SDWebImageSwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43DCC22FD540D00BE87F5 /* SDWebImageSwiftUI.framework */; }; - 3211F85023DE98E300FC757F /* WebImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3211F84F23DE98E300FC757F /* WebImageTests.swift */; }; - 3211F85323DE996700FC757F /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 3211F85223DE996700FC757F /* ViewInspector */; }; - 321C1D3323DEA28E009CF62A /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43DE922FD577300BE87F5 /* SDWebImage.framework */; }; - 321C1D3623DEA9E8009CF62A /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3211F85423DE9D2700FC757F /* Images.bundle */; }; - 321C1D4023DEC17D009CF62A /* SDWebImageSwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43DF422FD57FD00BE87F5 /* SDWebImageSwiftUI.framework */; }; - 321C1D4F23DEC185009CF62A /* SDWebImageSwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43E0122FD581400BE87F5 /* SDWebImageSwiftUI.framework */; }; - 321C1D5A23DEC207009CF62A /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 321C1D5923DEC207009CF62A /* ViewInspector */; }; - 321C1D5B23DEC219009CF62A /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43E2522FD585300BE87F5 /* SDWebImage.framework */; }; - 321C1D5C23DEC221009CF62A /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3211F85423DE9D2700FC757F /* Images.bundle */; }; - 321C1D5D23DEC222009CF62A /* Images.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3211F85423DE9D2700FC757F /* Images.bundle */; }; - 321C1D5E23DEC22D009CF62A /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43E2922FD586200BE87F5 /* SDWebImage.framework */; }; - 321C1D6023DEC231009CF62A /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 321C1D5F23DEC231009CF62A /* ViewInspector */; }; - 321C1D6A23DEDB98009CF62A /* AnimatedImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3211F84623DE984D00FC757F /* AnimatedImageTests.swift */; }; - 321C1D6B23DEDB98009CF62A /* WebImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3211F84F23DE98E300FC757F /* WebImageTests.swift */; }; - 321C1D6C23DEDB98009CF62A /* AnimatedImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3211F84623DE984D00FC757F /* AnimatedImageTests.swift */; }; - 321C1D6D23DEDB98009CF62A /* WebImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3211F84F23DE98E300FC757F /* WebImageTests.swift */; }; - 322E0F4823E57F09006836DC /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0F4723E57F09006836DC /* TestUtils.swift */; }; - 322E0F4923E57F09006836DC /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0F4723E57F09006836DC /* TestUtils.swift */; }; - 322E0F4A23E57F09006836DC /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322E0F4723E57F09006836DC /* TestUtils.swift */; }; + 3243AFE72AA37EFF0049A43B /* WebImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43DDE22FD54C600BE87F5 /* WebImage.swift */; }; + 3243AFE82AA37EFF0049A43B /* ImagePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */; }; + 3243AFE92AA37EFF0049A43B /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; }; + 3243AFEA2AA37EFF0049A43B /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; + 3243AFEB2AA37EFF0049A43B /* AnimatedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43DDF22FD54C600BE87F5 /* AnimatedImage.swift */; }; + 3243AFEC2AA37EFF0049A43B /* ImageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43DDC22FD54C600BE87F5 /* ImageManager.swift */; }; + 3243AFED2AA37EFF0049A43B /* SDWebImageSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */; }; + 3243AFEE2AA37F010049A43B /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; }; + 3243AFEF2AA37F030049A43B /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 326B84822363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; }; 326B84832363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; }; 326B84842363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; }; 326B84852363350C0011BDFB /* Indicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B84812363350C0011BDFB /* Indicator.swift */; }; - 326B8487236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; }; - 326B8488236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; }; - 326B8489236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; }; - 326B848A236335110011BDFB /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B8486236335110011BDFB /* ActivityIndicator.swift */; }; - 326B848C236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; }; - 326B848D236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; }; - 326B848E236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; }; - 326B848F236335400011BDFB /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B848B236335400011BDFB /* ProgressIndicator.swift */; }; 326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; }; 326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; }; 326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; }; 326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; }; + 329885EE2AA37FCB0071F2BA /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 329885ED2AA37FCB0071F2BA /* SDWebImage.framework */; }; 32B933E523659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 32B933E623659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 32B933E723659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; 32B933E823659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; }; - 32BD9C4723E03B08008D5F6A /* IndicatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BD9C4623E03B08008D5F6A /* IndicatorTests.swift */; }; - 32BD9C4823E03B08008D5F6A /* IndicatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BD9C4623E03B08008D5F6A /* IndicatorTests.swift */; }; - 32BD9C4923E03B08008D5F6A /* IndicatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BD9C4623E03B08008D5F6A /* IndicatorTests.swift */; }; 32C43DE622FD54CD00BE87F5 /* SDWebImageSwiftUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 32C43DE422FD54CD00BE87F5 /* SDWebImageSwiftUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32C43DEA22FD577300BE87F5 /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32C43DE922FD577300BE87F5 /* SDWebImage.framework */; }; 32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C43DDC22FD54C600BE87F5 /* ImageManager.swift */; }; @@ -79,58 +57,28 @@ 32CBA78125E4D7D800C6A8DC /* ImagePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */; }; 32CBA78225E4D7D800C6A8DC /* ImagePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */; }; 32CBA78325E4D7D800C6A8DC /* ImagePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */; }; - 32CBA78425E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */; }; - 32CBA78525E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */; }; - 32CBA78625E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */; }; - 32CBA78725E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */; }; 32D26A022446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; 32D26A032446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; 32D26A042446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; 32D26A052446B546005905DA /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D26A012446B546005905DA /* Image.swift */; }; - 32ED4826242A13030053338E /* ImageManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32ED4825242A13030053338E /* ImageManagerTests.swift */; }; - 32ED4827242A13030053338E /* ImageManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32ED4825242A13030053338E /* ImageManagerTests.swift */; }; - 32ED4828242A13030053338E /* ImageManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32ED4825242A13030053338E /* ImageManagerTests.swift */; }; + 32FFFE782BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE792BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE7A2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE7B2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */; }; + 32FFFE7C2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 3211F84A23DE984D00FC757F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 32C43DC322FD540D00BE87F5 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 32C43DCB22FD540D00BE87F5; - remoteInfo = SDWebImageSwiftUI; - }; - 321C1D4123DEC17D009CF62A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 32C43DC322FD540D00BE87F5 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 32C43DF322FD57FD00BE87F5; - remoteInfo = "SDWebImageSwiftUI macOS"; - }; - 321C1D5023DEC185009CF62A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 32C43DC322FD540D00BE87F5 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 32C43E0022FD581400BE87F5; - remoteInfo = "SDWebImageSwiftUI tvOS"; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXFileReference section */ - 3211F84423DE984D00FC757F /* SDWebImageSwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SDWebImageSwiftUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3211F84623DE984D00FC757F /* AnimatedImageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimatedImageTests.swift; sourceTree = ""; }; 3211F84823DE984D00FC757F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3211F84F23DE98E300FC757F /* WebImageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebImageTests.swift; sourceTree = ""; }; 3211F85423DE9D2700FC757F /* Images.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Images.bundle; sourceTree = ""; }; - 321C1D3B23DEC17D009CF62A /* SDWebImageSwiftUITests macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SDWebImageSwiftUITests macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 321C1D4A23DEC185009CF62A /* SDWebImageSwiftUITests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SDWebImageSwiftUITests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 322E0F4723E57F09006836DC /* TestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = ""; }; + 3243AFDF2AA37EE90049A43B /* SDWebImageSwiftUI_visionOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageSwiftUI_visionOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 326B84812363350C0011BDFB /* Indicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Indicator.swift; sourceTree = ""; }; - 326B8486236335110011BDFB /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; - 326B848B236335400011BDFB /* ProgressIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = ""; }; 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewWrapper.swift; sourceTree = ""; }; + 329885ED2AA37FCB0071F2BA /* SDWebImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDWebImage.framework; path = Carthage/Build/visionOS/SDWebImage.framework; sourceTree = ""; }; 32B933E423659A1900BB7CAD /* Transition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transition.swift; sourceTree = ""; }; - 32BD9C4623E03B08008D5F6A /* IndicatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndicatorTests.swift; sourceTree = ""; }; 32C43DCC22FD540D00BE87F5 /* SDWebImageSwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageSwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32C43DDC22FD54C600BE87F5 /* ImageManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageManager.swift; sourceTree = ""; }; 32C43DDE22FD54C600BE87F5 /* WebImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebImage.swift; sourceTree = ""; }; @@ -146,39 +94,17 @@ 32C43E2D22FD586E00BE87F5 /* SDWebImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDWebImage.framework; path = Carthage/Build/watchOS/SDWebImage.framework; sourceTree = ""; }; 32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDWebImageSwiftUI.swift; sourceTree = ""; }; 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagePlayer.swift; sourceTree = ""; }; - 32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUICompatibility.swift; sourceTree = ""; }; 32D26A012446B546005905DA /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; 32ED4825242A13030053338E /* ImageManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageManagerTests.swift; sourceTree = ""; }; + 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 3211F84123DE984D00FC757F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 321C1D3323DEA28E009CF62A /* SDWebImage.framework in Frameworks */, - 3211F84923DE984D00FC757F /* SDWebImageSwiftUI.framework in Frameworks */, - 3211F85323DE996700FC757F /* ViewInspector in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 321C1D3823DEC17D009CF62A /* Frameworks */ = { + 3243AFDC2AA37EE90049A43B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 321C1D5B23DEC219009CF62A /* SDWebImage.framework in Frameworks */, - 321C1D4023DEC17D009CF62A /* SDWebImageSwiftUI.framework in Frameworks */, - 321C1D5A23DEC207009CF62A /* ViewInspector in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 321C1D4723DEC185009CF62A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 321C1D5E23DEC22D009CF62A /* SDWebImage.framework in Frameworks */, - 321C1D4F23DEC185009CF62A /* SDWebImageSwiftUI.framework in Frameworks */, - 321C1D6023DEC231009CF62A /* ViewInspector in Frameworks */, + 329885EE2AA37FCB0071F2BA /* SDWebImage.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -224,7 +150,6 @@ 3211F84823DE984D00FC757F /* Info.plist */, 3211F84623DE984D00FC757F /* AnimatedImageTests.swift */, 3211F84F23DE98E300FC757F /* WebImageTests.swift */, - 32BD9C4623E03B08008D5F6A /* IndicatorTests.swift */, 32ED4825242A13030053338E /* ImageManagerTests.swift */, 322E0F4723E57F09006836DC /* TestUtils.swift */, ); @@ -235,8 +160,6 @@ isa = PBXGroup; children = ( 326B84812363350C0011BDFB /* Indicator.swift */, - 326B8486236335110011BDFB /* ActivityIndicator.swift */, - 326B848B236335400011BDFB /* ProgressIndicator.swift */, ); path = Indicator; sourceTree = ""; @@ -268,9 +191,7 @@ 32C43DF422FD57FD00BE87F5 /* SDWebImageSwiftUI.framework */, 32C43E0122FD581400BE87F5 /* SDWebImageSwiftUI.framework */, 32C43E0E22FD581C00BE87F5 /* SDWebImageSwiftUI.framework */, - 3211F84423DE984D00FC757F /* SDWebImageSwiftUITests.xctest */, - 321C1D3B23DEC17D009CF62A /* SDWebImageSwiftUITests macOS.xctest */, - 321C1D4A23DEC185009CF62A /* SDWebImageSwiftUITests tvOS.xctest */, + 3243AFDF2AA37EE90049A43B /* SDWebImageSwiftUI_visionOS.framework */, ); name = Products; sourceTree = ""; @@ -278,6 +199,7 @@ 32C43DCE22FD540D00BE87F5 /* SDWebImageSwiftUI */ = { isa = PBXGroup; children = ( + 32FFFE762BDF9CFD005D0719 /* Resources */, 32C43DDB22FD54C600BE87F5 /* Classes */, ); path = SDWebImageSwiftUI; @@ -290,7 +212,6 @@ 326099472362E09E006EBB22 /* Indicator */, 32C43DDC22FD54C600BE87F5 /* ImageManager.swift */, 32CBA77E25E4D7D800C6A8DC /* ImagePlayer.swift */, - 32CBA77F25E4D7D800C6A8DC /* SwiftUICompatibility.swift */, 32C43DDE22FD54C600BE87F5 /* WebImage.swift */, 32C43DDF22FD54C600BE87F5 /* AnimatedImage.swift */, 32C43E3122FD5DE100BE87F5 /* SDWebImageSwiftUI.swift */, @@ -303,6 +224,7 @@ 32C43DE822FD577300BE87F5 /* Frameworks */ = { isa = PBXGroup; children = ( + 329885ED2AA37FCB0071F2BA /* SDWebImage.framework */, 32C43E2D22FD586E00BE87F5 /* SDWebImage.framework */, 32C43E2922FD586200BE87F5 /* SDWebImage.framework */, 32C43E2522FD585300BE87F5 /* SDWebImage.framework */, @@ -311,9 +233,24 @@ name = Frameworks; sourceTree = ""; }; + 32FFFE762BDF9CFD005D0719 /* Resources */ = { + isa = PBXGroup; + children = ( + 32FFFE772BDF9CFD005D0719 /* PrivacyInfo.xcprivacy */, + ); + path = Resources; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 3243AFDA2AA37EE90049A43B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 32C43DC722FD540D00BE87F5 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -349,71 +286,23 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 3211F84323DE984D00FC757F /* SDWebImageSwiftUITests */ = { + 3243AFDE2AA37EE90049A43B /* SDWebImageSwiftUI visionOS */ = { isa = PBXNativeTarget; - buildConfigurationList = 3211F84E23DE984D00FC757F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests" */; + buildConfigurationList = 3243AFE32AA37EEA0049A43B /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUI visionOS" */; buildPhases = ( - 3211F84023DE984D00FC757F /* Sources */, - 3211F84123DE984D00FC757F /* Frameworks */, - 3211F84223DE984D00FC757F /* Resources */, - 321C1D3523DEA309009CF62A /* ShellScript */, + 3243AFDA2AA37EE90049A43B /* Headers */, + 3243AFDB2AA37EE90049A43B /* Sources */, + 3243AFDC2AA37EE90049A43B /* Frameworks */, + 3243AFDD2AA37EE90049A43B /* Resources */, ); buildRules = ( ); dependencies = ( - 3211F84B23DE984D00FC757F /* PBXTargetDependency */, ); - name = SDWebImageSwiftUITests; - packageProductDependencies = ( - 3211F85223DE996700FC757F /* ViewInspector */, - ); - productName = SDWebImageSwiftUITests; - productReference = 3211F84423DE984D00FC757F /* SDWebImageSwiftUITests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 321C1D3A23DEC17D009CF62A /* SDWebImageSwiftUITests macOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 321C1D4323DEC17D009CF62A /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests macOS" */; - buildPhases = ( - 321C1D3723DEC17D009CF62A /* Sources */, - 321C1D3823DEC17D009CF62A /* Frameworks */, - 321C1D3923DEC17D009CF62A /* Resources */, - 321C1D5523DEC1A9009CF62A /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - 321C1D4223DEC17D009CF62A /* PBXTargetDependency */, - ); - name = "SDWebImageSwiftUITests macOS"; - packageProductDependencies = ( - 321C1D5923DEC207009CF62A /* ViewInspector */, - ); - productName = "SDWebImageSwiftUITests macOS"; - productReference = 321C1D3B23DEC17D009CF62A /* SDWebImageSwiftUITests macOS.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 321C1D4923DEC185009CF62A /* SDWebImageSwiftUITests tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 321C1D5223DEC185009CF62A /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests tvOS" */; - buildPhases = ( - 321C1D4623DEC185009CF62A /* Sources */, - 321C1D4723DEC185009CF62A /* Frameworks */, - 321C1D4823DEC185009CF62A /* Resources */, - 321C1D5623DEC1C7009CF62A /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - 321C1D5123DEC185009CF62A /* PBXTargetDependency */, - ); - name = "SDWebImageSwiftUITests tvOS"; - packageProductDependencies = ( - 321C1D5F23DEC231009CF62A /* ViewInspector */, - ); - productName = "SDWebImageSwiftUITests tvOS"; - productReference = 321C1D4A23DEC185009CF62A /* SDWebImageSwiftUITests tvOS.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; + name = "SDWebImageSwiftUI visionOS"; + productName = "SDWebImageSwiftUI visionOS"; + productReference = 3243AFDF2AA37EE90049A43B /* SDWebImageSwiftUI_visionOS.framework */; + productType = "com.apple.product-type.framework"; }; 32C43DCB22FD540D00BE87F5 /* SDWebImageSwiftUI */ = { isa = PBXNativeTarget; @@ -497,14 +386,8 @@ LastUpgradeCheck = 1100; ORGANIZATIONNAME = SDWebImage; TargetAttributes = { - 3211F84323DE984D00FC757F = { - CreatedOnToolsVersion = 11.3.1; - }; - 321C1D3A23DEC17D009CF62A = { - CreatedOnToolsVersion = 11.3.1; - }; - 321C1D4923DEC185009CF62A = { - CreatedOnToolsVersion = 11.3.1; + 3243AFDE2AA37EE90049A43B = { + CreatedOnToolsVersion = 15.0; }; 32C43DCB22FD540D00BE87F5 = { CreatedOnToolsVersion = 11.0; @@ -533,9 +416,6 @@ Base, ); mainGroup = 32C43DC222FD540D00BE87F5; - packageReferences = ( - 3211F85123DE996700FC757F /* XCRemoteSwiftPackageReference "ViewInspector" */, - ); productRefGroup = 32C43DCD22FD540D00BE87F5 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -544,35 +424,17 @@ 32C43DF322FD57FD00BE87F5 /* SDWebImageSwiftUI macOS */, 32C43E0022FD581400BE87F5 /* SDWebImageSwiftUI tvOS */, 32C43E0D22FD581C00BE87F5 /* SDWebImageSwiftUI watchOS */, - 3211F84323DE984D00FC757F /* SDWebImageSwiftUITests */, - 321C1D3A23DEC17D009CF62A /* SDWebImageSwiftUITests macOS */, - 321C1D4923DEC185009CF62A /* SDWebImageSwiftUITests tvOS */, + 3243AFDE2AA37EE90049A43B /* SDWebImageSwiftUI visionOS */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 3211F84223DE984D00FC757F /* Resources */ = { + 3243AFDD2AA37EE90049A43B /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 321C1D3623DEA9E8009CF62A /* Images.bundle in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 321C1D3923DEC17D009CF62A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 321C1D5C23DEC221009CF62A /* Images.bundle in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 321C1D4823DEC185009CF62A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 321C1D5D23DEC222009CF62A /* Images.bundle in Resources */, + 32FFFE7C2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -580,6 +442,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32FFFE782BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -587,6 +450,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32FFFE792BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -594,6 +458,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32FFFE7A2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -601,105 +466,26 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32FFFE7B2BDF9CFD005D0719 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 321C1D3523DEA309009CF62A /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(SRCROOT)/Carthage/Build/iOS/SDWebImage.framework", - ); - outputFileListPaths = ( - ); - outputPaths = ( - "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/SDWebImage.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/usr/local/bin/carthage copy-frameworks\n"; - }; - 321C1D5523DEC1A9009CF62A /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(SRCROOT)/Carthage/Build/Mac/SDWebImage.framework", - ); - outputFileListPaths = ( - ); - outputPaths = ( - "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/SDWebImage.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/usr/local/bin/carthage copy-frameworks\n"; - }; - 321C1D5623DEC1C7009CF62A /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(SRCROOT)/Carthage/Build/tvOS/SDWebImage.framework", - ); - outputFileListPaths = ( - ); - outputPaths = ( - "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/SDWebImage.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/usr/local/bin/carthage copy-frameworks\n"; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ - 3211F84023DE984D00FC757F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3211F85023DE98E300FC757F /* WebImageTests.swift in Sources */, - 32BD9C4723E03B08008D5F6A /* IndicatorTests.swift in Sources */, - 3211F84723DE984D00FC757F /* AnimatedImageTests.swift in Sources */, - 322E0F4823E57F09006836DC /* TestUtils.swift in Sources */, - 32ED4826242A13030053338E /* ImageManagerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 321C1D3723DEC17D009CF62A /* Sources */ = { + 3243AFDB2AA37EE90049A43B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 321C1D6B23DEDB98009CF62A /* WebImageTests.swift in Sources */, - 32BD9C4823E03B08008D5F6A /* IndicatorTests.swift in Sources */, - 321C1D6A23DEDB98009CF62A /* AnimatedImageTests.swift in Sources */, - 322E0F4923E57F09006836DC /* TestUtils.swift in Sources */, - 32ED4827242A13030053338E /* ImageManagerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 321C1D4623DEC185009CF62A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 321C1D6D23DEDB98009CF62A /* WebImageTests.swift in Sources */, - 32BD9C4923E03B08008D5F6A /* IndicatorTests.swift in Sources */, - 321C1D6C23DEDB98009CF62A /* AnimatedImageTests.swift in Sources */, - 322E0F4A23E57F09006836DC /* TestUtils.swift in Sources */, - 32ED4828242A13030053338E /* ImageManagerTests.swift in Sources */, + 3243AFEF2AA37F030049A43B /* Transition.swift in Sources */, + 3243AFE92AA37EFF0049A43B /* ImageViewWrapper.swift in Sources */, + 3243AFE72AA37EFF0049A43B /* WebImage.swift in Sources */, + 3243AFEC2AA37EFF0049A43B /* ImageManager.swift in Sources */, + 3243AFEB2AA37EFF0049A43B /* AnimatedImage.swift in Sources */, + 3243AFE82AA37EFF0049A43B /* ImagePlayer.swift in Sources */, + 3243AFED2AA37EFF0049A43B /* SDWebImageSwiftUI.swift in Sources */, + 3243AFEE2AA37F010049A43B /* Indicator.swift in Sources */, + 3243AFEA2AA37EFF0049A43B /* Image.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -709,13 +495,10 @@ files = ( 32B933E523659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78025E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, - 32CBA78425E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */, 32C43E1722FD583700BE87F5 /* WebImage.swift in Sources */, - 326B848C236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84822363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3222FD5DE100BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480A23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, - 326B8487236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1622FD583700BE87F5 /* ImageManager.swift in Sources */, 32C43E1822FD583700BE87F5 /* AnimatedImage.swift in Sources */, 32D26A022446B546005905DA /* Image.swift in Sources */, @@ -728,13 +511,10 @@ files = ( 32B933E623659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78125E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, - 32CBA78525E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */, 32C43E1A22FD583700BE87F5 /* WebImage.swift in Sources */, - 326B848D236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84832363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3322FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, - 326B8488236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1922FD583700BE87F5 /* ImageManager.swift in Sources */, 32C43E1B22FD583700BE87F5 /* AnimatedImage.swift in Sources */, 32D26A032446B546005905DA /* Image.swift in Sources */, @@ -747,13 +527,10 @@ files = ( 32B933E723659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78225E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, - 32CBA78625E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */, 32C43E1D22FD583800BE87F5 /* WebImage.swift in Sources */, - 326B848E236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84842363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3422FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, - 326B8489236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1C22FD583800BE87F5 /* ImageManager.swift in Sources */, 32C43E1E22FD583800BE87F5 /* AnimatedImage.swift in Sources */, 32D26A042446B546005905DA /* Image.swift in Sources */, @@ -766,13 +543,10 @@ files = ( 32B933E823659A1900BB7CAD /* Transition.swift in Sources */, 32CBA78325E4D7D800C6A8DC /* ImagePlayer.swift in Sources */, - 32CBA78725E4D7D800C6A8DC /* SwiftUICompatibility.swift in Sources */, 32C43E2022FD583800BE87F5 /* WebImage.swift in Sources */, - 326B848F236335400011BDFB /* ProgressIndicator.swift in Sources */, 326B84852363350C0011BDFB /* Indicator.swift in Sources */, 32C43E3522FD5DF400BE87F5 /* SDWebImageSwiftUI.swift in Sources */, 326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */, - 326B848A236335110011BDFB /* ActivityIndicator.swift in Sources */, 32C43E1F22FD583800BE87F5 /* ImageManager.swift in Sources */, 32C43E2122FD583800BE87F5 /* AnimatedImage.swift in Sources */, 32D26A052446B546005905DA /* Image.swift in Sources */, @@ -781,158 +555,89 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 3211F84B23DE984D00FC757F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 32C43DCB22FD540D00BE87F5 /* SDWebImageSwiftUI */; - targetProxy = 3211F84A23DE984D00FC757F /* PBXContainerItemProxy */; - }; - 321C1D4223DEC17D009CF62A /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 32C43DF322FD57FD00BE87F5 /* SDWebImageSwiftUI macOS */; - targetProxy = 321C1D4123DEC17D009CF62A /* PBXContainerItemProxy */; - }; - 321C1D5123DEC185009CF62A /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 32C43E0022FD581400BE87F5 /* SDWebImageSwiftUI tvOS */; - targetProxy = 321C1D5023DEC185009CF62A /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin XCBuildConfiguration section */ - 3211F84C23DE984D00FC757F /* Debug */ = { + 3243AFE42AA37EEA0049A43B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CODE_SIGN_STYLE = Automatic; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.dreampiggy.SDWebImageSwiftUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 3211F84D23DE984D00FC757F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CODE_SIGN_STYLE = Automatic; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.dreampiggy.SDWebImageSwiftUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - 321C1D4423DEC17D009CF62A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUITests-macOS"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 321C1D4523DEC17D009CF62A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUITests-macOS"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 321C1D5323DEC185009CF62A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 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; + ENABLE_USER_SCRIPT_SANDBOXING = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/tvOS", + "$(PROJECT_DIR)/Carthage/Build/visionOS", ); - INFOPLIST_FILE = Tests/Info.plist; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 SDWebImage. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUITests-tvOS"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUI-visionOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = xros; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; + TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Debug; }; - 321C1D5423DEC185009CF62A /* Release */ = { + 3243AFE52AA37EEA0049A43B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 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; + ENABLE_USER_SCRIPT_SANDBOXING = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/tvOS", + "$(PROJECT_DIR)/Carthage/Build/visionOS", ); - INFOPLIST_FILE = Tests/Info.plist; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 SDWebImage. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUITests-tvOS"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUI-visionOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = xros; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; + TARGETED_DEVICE_FAMILY = "1,2,7"; }; name = Release; }; @@ -988,8 +693,8 @@ 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; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -1002,10 +707,11 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 14.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 6.0; + WATCHOS_DEPLOYMENT_TARGET = 7.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Debug; }; @@ -1055,8 +761,8 @@ 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; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ( @@ -1068,11 +774,12 @@ SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 14.0; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 6.0; + WATCHOS_DEPLOYMENT_TARGET = 7.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Release; }; @@ -1156,7 +863,6 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUI-macOS"; PRODUCT_NAME = SDWebImageSwiftUI; SDKROOT = macosx; @@ -1187,7 +893,6 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = "com.dreampiggy.SDWebImageSwiftUI-macOS"; PRODUCT_NAME = SDWebImageSwiftUI; SDKROOT = macosx; @@ -1319,29 +1024,11 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 3211F84E23DE984D00FC757F /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3211F84C23DE984D00FC757F /* Debug */, - 3211F84D23DE984D00FC757F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 321C1D4323DEC17D009CF62A /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests macOS" */ = { + 3243AFE32AA37EEA0049A43B /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUI visionOS" */ = { isa = XCConfigurationList; buildConfigurations = ( - 321C1D4423DEC17D009CF62A /* Debug */, - 321C1D4523DEC17D009CF62A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 321C1D5223DEC185009CF62A /* Build configuration list for PBXNativeTarget "SDWebImageSwiftUITests tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 321C1D5323DEC185009CF62A /* Debug */, - 321C1D5423DEC185009CF62A /* Release */, + 3243AFE42AA37EEA0049A43B /* Debug */, + 3243AFE52AA37EEA0049A43B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1392,35 +1079,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 3211F85123DE996700FC757F /* XCRemoteSwiftPackageReference "ViewInspector" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/nalexn/ViewInspector.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.3.11; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 3211F85223DE996700FC757F /* ViewInspector */ = { - isa = XCSwiftPackageProductDependency; - package = 3211F85123DE996700FC757F /* XCRemoteSwiftPackageReference "ViewInspector" */; - productName = ViewInspector; - }; - 321C1D5923DEC207009CF62A /* ViewInspector */ = { - isa = XCSwiftPackageProductDependency; - package = 3211F85123DE996700FC757F /* XCRemoteSwiftPackageReference "ViewInspector" */; - productName = ViewInspector; - }; - 321C1D5F23DEC231009CF62A /* ViewInspector */ = { - isa = XCSwiftPackageProductDependency; - package = 3211F85123DE996700FC757F /* XCRemoteSwiftPackageReference "ViewInspector" */; - productName = ViewInspector; - }; -/* End XCSwiftPackageProductDependency section */ }; rootObject = 32C43DC322FD540D00BE87F5 /* Project object */; } diff --git a/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 03bf90e2..00000000 --- a/SDWebImageSwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,16 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "ViewInspector", - "repositoryURL": "https://github.com/nalexn/ViewInspector.git", - "state": { - "branch": null, - "revision": "7d55eb940242512aad2bf28db354d09d5de43893", - "version": "0.3.11" - } - } - ] - }, - "version": 1 -} diff --git a/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUITests.xcscheme b/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUI visionOS.xcscheme similarity index 68% rename from SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUITests.xcscheme rename to SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUI visionOS.xcscheme index 6622575b..def8a061 100644 --- a/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUITests.xcscheme +++ b/SDWebImageSwiftUI.xcodeproj/xcshareddata/xcschemes/SDWebImageSwiftUI visionOS.xcscheme @@ -1,7 +1,7 @@ + LastUpgradeVersion = "1500" + version = "1.7"> @@ -14,9 +14,9 @@ buildForAnalyzing = "YES"> @@ -27,19 +27,7 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> - - - - - - + shouldAutocreateTestPlan = "YES"> diff --git a/SDWebImageSwiftUI.xcworkspace/contents.xcworkspacedata b/SDWebImageSwiftUI.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..9b0b7f6b --- /dev/null +++ b/SDWebImageSwiftUI.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SDWebImageSwiftUI/Assets/.gitkeep b/SDWebImageSwiftUI/Assets/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/SDWebImageSwiftUI/Classes/AnimatedImage.swift b/SDWebImageSwiftUI/Classes/AnimatedImage.swift index d5df9c16..4e5a7a75 100644 --- a/SDWebImageSwiftUI/Classes/AnimatedImage.swift +++ b/SDWebImageSwiftUI/Classes/AnimatedImage.swift @@ -9,10 +9,10 @@ import SwiftUI import SDWebImage -#if os(iOS) || os(tvOS) || os(macOS) +#if !os(watchOS) /// A coordinator object used for `AnimatedImage`native view bridge for UIKit/AppKit. -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public final class AnimatedImageCoordinator: NSObject { /// Any user-provided object for actual coordinator, such as delegate method, taget-action @@ -20,15 +20,30 @@ public final class AnimatedImageCoordinator: NSObject { /// Any user-provided info stored into coordinator, such as status value used for coordinator public var userInfo: [AnyHashable : Any]? + + var imageLoading = AnimatedLoadingModel() } /// Data Binding Object, only properties in this object can support changes from user with @State and refresh -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageModel : ObservableObject { + enum Kind { + case url + case data + case name + case unknown + } + var kind: Kind = .unknown /// URL image @Published var url: URL? @Published var webOptions: SDWebImageOptions = [] @Published var webContext: [SDWebImageContextOption : Any]? = nil + @Published var placeholderImage: PlatformImage? + @Published var placeholderView: PlatformView? { + didSet { + oldValue?.removeFromSuperview() + } + } /// Name image @Published var name: String? @Published var bundle: Bundle? @@ -38,8 +53,8 @@ final class AnimatedImageModel : ObservableObject { } /// Loading Binding Object, only properties in this object can support changes from user with @State and refresh -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -final class AnimatedLoadingModel : ObservableObject, IndicatorReportable { +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +final class AnimatedLoadingModel : ObservableObject { @Published var image: PlatformImage? // loaded image, note when progressive loading, this will published multiple times with different partial image @Published var isLoading: Bool = false // whether network is loading or cache is querying, should only be used for indicator binding @Published var progress: Double = 0 // network progress, should only be used for indicator binding @@ -51,19 +66,19 @@ final class AnimatedLoadingModel : ObservableObject, IndicatorReportable { } /// Completion Handler Binding Object, supports dynamic @State changes -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageHandler: ObservableObject { // Completion Handler @Published var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? @Published var failureBlock: ((Error) -> Void)? @Published var progressBlock: ((Int, Int) -> Void)? // Coordinator Handler - @Published var viewCreateBlock: ((PlatformView, AnimatedImage.Context) -> Void)? - @Published var viewUpdateBlock: ((PlatformView, AnimatedImage.Context) -> Void)? + @Published var viewCreateBlock: ((SDAnimatedImageView, AnimatedImage.Context) -> Void)? + @Published var viewUpdateBlock: ((SDAnimatedImageView, AnimatedImage.Context) -> Void)? } /// Layout Binding Object, supports dynamic @State changes -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageLayout : ObservableObject { var contentMode: ContentMode? var aspectRatio: CGFloat? @@ -75,7 +90,7 @@ final class AnimatedImageLayout : ObservableObject { } /// Configuration Binding Object, supports dynamic @State changes -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) final class AnimatedImageConfiguration: ObservableObject { var incrementalLoad: Bool? var maxBufferSize: UInt? @@ -88,36 +103,39 @@ final class AnimatedImageConfiguration: ObservableObject { // These configurations only useful for web image loading var indicator: SDWebImageIndicator? var transition: SDWebImageTransition? - var placeholder: PlatformImage? - var placeholderView: PlatformView? { - didSet { - oldValue?.removeFromSuperview() - } - } } /// A Image View type to load image from url, data or bundle. Supports animated and static image format. -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public struct AnimatedImage : PlatformViewRepresentable { - @ObservedObject var imageModel = AnimatedImageModel() - @ObservedObject var imageLoading = AnimatedLoadingModel() + @ObservedObject var imageModel: AnimatedImageModel @ObservedObject var imageHandler = AnimatedImageHandler() @ObservedObject var imageLayout = AnimatedImageLayout() @ObservedObject var imageConfiguration = AnimatedImageConfiguration() - static var viewDestroyBlock: ((PlatformView, Coordinator) -> Void)? + /// A observed object to pass through the image manager loading status to indicator + @ObservedObject var indicatorStatus = IndicatorStatus() + + static var viewDestroyBlock: ((SDAnimatedImageView, Coordinator) -> Void)? /// A Binding to control the animation. You can bind external logic to control the animation status. /// True to start animation, false to stop animation. @Binding public var isAnimating: Bool - /// Create an animated image with url, placeholder, custom options and context. + /// Create an animated image with url, placeholder, custom options and context, including animation control binding. /// - Parameter url: The image url /// - Parameter placeholder: The placeholder image to show during loading /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) { - self.init(url: url, options: options, context: context, isAnimating: .constant(true)) + /// - Parameter isAnimating: The binding for animation control + public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), placeholderImage: PlatformImage? = nil) { + let imageModel = AnimatedImageModel() + imageModel.kind = .url + imageModel.url = url + imageModel.webOptions = options + imageModel.webContext = context + imageModel.placeholderImage = placeholderImage + self.init(imageModel: imageModel, isAnimating: isAnimating) } /// Create an animated image with url, placeholder, custom options and context, including animation control binding. @@ -126,19 +144,19 @@ public struct AnimatedImage : PlatformViewRepresentable { /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. /// - Parameter isAnimating: The binding for animation control - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding) { - self._isAnimating = isAnimating - self.imageModel.url = url - self.imageModel.webOptions = options - self.imageModel.webContext = context - } - - /// Create an animated image with name and bundle. - /// - Note: Asset Catalog is not supported. - /// - Parameter name: The image name - /// - Parameter bundle: The bundle contains image - public init(name: String, bundle: Bundle? = nil) { - self.init(name: name, bundle: bundle, isAnimating: .constant(true)) + public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), @ViewBuilder placeholder: @escaping () -> T) where T : View { + let imageModel = AnimatedImageModel() + imageModel.kind = .url + imageModel.url = url + imageModel.webOptions = options + imageModel.webContext = context + #if os(macOS) + let hostingView = NSHostingView(rootView: placeholder()) + #else + let hostingView = _UIHostingView(rootView: placeholder()) + #endif + imageModel.placeholderView = hostingView + self.init(imageModel: imageModel, isAnimating: isAnimating) } /// Create an animated image with name and bundle, including animation control binding. @@ -146,34 +164,32 @@ public struct AnimatedImage : PlatformViewRepresentable { /// - Parameter name: The image name /// - Parameter bundle: The bundle contains image /// - Parameter isAnimating: The binding for animation control - public init(name: String, bundle: Bundle? = nil, isAnimating: Binding) { - self._isAnimating = isAnimating - self.imageModel.name = name - self.imageModel.bundle = bundle - } - - /// Create an animated image with data and scale. - /// - Parameter data: The image data - /// - Parameter scale: The scale factor - public init(data: Data, scale: CGFloat = 1) { - self.init(data: data, scale: scale, isAnimating: .constant(true)) + public init(name: String, bundle: Bundle? = nil, isAnimating: Binding = .constant(true)) { + let imageModel = AnimatedImageModel() + imageModel.kind = .name + imageModel.name = name + imageModel.bundle = bundle + self.init(imageModel: imageModel, isAnimating: isAnimating) } /// Create an animated image with data and scale, including animation control binding. /// - Parameter data: The image data /// - Parameter scale: The scale factor /// - Parameter isAnimating: The binding for animation control - public init(data: Data, scale: CGFloat = 1, isAnimating: Binding) { + public init(data: Data, scale: CGFloat = 1, isAnimating: Binding = .constant(true)) { + let imageModel = AnimatedImageModel() + imageModel.kind = .data + imageModel.data = data + imageModel.scale = scale + self.init(imageModel: imageModel, isAnimating: isAnimating) + } + + init(imageModel: AnimatedImageModel, isAnimating: Binding) { self._isAnimating = isAnimating - self.imageModel.data = data - self.imageModel.scale = scale + _imageModel = ObservedObject(wrappedValue: imageModel) } - #if os(macOS) - public typealias NSViewType = AnimatedImageViewWrapper - #elseif os(iOS) || os(tvOS) - public typealias UIViewType = AnimatedImageViewWrapper - #endif + public typealias PlatformViewType = AnimatedImageViewWrapper public typealias Coordinator = AnimatedImageCoordinator @@ -182,23 +198,23 @@ public struct AnimatedImage : PlatformViewRepresentable { } #if os(macOS) - public func makeNSView(context: NSViewRepresentableContext) -> AnimatedImageViewWrapper { + public func makeNSView(context: Context) -> AnimatedImageViewWrapper { makeView(context: context) } - public func updateNSView(_ nsView: AnimatedImageViewWrapper, context: NSViewRepresentableContext) { + public func updateNSView(_ nsView: AnimatedImageViewWrapper, context: Context) { updateView(nsView, context: context) } public static func dismantleNSView(_ nsView: AnimatedImageViewWrapper, coordinator: Coordinator) { dismantleView(nsView, coordinator: coordinator) } - #elseif os(iOS) || os(tvOS) - public func makeUIView(context: UIViewRepresentableContext) -> AnimatedImageViewWrapper { + #else + public func makeUIView(context: Context) -> AnimatedImageViewWrapper { makeView(context: context) } - public func updateUIView(_ uiView: AnimatedImageViewWrapper, context: UIViewRepresentableContext) { + public func updateUIView(_ uiView: AnimatedImageViewWrapper, context: Context) { updateView(uiView, context: context) } @@ -210,7 +226,7 @@ public struct AnimatedImage : PlatformViewRepresentable { func setupIndicator(_ view: AnimatedImageViewWrapper, context: Context) { view.wrapped.sd_imageIndicator = imageConfiguration.indicator view.wrapped.sd_imageTransition = imageConfiguration.transition - if let placeholderView = imageConfiguration.placeholderView { + if let placeholderView = imageModel.placeholderView { placeholderView.removeFromSuperview() placeholderView.isHidden = true // Placeholder View should below the Indicator View @@ -228,16 +244,16 @@ public struct AnimatedImage : PlatformViewRepresentable { } func loadImage(_ view: AnimatedImageViewWrapper, context: Context) { - self.imageLoading.isLoading = true - let options = imageModel.webOptions - if options.contains(.delayPlaceholder) { - self.imageConfiguration.placeholderView?.isHidden = true + context.coordinator.imageLoading.isLoading = true + let webOptions = imageModel.webOptions + if webOptions.contains(.delayPlaceholder) { + self.imageModel.placeholderView?.isHidden = true } else { - self.imageConfiguration.placeholderView?.isHidden = false + self.imageModel.placeholderView?.isHidden = false } - var context = imageModel.webContext ?? [:] - context[.animatedImageClass] = SDAnimatedImage.self - view.wrapped.sd_internalSetImage(with: imageModel.url, placeholderImage: imageConfiguration.placeholder, options: options, context: context, setImageBlock: nil, progress: { (receivedSize, expectedSize, _) in + var webContext = imageModel.webContext ?? [:] + webContext[.animatedImageClass] = SDAnimatedImage.self + view.wrapped.sd_internalSetImage(with: imageModel.url, placeholderImage: imageModel.placeholderImage, options: webOptions, context: webContext, setImageBlock: nil, progress: { (receivedSize, expectedSize, _) in let progress: Double if (expectedSize > 0) { progress = Double(receivedSize) / Double(expectedSize) @@ -245,35 +261,22 @@ public struct AnimatedImage : PlatformViewRepresentable { progress = 0 } DispatchQueue.main.async { - self.imageLoading.progress = progress + context.coordinator.imageLoading.progress = progress } self.imageHandler.progressBlock?(receivedSize, expectedSize) }) { (image, data, error, cacheType, finished, _) in - if #available(iOS 14.0, macOS 11.0, watchOS 7.0, tvOS 14.0, *) { - // Do nothing. on iOS 14's SwiftUI, the @Published will always trigger another `updateUIView` call with new UIView instance. - } else { - // This is a hack because of iOS 13's SwiftUI bug, the @Published does not trigger another `updateUIView` call - // Here I have to use UIKit/AppKit API to triger the same effect (the window change implicitly cause re-render) - if let hostingView = view.findHostingView() { - if let _ = hostingView.window { - #if os(macOS) - hostingView.viewDidMoveToWindow() - #else - hostingView.didMoveToWindow() - #endif - } - } - } - self.imageLoading.image = image - self.imageLoading.isLoading = false - self.imageLoading.progress = 1 + context.coordinator.imageLoading.image = image + context.coordinator.imageLoading.isLoading = false + context.coordinator.imageLoading.progress = 1 if let image = image { - self.imageConfiguration.placeholderView?.isHidden = true + self.imageModel.placeholderView?.isHidden = true self.imageHandler.successBlock?(image, data, cacheType) } else { - self.imageConfiguration.placeholderView?.isHidden = false + self.imageModel.placeholderView?.isHidden = false self.imageHandler.failureBlock?(error ?? NSError()) } + // Finished loading, async + finishUpdateView(view, context: context) } } @@ -285,61 +288,84 @@ public struct AnimatedImage : PlatformViewRepresentable { return view } - func updateView(_ view: AnimatedImageViewWrapper, context: Context) { - // Refresh image, imageModel is the Source of Truth, switch the type - // Although we have Source of Truth, we can check the previous value, to avoid re-generate SDAnimatedImage, which is performance-cost. - if let name = imageModel.name, name != imageLoading.imageName { - #if os(macOS) - let image = SDAnimatedImage(named: name, in: imageModel.bundle) - #else - let image = SDAnimatedImage(named: name, in: imageModel.bundle, compatibleWith: nil) - #endif - imageLoading.imageName = name - view.wrapped.image = image - } else if let data = imageModel.data, data != imageLoading.imageData { - let image = SDAnimatedImage(data: data, scale: imageModel.scale) - imageLoading.imageData = data - view.wrapped.image = image - } else if let url = imageModel.url { - // Determine if image already been loaded and URL is match - var shouldLoad: Bool - if url != imageLoading.imageURL { - // Change the URL, need new loading - shouldLoad = true - imageLoading.imageURL = url - } else { - // Same URL, check if already loaded - if imageLoading.isLoading { - shouldLoad = false - } else if let image = imageLoading.image { - shouldLoad = false - view.wrapped.image = image - } else { - shouldLoad = true - } - } - if shouldLoad { - setupIndicator(view, context: context) - loadImage(view, context: context) - } + private func updateViewForName(_ name: String?, view: AnimatedImageViewWrapper, context: Context) { + guard let name = name, name != context.coordinator.imageLoading.imageName else { + return } - + var image: PlatformImage? #if os(macOS) - if self.isAnimating != view.wrapped.animates { - view.wrapped.animates = self.isAnimating + image = SDAnimatedImage(named: name, in: imageModel.bundle) + if image == nil { + // For static image, use NSImage as defaults + let bundle = imageModel.bundle ?? .main + image = bundle.image(forResource: name) } #else - if self.isAnimating != view.wrapped.isAnimating { - if self.isAnimating { - view.wrapped.startAnimating() + image = SDAnimatedImage(named: name, in: imageModel.bundle, compatibleWith: nil) + if image == nil { + // For static image, use UIImage as defaults + image = PlatformImage(named: name, in: imageModel.bundle, compatibleWith: nil) + } + #endif + context.coordinator.imageLoading.imageName = name + view.wrapped.image = image + } + + private func updateViewForData(_ data: Data?, view: AnimatedImageViewWrapper, context: Context) { + guard let data = data, data != context.coordinator.imageLoading.imageData else { + return + } + var image: PlatformImage? = SDAnimatedImage(data: data, scale: imageModel.scale) + if image == nil { + // For static image, use UIImage as defaults + image = PlatformImage.sd_image(with: data, scale: imageModel.scale) + } + context.coordinator.imageLoading.imageData = data + view.wrapped.image = image + } + + private func updateViewForURL(_ url: URL?, view: AnimatedImageViewWrapper, context: Context) { + // Determine if image already been loaded and URL is match + var shouldLoad: Bool + if url != context.coordinator.imageLoading.imageURL { + // Change the URL, need new loading + shouldLoad = true + context.coordinator.imageLoading.imageURL = url + } else { + // Same URL, check if already loaded + if context.coordinator.imageLoading.isLoading { + shouldLoad = false + } else if let image = context.coordinator.imageLoading.image { + shouldLoad = false + view.wrapped.image = image } else { - view.wrapped.stopAnimating() + shouldLoad = true } } - #endif + if shouldLoad { + setupIndicator(view, context: context) + loadImage(view, context: context) + } + } + + func updateView(_ view: AnimatedImageViewWrapper, context: Context) { + // Refresh image, imageModel is the Source of Truth, switch the type + // Although we have Source of Truth, we can check the previous value, to avoid re-generate SDAnimatedImage, which is performance-cost. + let kind = imageModel.kind + switch kind { + case .name: + updateViewForName(imageModel.name, view: view, context: context) + case .data: + updateViewForData(imageModel.data, view: view, context: context) + case .url: + updateViewForURL(imageModel.url, view: view, context: context) + case .unknown: + break // impossible + } + + // Finished loading, sync + finishUpdateView(view, context: context) - configureView(view, context: context) - layoutView(view, context: context) if let viewUpdateBlock = imageHandler.viewUpdateBlock { viewUpdateBlock(view.wrapped, context) } @@ -357,18 +383,24 @@ public struct AnimatedImage : PlatformViewRepresentable { } } + func finishUpdateView(_ view: AnimatedImageViewWrapper, context: Context) { + // Finished loading + configureView(view, context: context) + layoutView(view, context: context) + } + func layoutView(_ view: AnimatedImageViewWrapper, context: Context) { // AspectRatio && ContentMode #if os(macOS) let contentMode: NSImageScaling - #elseif os(iOS) || os(tvOS) + #else let contentMode: UIView.ContentMode #endif if let _ = imageLayout.aspectRatio { // If `aspectRatio` is not `nil`, always scale to fill and SwiftUI will layout the container with custom aspect ratio. #if os(macOS) contentMode = .scaleAxesIndependently - #elseif os(iOS) || os(tvOS) + #else contentMode = .scaleToFill #endif } else { @@ -379,20 +411,20 @@ public struct AnimatedImage : PlatformViewRepresentable { // Actually, NSImageView have no `.aspectFill` unlike UIImageView, only `CALayerContentsGravity.resizeAspectFill` have the same concept // However, using `.scaleProportionallyUpOrDown`, SwiftUI still layout the HostingView correctly, so this is OK contentMode = .scaleProportionallyUpOrDown - #elseif os(iOS) || os(tvOS) + #else contentMode = .scaleAspectFill #endif case .fit: #if os(macOS) contentMode = .scaleProportionallyUpOrDown - #elseif os(iOS) || os(tvOS) + #else contentMode = .scaleAspectFit #endif case .none: // If `contentMode` is not set at all, using scale to fill as SwiftUI default value #if os(macOS) contentMode = .scaleAxesIndependently - #elseif os(iOS) || os(tvOS) + #else contentMode = .scaleToFill #endif } @@ -405,12 +437,10 @@ public struct AnimatedImage : PlatformViewRepresentable { #endif // Resizable - if let _ = imageLayout.resizingMode { - view.resizable = true - } + view.resizingMode = imageLayout.resizingMode // Animated Image does not support resizing mode and rendering mode - if let image = view.wrapped.image, !image.conforms(to: SDAnimatedImageProtocol.self) { + if let image = view.wrapped.image { var image = image // ResizingMode if let resizingMode = imageLayout.resizingMode, imageLayout.capInsets != EdgeInsets() { @@ -550,11 +580,26 @@ public struct AnimatedImage : PlatformViewRepresentable { } else { view.wrapped.playbackMode = .normal } + + // Animation + #if os(macOS) + if self.isAnimating != view.wrapped.animates { + view.wrapped.animates = self.isAnimating + } + #else + if self.isAnimating != view.wrapped.isAnimating { + if self.isAnimating { + view.wrapped.startAnimating() + } else { + view.wrapped.stopAnimating() + } + } + #endif } } // Layout -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Configurate this view's image with the specified cap insets and options. @@ -593,61 +638,8 @@ extension AnimatedImage { } } -// Aspect Ratio -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -extension AnimatedImage { - /// Constrains this view's dimensions to the specified aspect ratio. - /// - Parameters: - /// - aspectRatio: The ratio of width to height to use for the resulting - /// view. If `aspectRatio` is `nil`, the resulting view maintains this - /// view's aspect ratio. - /// - contentMode: A flag indicating whether this view should fit or - /// fill the parent context. - /// - Returns: A view that constrains this view's dimensions to - /// `aspectRatio`, using `contentMode` as its scaling algorithm. - public func aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> some View { - // The `SwifUI.View.aspectRatio(_:contentMode:)` says: - // If `aspectRatio` is `nil`, the resulting view maintains this view's aspect ratio - // But 1: there are no public API to declare what `this view's aspect ratio` is - // So, if we don't override this method, SwiftUI ignore the content mode on actual ImageView - // To workaround, we want to call the default `SwifUI.View.aspectRatio(_:contentMode:)` method - // But 2: there are no way to call a Protocol Extention default implementation in Swift 5.1 - // So, we directly call the implementation detail modifier instead - // Fired Radar: FB7413534 - self.imageLayout.aspectRatio = aspectRatio - self.imageLayout.contentMode = contentMode - return self.modifier(_AspectRatioLayout(aspectRatio: aspectRatio, contentMode: contentMode)) - } - - /// Constrains this view's dimensions to the aspect ratio of the given size. - /// - Parameters: - /// - aspectRatio: A size specifying the ratio of width to height to use - /// for the resulting view. - /// - contentMode: A flag indicating whether this view should fit or - /// fill the parent context. - /// - Returns: A view that constrains this view's dimensions to - /// `aspectRatio`, using `contentMode` as its scaling algorithm. - public func aspectRatio(_ aspectRatio: CGSize, contentMode: ContentMode) -> some View { - return self.aspectRatio(aspectRatio.width / aspectRatio.height, contentMode: contentMode) - } - - /// Scales this view to fit its parent. - /// - Returns: A view that scales this view to fit its parent, - /// maintaining this view's aspect ratio. - public func scaledToFit() -> some View { - return self.aspectRatio(nil, contentMode: .fit) - } - - /// Scales this view to fill its parent. - /// - Returns: A view that scales this view to fit its parent, - /// maintaining this view's aspect ratio. - public func scaledToFill() -> some View { - return self.aspectRatio(nil, contentMode: .fill) - } -} - // AnimatedImage Modifier -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Total loop count for animated image rendering. Defaults to nil. @@ -724,7 +716,7 @@ extension AnimatedImage { } // Completion Handler -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Provide the action when image load fails. @@ -756,13 +748,13 @@ extension AnimatedImage { } // View Coordinator Handler -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { /// Provide the action when view representable create the native view. /// - Parameter action: The action to perform. The first arg is the native view. The seconds arg is the context. /// - Returns: A view that triggers `action` when view representable create the native view. - public func onViewCreate(perform action: ((PlatformView, Context) -> Void)? = nil) -> AnimatedImage { + public func onViewCreate(perform action: ((SDAnimatedImageView, Context) -> Void)? = nil) -> AnimatedImage { self.imageHandler.viewCreateBlock = action return self } @@ -770,7 +762,7 @@ extension AnimatedImage { /// Provide the action when view representable update the native view. /// - Parameter action: The action to perform. The first arg is the native view. The seconds arg is the context. /// - Returns: A view that triggers `action` when view representable update the native view. - public func onViewUpdate(perform action: ((PlatformView, Context) -> Void)? = nil) -> AnimatedImage { + public func onViewUpdate(perform action: ((SDAnimatedImageView, Context) -> Void)? = nil) -> AnimatedImage { self.imageHandler.viewUpdateBlock = action return self } @@ -778,35 +770,24 @@ extension AnimatedImage { /// Provide the action when view representable destroy the native view /// - Parameter action: The action to perform. The first arg is the native view. The seconds arg is the coordinator (with userInfo). /// - Returns: A view that triggers `action` when view representable destroy the native view. - public static func onViewDestroy(perform action: ((PlatformView, Coordinator) -> Void)? = nil) { + public static func onViewDestroy(perform action: ((SDAnimatedImageView, Coordinator) -> Void)? = nil) { self.viewDestroyBlock = action } } +// Convenient indicator dot syntax +extension SDWebImageIndicator where Self == SDWebImageActivityIndicator { + public static var activity: Self { Self() } +} + +extension SDWebImageIndicator where Self == SDWebImageProgressIndicator { + public static var progress: Self { Self() } +} + // Web Image convenience, based on UIKit/AppKit API -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnimatedImage { - /// Associate a placeholder when loading image with url - /// - Parameter content: A view that describes the placeholder. - /// - note: The differences between this and placeholder image, it's that placeholder image replace the image for image view, but this modify the View Hierarchy to overlay the placeholder hosting view - public func placeholder(@ViewBuilder content: () -> T) -> AnimatedImage where T : View { - #if os(macOS) - let hostingView = NSHostingView(rootView: content()) - #else - let hostingView = _UIHostingView(rootView: content()) - #endif - self.imageConfiguration.placeholderView = hostingView - return self - } - - /// Associate a placeholder image when loading image with url - /// - Parameter content: A view that describes the placeholder. - public func placeholder(_ image: PlatformImage?) -> AnimatedImage { - self.imageConfiguration.placeholder = image - return self - } - /// Associate a indicator when loading image with url /// - Note: If you do not need indicator, specify nil. Defaults to nil /// - Parameter indicator: indicator, see more in `SDWebImageIndicator` @@ -824,25 +805,8 @@ extension AnimatedImage { } } -// Indicator -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -extension AnimatedImage { - - /// Associate a indicator when loading image with url - /// - Parameter indicator: The indicator type, see `Indicator` - public func indicator(_ indicator: Indicator) -> some View where T : View { - return self.modifier(IndicatorViewModifier(reporter: self.imageLoading, indicator: indicator)) - } - - /// Associate a indicator when loading image with url, convenient method with block - /// - Parameter content: A view that describes the indicator. - public func indicator(@ViewBuilder content: @escaping (_ isAnimating: Binding, _ progress: Binding) -> T) -> some View where T : View { - return indicator(Indicator(content: content)) - } -} - #if DEBUG -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) struct AnimatedImage_Previews : PreviewProvider { static var previews: some View { Group { diff --git a/SDWebImageSwiftUI/Classes/Image.swift b/SDWebImageSwiftUI/Classes/Image.swift index 28ba9697..8978df72 100644 --- a/SDWebImageSwiftUI/Classes/Image.swift +++ b/SDWebImageSwiftUI/Classes/Image.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension Image { @inlinable init(platformImage: PlatformImage) { #if os(macOS) @@ -20,13 +20,13 @@ extension Image { } } -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension PlatformImage { static var empty = PlatformImage() } -#if os(iOS) || os(tvOS) || os(watchOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +#if !os(macOS) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension PlatformImage.Orientation { @inlinable var toSwiftUI: Image.Orientation { switch self { @@ -52,7 +52,7 @@ extension PlatformImage.Orientation { } } -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension Image.Orientation { @inlinable var toPlatform: PlatformImage.Orientation { switch self { diff --git a/SDWebImageSwiftUI/Classes/ImageManager.swift b/SDWebImageSwiftUI/Classes/ImageManager.swift index f5aaaefb..045bfa37 100644 --- a/SDWebImageSwiftUI/Classes/ImageManager.swift +++ b/SDWebImageSwiftUI/Classes/ImageManager.swift @@ -7,61 +7,85 @@ */ import SwiftUI +import Combine import SDWebImage /// A Image observable object for handle image load process. This drive the Source of Truth for image loading status. /// You can use `@ObservedObject` to associate each instance of manager to your View type, which update your view's body from SwiftUI framework when image was loaded. -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public final class ImageManager : ObservableObject { /// loaded image, note when progressive loading, this will published multiple times with different partial image - @Published public var image: PlatformImage? + public var image: PlatformImage? { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// loaded image data, may be nil if hit from memory cache. This will only published once even on incremental image loading - @Published public var imageData: Data? + public var imageData: Data? { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// loaded image cache type, .none means from network - @Published public var cacheType: SDImageCacheType = .none + public var cacheType: SDImageCacheType = .none { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// loading error, you can grab the error code and reason listed in `SDWebImageErrorDomain`, to provide a user interface about the error reason - @Published public var error: Error? - /// whether network is loading or cache is querying, should only be used for indicator binding - @Published public var isLoading: Bool = false - /// network progress, should only be used for indicator binding - @Published public var progress: Double = 0 + public var error: Error? { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// true means during incremental loading - @Published public var isIncremental: Bool = false + public var isIncremental: Bool = false { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } + /// A observed object to pass through the image manager loading status to indicator + public var indicatorStatus = IndicatorStatus() - var manager: SDWebImageManager weak var currentOperation: SDWebImageOperation? = nil - var isFirstLoad: Bool = true // false after first call `load()` - - var url: URL? - var options: SDWebImageOptions - var context: [SDWebImageContextOption : Any]? + + var currentURL: URL? + var transaction = Transaction() var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? var failureBlock: ((Error) -> Void)? var progressBlock: ((Int, Int) -> Void)? - /// Create a image manager for loading the specify url, with custom options and context. + public init() {} + + /// Start to load the url operation /// - Parameter url: The image url /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) { - self.url = url - self.options = options - self.context = context - if let manager = context?[.customManager] as? SDWebImageManager { - self.manager = manager + public func load(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) { + let manager: SDWebImageManager + if let customManager = context?[.customManager] as? SDWebImageManager { + manager = customManager } else { - self.manager = .shared + manager = .shared } - } - - /// Start to load the url operation - public func load() { - isFirstLoad = false - if currentOperation != nil { + if (currentOperation != nil && currentURL == url) { return } - self.isLoading = true + currentURL = url + self.indicatorStatus.isLoading = true + self.indicatorStatus.progress = 0 currentOperation = manager.loadImage(with: url, options: options, context: context, progress: { [weak self] (receivedSize, expectedSize, _) in + // This block may be called in non-main thread guard let self = self else { return } @@ -71,10 +95,12 @@ public final class ImageManager : ObservableObject { } else { progress = 0 } - DispatchQueue.main.async { - self.progress = progress + self.indicatorStatus.progress = progress + if let progressBlock = self.progressBlock { + DispatchQueue.main.async { + progressBlock(receivedSize, expectedSize) + } } - self.progressBlock?(receivedSize, expectedSize) }) { [weak self] (image, data, error, cacheType, finished, _) in guard let self = self else { return @@ -86,18 +112,20 @@ public final class ImageManager : ObservableObject { // So previous View struct call `onDisappear` and cancel the currentOperation return } - self.image = image - self.error = error - self.isIncremental = !finished - if finished { - self.imageData = data - self.cacheType = cacheType - self.isLoading = false - self.progress = 1 - if let image = image { - self.successBlock?(image, data, cacheType) - } else { - self.failureBlock?(error ?? NSError()) + withTransaction(self.transaction) { + self.image = image + self.error = error + self.isIncremental = !finished + if finished { + self.imageData = data + self.cacheType = cacheType + self.indicatorStatus.isLoading = false + self.indicatorStatus.progress = 1 + if let image = image { + self.successBlock?(image, data, cacheType) + } else { + self.failureBlock?(error ?? NSError()) + } } } } @@ -108,14 +136,15 @@ public final class ImageManager : ObservableObject { if let operation = currentOperation { operation.cancel() currentOperation = nil - isLoading = false } + indicatorStatus.isLoading = false + currentURL = nil } } // Completion Handler -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension ImageManager { /// Provide the action when image load fails. /// - Parameters: @@ -138,7 +167,3 @@ extension ImageManager { self.progressBlock = action } } - -// Indicator Reportor -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -extension ImageManager: IndicatorReportable {} diff --git a/SDWebImageSwiftUI/Classes/ImagePlayer.swift b/SDWebImageSwiftUI/Classes/ImagePlayer.swift index 9c1e41e0..1548744f 100644 --- a/SDWebImageSwiftUI/Classes/ImagePlayer.swift +++ b/SDWebImageSwiftUI/Classes/ImagePlayer.swift @@ -7,10 +7,11 @@ */ import SwiftUI +import Combine import SDWebImage /// A Image observable object for handle aniamted image playback. This is used to avoid `@State` update may capture the View struct type and cause memory leak. -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public final class ImagePlayer : ObservableObject { var player: SDAnimatedImagePlayer? @@ -31,7 +32,6 @@ public final class ImagePlayer : ObservableObject { deinit { player?.stopPlaying() - currentFrame = nil } /// Current playing frame image @@ -43,6 +43,8 @@ public final class ImagePlayer : ObservableObject { /// Current playing loop count @Published public var currentLoopCount: UInt = 0 + var currentAnimatedImage: (PlatformImage & SDAnimatedImageProvider)? + /// Whether current player is valid for playing. This will check the internal player exist or not public var isValid: Bool { player != nil @@ -81,17 +83,28 @@ public final class ImagePlayer : ObservableObject { /// Setup the player using Animated Image. /// After setup, you can always check `isValid` status, or call `startPlaying` to play the animation. /// - Parameter image: animated image - public func setupPlayer(animatedImage: SDAnimatedImageProvider) { + public func setupPlayer(animatedImage: PlatformImage & SDAnimatedImageProvider) { if isValid { return } + currentAnimatedImage = animatedImage if let imagePlayer = SDAnimatedImagePlayer(provider: animatedImage) { imagePlayer.animationFrameHandler = { [weak self] (index, frame) in - self?.currentFrameIndex = index - self?.currentFrame = frame + guard let self = self else { + return + } + if (self.isPlaying) { + self.currentFrameIndex = index + self.currentFrame = frame + } } imagePlayer.animationLoopHandler = { [weak self] (loopCount) in - self?.currentLoopCount = loopCount + guard let self = self else { + return + } + if (self.isPlaying) { + self.currentLoopCount = loopCount + } } // Setup configuration if let maxBufferSize = maxBufferSize { diff --git a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift index 80f936ee..49ac173b 100644 --- a/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift +++ b/SDWebImageSwiftUI/Classes/ImageViewWrapper.swift @@ -8,18 +8,25 @@ import Foundation import SDWebImage +import SwiftUI -#if os(iOS) || os(tvOS) || os(macOS) +#if !os(watchOS) /// Use wrapper to solve tne `UIImageView`/`NSImageView` frame size become image size issue (SwiftUI's Bug) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public class AnimatedImageViewWrapper : PlatformView { - var wrapped = SDAnimatedImageView() + /// The wrapped actual image view, using SDWebImage's aniamted image view + @objc dynamic public var wrapped = SDAnimatedImageView() + var observation: NSKeyValueObservation? var interpolationQuality = CGInterpolationQuality.default var shouldAntialias = false - var resizable = false + var resizingMode: Image.ResizingMode? - override public func draw(_ rect: CGRect) { + deinit { + observation?.invalidate() + } + + public override func draw(_ rect: CGRect) { #if os(macOS) guard let ctx = NSGraphicsContext.current?.cgContext else { return @@ -47,58 +54,46 @@ public class AnimatedImageViewWrapper : PlatformView { public override var intrinsicContentSize: CGSize { /// Match the behavior of SwiftUI.Image, only when image is resizable, use the super implementation to calculate size - if resizable { - return super.intrinsicContentSize + let contentSize = wrapped.intrinsicContentSize + if let _ = resizingMode { + /// Keep aspect ratio + if contentSize.width > 0 && contentSize.height > 0 { + let ratio = contentSize.width / contentSize.height + let size = CGSize(width: ratio, height: 1) + return size + } else { + return contentSize + } } else { /// Not resizable, always use image size, like SwiftUI.Image - return wrapped.intrinsicContentSize + return contentSize } } public override init(frame frameRect: CGRect) { super.init(frame: frameRect) addSubview(wrapped) + observation = observe(\.wrapped.image, options: [.new]) { [weak self] _, _ in + guard let self = self else { + return + } + self.invalidateIntrinsicContentSize() + } } public required init?(coder: NSCoder) { super.init(coder: coder) addSubview(wrapped) + observation = observe(\.wrapped.image, options: [.new]) { [weak self] _, _ in + guard let self = self else { + return + } + self.invalidateIntrinsicContentSize() + } } } -/// Use wrapper to solve the `UIProgressView`/`NSProgressIndicator` frame origin NaN crash (SwiftUI's bug) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -public class ProgressIndicatorWrapper : PlatformView { - #if os(macOS) - var wrapped = NSProgressIndicator() - #else - var wrapped = UIProgressView(progressViewStyle: .default) - #endif - - #if os(macOS) - public override func layout() { - super.layout() - wrapped.setFrameOrigin(CGPoint(x: round(self.bounds.width - wrapped.frame.width) / 2, y: round(self.bounds.height - wrapped.frame.height) / 2)) - } - #else - public override func layoutSubviews() { - super.layoutSubviews() - wrapped.center = self.center - } - #endif - - public override init(frame frameRect: CGRect) { - super.init(frame: frameRect) - addSubview(wrapped) - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - addSubview(wrapped) - } -} - -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension PlatformView { /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview. /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this. @@ -114,20 +109,6 @@ extension PlatformView { self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0).isActive = true self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0).isActive = true } - - /// Finding the HostingView for UIKit/AppKit View. - /// - Parameter entry: The entry platform view - /// - Returns: The hosting view. - func findHostingView() -> PlatformView? { - var superview = self.superview - while let s = superview { - if NSStringFromClass(type(of: s)).contains("HostingView") { - return s - } - superview = s.superview - } - return nil - } } #endif diff --git a/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift b/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift deleted file mode 100644 index 7103c77e..00000000 --- a/SDWebImageSwiftUI/Classes/Indicator/ActivityIndicator.swift +++ /dev/null @@ -1,82 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -import SwiftUI - -#if os(macOS) || os(iOS) || os(tvOS) -/// An activity indicator (system style) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -public struct ActivityIndicator: PlatformViewRepresentable { - @Binding var isAnimating: Bool - var style: Style - - /// Create the indicator with animation binding and style - /// - Parameters: - /// - isAnimating: The binding to control the animation - /// - style: The indicator style - public init(_ isAnimating: Binding, style: Style = .medium) { - self._isAnimating = isAnimating - self.style = style - } - - #if os(macOS) - public typealias NSViewType = NSProgressIndicator - #elseif os(iOS) || os(tvOS) - public typealias UIViewType = UIActivityIndicatorView - #endif - - #if os(iOS) || os(tvOS) - public func makeUIView(context: UIViewRepresentableContext) -> UIActivityIndicatorView { - let activityStyle: UIActivityIndicatorView.Style - switch style { - case .medium: - activityStyle = .medium - case .large: - activityStyle = .large - } - let indicator = UIActivityIndicatorView(style: activityStyle) - indicator.hidesWhenStopped = true - return indicator - } - - public func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext) { - isAnimating ? uiView.startAnimating() : uiView.stopAnimating() - } - #endif - - #if os(macOS) - public func makeNSView(context: NSViewRepresentableContext) -> NSProgressIndicator { - let controlSize: NSControl.ControlSize - switch style { - case .medium: - controlSize = .small - case .large: - controlSize = .regular - } - let indicator = NSProgressIndicator() - indicator.style = .spinning - indicator.controlSize = controlSize - indicator.isDisplayedWhenStopped = false - return indicator - } - - public func updateNSView(_ nsView: NSProgressIndicator, context: NSViewRepresentableContext) { - isAnimating ? nsView.startAnimation(nil) : nsView.stopAnimation(nil) - } - - #endif -} - -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -extension ActivityIndicator { - public enum Style { - case medium - case large - } -} -#endif diff --git a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift index 20d3aa76..bf6cc6b3 100644 --- a/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift +++ b/SDWebImageSwiftUI/Classes/Indicator/Indicator.swift @@ -6,11 +6,11 @@ * file that was distributed with this source code. */ -import Foundation import SwiftUI +import Combine /// A type to build the indicator -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public struct Indicator where T : View { var content: (Binding, Binding) -> T @@ -24,71 +24,86 @@ public struct Indicator where T : View { } } -/// A protocol to report indicator progress -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -public protocol IndicatorReportable : ObservableObject { +/// A observable model to report indicator loading status +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +public class IndicatorStatus : ObservableObject { /// whether indicator is loading or not - var isLoading: Bool { get set } + var isLoading: Bool = false { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } /// indicator progress, should only be used for indicator binding, value between [0.0, 1.0] - var progress: Double { get set } + var progress: Double = 0 { + didSet { + DispatchQueue.main.async { + self.objectWillChange.send() + } + } + } } /// A implementation detail View Modifier with indicator /// SwiftUI View Modifier construced by using a internal View type which modify the `body` /// It use type system to represent the view hierarchy, and Swift `some View` syntax to hide the type detail for users -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -public struct IndicatorViewModifier : ViewModifier where T : View, V : IndicatorReportable { +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +public struct IndicatorViewModifier : ViewModifier where T : View { - /// The progress reporter - @ObservedObject public var reporter: V + /// The loading status + @ObservedObject public var status: IndicatorStatus /// The indicator public var indicator: Indicator + @ViewBuilder + private var overlay: some View { + if status.isLoading { + indicator.content($status.isLoading, $status.progress) + } + } + public func body(content: Content) -> some View { ZStack { content - if reporter.isLoading { - indicator.content($reporter.isLoading, $reporter.progress) - } + overlay } } } -#if os(macOS) || os(iOS) || os(tvOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -extension Indicator where T == ActivityIndicator { +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +extension Indicator where T == AnyView { /// Activity Indicator - public static var activity: Indicator { + public static var activity: Indicator { Indicator { isAnimating, _ in - ActivityIndicator(isAnimating) + AnyView(ProgressView().opacity(isAnimating.wrappedValue ? 1 : 0)) } } /// Activity Indicator with style /// - Parameter style: style - public static func activity(style: ActivityIndicator.Style) -> Indicator { + public static func activity(style: S) -> Indicator where S: ProgressViewStyle { Indicator { isAnimating, _ in - ActivityIndicator(isAnimating, style: style) + AnyView(ProgressView().progressViewStyle(style).opacity(isAnimating.wrappedValue ? 1 : 0)) } } } -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -extension Indicator where T == ProgressIndicator { +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +extension Indicator where T == AnyView { /// Progress Indicator - public static var progress: Indicator { + public static var progress: Indicator { Indicator { isAnimating, progress in - ProgressIndicator(isAnimating, progress: progress) + AnyView(ProgressView(value: progress.wrappedValue).opacity(isAnimating.wrappedValue ? 1 : 0)) } } /// Progress Indicator with style /// - Parameter style: style - public static func progress(style: ProgressIndicator.Style) -> Indicator { + public static func progress(style: S) -> Indicator where S: ProgressViewStyle { Indicator { isAnimating, progress in - ProgressIndicator(isAnimating, progress: progress, style: style) + AnyView(ProgressView(value: progress.wrappedValue).progressViewStyle(style).opacity(isAnimating.wrappedValue ? 1 : 0)) } } } -#endif diff --git a/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift b/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift deleted file mode 100644 index 1256ee87..00000000 --- a/SDWebImageSwiftUI/Classes/Indicator/ProgressIndicator.swift +++ /dev/null @@ -1,114 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -import SwiftUI - -#if os(macOS) || os(iOS) || os(tvOS) -/// A progress bar indicator (system style) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -public struct ProgressIndicator: PlatformViewRepresentable { - @Binding var isAnimating: Bool - @Binding var progress: Double - var style: Style - - /// Create indicator with animation binding, progress binding and the style - /// - Parameters: - /// - isAnimating: The binding to control the animation - /// - progress: The binding to update the progress - /// - style: The indicator style - public init(_ isAnimating: Binding, progress: Binding, style: Style = .default) { - self._isAnimating = isAnimating - self._progress = progress - self.style = style - } - - #if os(macOS) - public typealias NSViewType = ProgressIndicatorWrapper - #elseif os(iOS) || os(tvOS) - public typealias UIViewType = ProgressIndicatorWrapper - #endif - - #if os(iOS) || os(tvOS) - public func makeUIView(context: UIViewRepresentableContext) -> ProgressIndicatorWrapper { - let progressStyle: UIProgressView.Style - switch style { - #if os(iOS) - case .bar: - progressStyle = .bar - #endif - case .default: - progressStyle = .default - } - let uiView = ProgressIndicatorWrapper() - let view = uiView.wrapped - view.progressViewStyle = progressStyle - return uiView - } - - public func updateUIView(_ uiView: ProgressIndicatorWrapper, context: UIViewRepresentableContext) { - let view = uiView.wrapped - if isAnimating { - view.setProgress(Float(progress), animated: true) - } else { - if progress == 0 { - view.isHidden = false - view.progress = 0 - } else { - view.isHidden = true - view.progress = 1 - } - } - } - #endif - - #if os(macOS) - public func makeNSView(context: NSViewRepresentableContext) -> ProgressIndicatorWrapper { - let nsView = ProgressIndicatorWrapper() - let view = nsView.wrapped - view.style = .bar - view.isDisplayedWhenStopped = false - view.controlSize = .small - view.frame = CGRect(x: 0, y: 0, width: 160, height: 0) // Width from `UIProgressView` default width - view.sizeToFit() - view.autoresizingMask = [.maxXMargin, .minXMargin, .maxYMargin, .minYMargin] - return nsView - } - - public func updateNSView(_ nsView: ProgressIndicatorWrapper, context: NSViewRepresentableContext) { - let view = nsView.wrapped - if isAnimating { - view.isIndeterminate = false - view.doubleValue = Double(progress) * 100 - view.startAnimation(nil) - } else { - if progress == 0 { - view.isHidden = false - view.isIndeterminate = true - view.doubleValue = 0 - view.stopAnimation(nil) - } else { - view.isHidden = true - view.isIndeterminate = false - view.doubleValue = 100 - view.stopAnimation(nil) - } - } - } - #endif -} - -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -extension ProgressIndicator { - public enum Style { - case `default` - #if os(iOS) - case bar - #endif - } -} -#endif diff --git a/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift b/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift index 9c0e71ad..adc40690 100644 --- a/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift +++ b/SDWebImageSwiftUI/Classes/SDWebImageSwiftUI.swift @@ -11,53 +11,53 @@ import SwiftUI @_exported import SDWebImage // Automatically import SDWebImage #if os(macOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformImage = NSImage #else -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformImage = UIImage #endif #if os(macOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformView = NSView #endif -#if os(iOS) || os(tvOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +#if os(iOS) || os(tvOS) || os(visionOS) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformView = UIView #endif #if os(watchOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformView = WKInterfaceObject #endif #if os(macOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformViewRepresentable = NSViewRepresentable #endif -#if os(iOS) || os(tvOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +#if os(iOS) || os(tvOS) || os(visionOS) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformViewRepresentable = UIViewRepresentable #endif #if os(watchOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public typealias PlatformViewRepresentable = WKInterfaceObjectRepresentable #endif #if os(macOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension NSViewRepresentable { typealias PlatformViewType = NSViewType } #endif -#if os(iOS) || os(tvOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +#if os(iOS) || os(tvOS) || os(visionOS) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension UIViewRepresentable { typealias PlatformViewType = UIViewType } #endif #if os(watchOS) -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WKInterfaceObjectRepresentable { typealias PlatformViewType = WKInterfaceObjectType } diff --git a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift b/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift deleted file mode 100644 index 6a3b9873..00000000 --- a/SDWebImageSwiftUI/Classes/SwiftUICompatibility.swift +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) DreamPiggy - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -import Foundation -import SwiftUI - -#if os(iOS) || os(tvOS) || os(macOS) - -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -struct PlatformAppear: PlatformViewRepresentable { - let appearAction: () -> Void - let disappearAction: () -> Void - - #if os(iOS) || os(tvOS) - func makeUIView(context: Context) -> some UIView { - let view = PlatformAppearView() - view.appearAction = appearAction - view.disappearAction = disappearAction - return view - } - - func updateUIView(_ uiView: UIViewType, context: Context) {} - #endif - #if os(macOS) - func makeNSView(context: Context) -> some NSView { - let view = PlatformAppearView() - view.appearAction = appearAction - view.disappearAction = disappearAction - return view - } - - func updateNSView(_ nsView: NSViewType, context: Context) {} - #endif -} - -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -class PlatformAppearView: PlatformView { - var appearAction: () -> Void = {} - var disappearAction: () -> Void = {} - - #if os(iOS) || os(tvOS) - override func willMove(toWindow newWindow: UIWindow?) { - if newWindow != nil { - appearAction() - } else { - disappearAction() - } - } - #endif - - #if os(macOS) - override func viewWillMove(toWindow newWindow: NSWindow?) { - if newWindow != nil { - appearAction() - } else { - disappearAction() - } - } - #endif -} - -#endif - -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -extension View { - /// Used UIKit/AppKit behavior to detect the SwiftUI view's visibility. - /// This hack is because of SwiftUI 1.0/2.0 buggy behavior. The built-in `onAppear` and `onDisappear` is so massive on some cases. Where UIKit/AppKit is solid. - /// - Parameters: - /// - appear: The action when view appears - /// - disappear: The action when view disappears - /// - Returns: Some view - func onPlatformAppear(appear: @escaping () -> Void = {}, disappear: @escaping () -> Void = {}) -> some View { - #if os(iOS) || os(tvOS) || os(macOS) - return self.background(PlatformAppear(appearAction: appear, disappearAction: disappear)) - #else - return self.onAppear(perform: appear).onDisappear(perform: disappear) - #endif - } -} diff --git a/SDWebImageSwiftUI/Classes/Transition/Transition.swift b/SDWebImageSwiftUI/Classes/Transition/Transition.swift index c4a908f2..fa3ca48c 100644 --- a/SDWebImageSwiftUI/Classes/Transition/Transition.swift +++ b/SDWebImageSwiftUI/Classes/Transition/Transition.swift @@ -8,7 +8,7 @@ import SwiftUI -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension AnyTransition { /// Fade-in transition diff --git a/SDWebImageSwiftUI/Classes/WebImage.swift b/SDWebImageSwiftUI/Classes/WebImage.swift index 6e0c3f34..59fe49a5 100644 --- a/SDWebImageSwiftUI/Classes/WebImage.swift +++ b/SDWebImageSwiftUI/Classes/WebImage.swift @@ -9,99 +9,178 @@ import SwiftUI import SDWebImage -/// A Image View type to load image from url. Supports static/animated image format. -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) -public struct WebImage : View { - var configurations: [(Image) -> Image] = [] - - var placeholder: AnyView? +public enum WebImagePhase { + /// No image is loaded. + case empty + + /// An image succesfully loaded. + case success(Image) + + /// An image failed to load with an error. + case failure(Error) + + /// The loaded image, if any. + /// + /// If this value isn't `nil`, the image load operation has finished, + /// and you can use the image to update the view. You can use the image + /// directly, or you can modify it in some way. For example, you can add + /// a ``Image/resizable(capInsets:resizingMode:)`` modifier to make the + /// image resizable. + public var image: Image? { + switch self { + case let .success(image): + return image + case .empty, .failure: + return nil + } + } + + /// The error that occurred when attempting to load an image, if any. + public var error: Error? { + switch self { + case .empty, .success: + return nil + case let .failure(error): + return error + } + } +} + +/// Data Binding Object, only properties in this object can support changes from user with @State and refresh +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +final class WebImageModel : ObservableObject { + /// URL image + @Published var url: URL? + @Published var options: SDWebImageOptions = [] + @Published var context: [SDWebImageContextOption : Any]? = nil +} + +/// Completion Handler Binding Object, supports dynamic @State changes +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +final class WebImageHandler: ObservableObject { + // Completion Handler + @Published var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)? + @Published var failureBlock: ((Error) -> Void)? + @Published var progressBlock: ((Int, Int) -> Void)? +} + +/// Configuration Binding Object, supports dynamic @State changes +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +final class WebImageConfiguration: ObservableObject { var retryOnAppear: Bool = true var cancelOnDisappear: Bool = true + var maxBufferSize: UInt? + var customLoopCount: UInt? + var runLoopMode: RunLoop.Mode = .common var pausable: Bool = true var purgeable: Bool = false + var playbackRate: Double = 1.0 + var playbackMode: SDAnimatedImagePlaybackMode = .normal +} + +/// A Image View type to load image from url. Supports static/animated image format. +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +public struct WebImage : View where Content: View { + var configurations: [(Image) -> Image] = [] - @ObservedObject var imageManager: ImageManager + var content: (WebImagePhase) -> Content /// A Binding to control the animation. You can bind external logic to control the animation status. /// True to start animation, false to stop animation. @Binding public var isAnimating: Bool - @ObservedObject var imagePlayer: ImagePlayer + /// A observed object to pass through the image model to manager + @ObservedObject var imageModel: WebImageModel - /// Create a web image with url, placeholder, custom options and context. - /// - Parameter url: The image url - /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. - /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil) { - self.init(url: url, options: options, context: context, isAnimating: .constant(true)) - } + /// A observed object to pass through the image handler to manager + @ObservedObject var imageHandler = WebImageHandler() + + /// A observed object to pass through the image configuration to player + @ObservedObject var imageConfiguration = WebImageConfiguration() + + @ObservedObject var indicatorStatus : IndicatorStatus + + @StateObject var imagePlayer = ImagePlayer() + + @StateObject var imageManager : ImageManager /// Create a web image with url, placeholder, custom options and context. Optional can support animated image using Binding. /// - Parameter url: The image url + /// - Parameter scale: The scale to use for the image. The default is 1. Set a different value when loading images designed for higher resolution displays. For example, set a value of 2 for an image that you would name with the @2x suffix if stored in a file on disk. /// - Parameter options: The options to use when downloading the image. See `SDWebImageOptions` for the possible values. /// - Parameter context: A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold. - /// - Parameter isAnimating: The binding for animation control. The binding value should be `true` when initialized to setup the correct animated image class. If not, you must provide the `.animatedImageClass` explicitly. When the animation started, this binding can been used to start / stop the animation. - public init(url: URL?, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding) { + /// - Parameter isAnimating: The binding for animation control. When the animation started, this binding can been used to start / stop the animation. You can still customize the `.animatedImageClass` context for advanced custom animation. + public init(url: URL?, scale: CGFloat = 1, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true)) where Content == Image { + self.init(url: url, options: options, context: context, isAnimating: isAnimating) { phase in + phase.image ?? Image(platformImage: .empty) + } + } + + public init(url: URL?, scale: CGFloat = 1, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), @ViewBuilder content: @escaping (Image) -> I, @ViewBuilder placeholder: @escaping () -> P) where Content == _ConditionalContent, I: View, P: View { + self.init(url: url, options: options, context: context, isAnimating: isAnimating) { phase in + if let i = phase.image { + content(i) + } else { + placeholder() + } + } + } + + public init(url: URL?, scale: CGFloat = 1, options: SDWebImageOptions = [], context: [SDWebImageContextOption : Any]? = nil, isAnimating: Binding = .constant(true), transaction: Transaction = Transaction(), @ViewBuilder content: @escaping (WebImagePhase) -> Content) { self._isAnimating = isAnimating var context = context ?? [:] - // provide animated image class if the initialized `isAnimating` is true, user can still custom the image class if they want - if isAnimating.wrappedValue { - if context[.animatedImageClass] == nil { - context[.animatedImageClass] = SDAnimatedImage.self - } + if context[.imageScaleFactor] == nil { + context[.imageScaleFactor] = scale + } + // always provide animated image class to allows dynamic control + // since most cases, SDAnimatedImage should be compatible with UIImage + // user can still custom the image class if they want + if context[.animatedImageClass] == nil { + context[.animatedImageClass] = SDAnimatedImage.self + } + let imageModel = WebImageModel() + imageModel.url = url + imageModel.options = options + imageModel.context = context + _imageModel = ObservedObject(wrappedValue: imageModel) + let imageManager = ImageManager() + imageManager.transaction = transaction + _imageManager = StateObject(wrappedValue: imageManager) + _indicatorStatus = ObservedObject(wrappedValue: imageManager.indicatorStatus) + + self.content = { phase in + content(phase) } - self.imageManager = ImageManager(url: url, options: options, context: context) - self.imagePlayer = ImagePlayer() } public var body: some View { - // This solve the case when WebImage created with new URL, but `onAppear` not been called, for example, some transaction indeterminate state, SwiftUI :) - if imageManager.isFirstLoad { - imageManager.load() - } - return Group { - if let image = imageManager.image { + // Container + return ZStack { + // Render Logic for actual animated image frame or static image + if imageManager.image != nil && imageModel.url == imageManager.currentURL { if isAnimating && !imageManager.isIncremental { setupPlayer() - .onPlatformAppear(appear: { - self.imagePlayer.startPlaying() - }, disappear: { - if self.pausable { - self.imagePlayer.pausePlaying() - } else { - self.imagePlayer.stopPlaying() - } - if self.purgeable { - self.imagePlayer.clearFrameBuffer() - } - }) } else { - if let currentFrame = imagePlayer.currentFrame { - configure(image: currentFrame) - } else { - configure(image: image) - } + displayImage() } } else { - setupPlaceholder() - .onPlatformAppear(appear: { - // Load remote image when first appear - if self.imageManager.isFirstLoad { - self.imageManager.load() - return - } - guard self.retryOnAppear else { return } + content((imageManager.error != nil) ? .failure(imageManager.error!) : .empty) + setupInitialState() + // Load Logic + .onAppear { + guard self.imageConfiguration.retryOnAppear else { return } // When using prorgessive loading, the new partial image will cause onAppear. Filter this case - if self.imageManager.image == nil && !self.imageManager.isIncremental { - self.imageManager.load() + if self.imageManager.error != nil && !self.imageManager.isIncremental { + self.imageManager.load(url: imageModel.url, options: imageModel.options, context: imageModel.context) } - }, disappear: { - guard self.cancelOnDisappear else { return } + } + .onDisappear { + guard self.imageConfiguration.cancelOnDisappear else { return } // When using prorgessive loading, the previous partial image will cause onDisappear. Filter this case - if self.imageManager.image == nil && !self.imageManager.isIncremental { + if self.imageManager.error != nil && !self.imageManager.isIncremental { self.imageManager.cancel() } - }) + } } } } @@ -109,7 +188,7 @@ public struct WebImage : View { /// Configure the platform image into the SwiftUI rendering image func configure(image: PlatformImage) -> some View { // Actual rendering SwiftUI image - let result: Image + var result: Image // NSImage works well with SwiftUI, include Vector and EXIF images. #if os(macOS) result = Image(nsImage: image) @@ -122,9 +201,11 @@ public struct WebImage : View { // ensure CGImage is nil if image.cgImage == nil { // draw vector into bitmap with the screen scale (behavior like AppKit) - #if os(iOS) || os(tvOS) + #if os(visionOS) + let scale = UITraitCollection.current.displayScale + #elseif os(iOS) || os(tvOS) || os(visionOS) let scale = UIScreen.main.scale - #else + #elseif os(watchOS) let scale = WKInterfaceDevice.current().screenScale #endif UIGraphicsBeginImageContextWithOptions(image.size, false, scale) @@ -150,42 +231,122 @@ public struct WebImage : View { // Should not use `EmptyView`, which does not respect to the container's frame modifier // Using a empty image instead for better compatible - return configurations.reduce(result) { (previous, configuration) in + let i = configurations.reduce(result) { (previous, configuration) in configuration(previous) } + + // Apply view builder + return content(.success(i)) + } + + /// Image Manager status + func setupManager() { + self.imageManager.successBlock = self.imageHandler.successBlock + self.imageManager.failureBlock = self.imageHandler.failureBlock + self.imageManager.progressBlock = self.imageHandler.progressBlock + if imageModel.url != imageManager.currentURL { + imageManager.cancel() + imageManager.image = nil + imageManager.imageData = nil + imageManager.cacheType = .none + imageManager.error = nil + imageManager.isIncremental = false + imageManager.indicatorStatus.isLoading = false + imageManager.indicatorStatus.progress = 0 + } + } + + /// Container level to resume animation when appear + func appearAction() { + self.imagePlayer.startPlaying() + } + + /// Container level to stop animation when disappear + func disappearAction() { + if self.imageConfiguration.pausable { + self.imagePlayer.pausePlaying() + } else { + self.imagePlayer.stopPlaying() + } + if self.imageConfiguration.purgeable { + self.imagePlayer.clearFrameBuffer() + } + } + + /// Static Image Display + func displayImage() -> some View { + disappearAction() + if let currentFrame = imagePlayer.currentFrame { + return configure(image: currentFrame) + } else { + return configure(image: imageManager.image!) + } } /// Animated Image Support func setupPlayer() -> some View { - if let currentFrame = imagePlayer.currentFrame { + let shouldResetPlayer: Bool + // Image compare should use ===/!==, which is faster than isEqual: + if let animatedImage = imagePlayer.currentAnimatedImage, animatedImage !== imageManager.image! { + shouldResetPlayer = true + } else { + shouldResetPlayer = false + } + if !shouldResetPlayer { + imagePlayer.startPlaying() + } + if let currentFrame = imagePlayer.currentFrame, !shouldResetPlayer { + // Bind frame index to ID to ensure onDisappear called with sync return configure(image: currentFrame) + .id("\(imageModel.url!):\(imagePlayer.currentFrameIndex)") + .onAppear {} } else { - if let animatedImage = imageManager.image as? SDAnimatedImageProvider { - self.imagePlayer.setupPlayer(animatedImage: animatedImage) - self.imagePlayer.startPlaying() - } return configure(image: imageManager.image!) + .id("\(imageModel.url!):\(imagePlayer.currentFrameIndex)") + .onAppear { + if shouldResetPlayer { + // Clear previous status + self.imagePlayer.stopPlaying() + self.imagePlayer.player = nil + self.imagePlayer.currentFrame = nil; + self.imagePlayer.currentFrameIndex = 0; + self.imagePlayer.currentLoopCount = 0; + } + if let animatedImage = imageManager.image as? PlatformImage & SDAnimatedImageProvider { + self.imagePlayer.customLoopCount = self.imageConfiguration.customLoopCount + self.imagePlayer.maxBufferSize = self.imageConfiguration.maxBufferSize + self.imagePlayer.runLoopMode = self.imageConfiguration.runLoopMode + self.imagePlayer.playbackMode = self.imageConfiguration.playbackMode + self.imagePlayer.playbackRate = self.imageConfiguration.playbackRate + // Setup new player + self.imagePlayer.setupPlayer(animatedImage: animatedImage) + self.imagePlayer.startPlaying() + } + } + } + } + + /// Initial state management (update when imageModel.url changed) + func setupInitialState() -> some View { + self.setupManager() + if (self.imageManager.error == nil) { + // Load remote image when first appear + self.imageManager.load(url: imageModel.url, options: imageModel.options, context: imageModel.context) } + return setupPlaceholder() } /// Placeholder View Support func setupPlaceholder() -> some View { - // Don't use `Group` because it will trigger `.onAppear` and `.onDisappear` when condition view removed, treat placeholder as an entire component - if let placeholder = placeholder { - // If use `.delayPlaceholder`, the placeholder is applied after loading failed, hide during loading :) - if imageManager.options.contains(.delayPlaceholder) && imageManager.isLoading { - return AnyView(configure(image: .empty)) - } else { - return placeholder - } - } else { - return AnyView(configure(image: .empty)) - } + let result = content((imageManager.error != nil) ? .failure(imageManager.error!) : .empty) + // Custom ID to avoid SwiftUI engine cache the status, and does not call `onAppear` when placeholder not changed (See `ContentView.swift/ContentView2` case) + // Because we load the image url in placeholder's `onAppear`, it should be called to sync with state changes :) + return result.id(imageModel.url) } } // Layout -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { func configure(_ block: @escaping (Image) -> Image) -> WebImage { var result = self @@ -223,7 +384,7 @@ extension WebImage { } // Completion Handler -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Provide the action when image load fails. @@ -231,7 +392,7 @@ extension WebImage { /// - action: The action to perform. The first arg is the error during loading. If `action` is `nil`, the call has no effect. /// - Returns: A view that triggers `action` when this image load fails. public func onFailure(perform action: ((Error) -> Void)? = nil) -> WebImage { - self.imageManager.failureBlock = action + self.imageHandler.failureBlock = action return self } @@ -240,7 +401,7 @@ extension WebImage { /// - action: The action to perform. The first arg is the loaded image, the second arg is the loaded image data, the third arg is the cache type loaded from. If `action` is `nil`, the call has no effect. /// - Returns: A view that triggers `action` when this image load successes. public func onSuccess(perform action: ((PlatformImage, Data?, SDImageCacheType) -> Void)? = nil) -> WebImage { - self.imageManager.successBlock = action + self.imageHandler.successBlock = action return self } @@ -249,60 +410,37 @@ extension WebImage { /// - action: The action to perform. The first arg is the received size, the second arg is the total size, all in bytes. If `action` is `nil`, the call has no effect. /// - Returns: A view that triggers `action` when this image load successes. public func onProgress(perform action: ((Int, Int) -> Void)? = nil) -> WebImage { - self.imageManager.progressBlock = action + self.imageHandler.progressBlock = action return self } } // WebImage Modifier -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { - - /// Associate a placeholder when loading image with url - /// - note: The differences between Placeholder and Indicator, is that placeholder does not supports animation, and return type is different - /// - Parameter content: A view that describes the placeholder. - public func placeholder(@ViewBuilder content: () -> T) -> WebImage where T : View { - var result = self - result.placeholder = AnyView(content()) - return result - } - - /// Associate a placeholder image when loading image with url - /// - note: This placeholder image will apply the same size and resizable from WebImage for convenience. If you don't want this, use the ViewBuilder one above instead - /// - Parameter image: A Image view that describes the placeholder. - public func placeholder(_ image: Image) -> WebImage { - return placeholder { - configurations.reduce(image) { (previous, configuration) in - configuration(previous) - } - } - } - /// Control the behavior to retry the failed loading when view become appears again /// - Parameter flag: Whether or not to retry the failed loading public func retryOnAppear(_ flag: Bool) -> WebImage { - var result = self - result.retryOnAppear = flag - return result + self.imageConfiguration.retryOnAppear = flag + return self } /// Control the behavior to cancel the pending loading when view become disappear again /// - Parameter flag: Whether or not to cancel the pending loading public func cancelOnDisappear(_ flag: Bool) -> WebImage { - var result = self - result.cancelOnDisappear = flag - return result + self.imageConfiguration.cancelOnDisappear = flag + return self } } // Indicator -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Associate a indicator when loading image with url /// - Parameter indicator: The indicator type, see `Indicator` public func indicator(_ indicator: Indicator) -> some View where T : View { - return self.modifier(IndicatorViewModifier(reporter: imageManager, indicator: indicator)) + return self.modifier(IndicatorViewModifier(status: indicatorStatus, indicator: indicator)) } /// Associate a indicator when loading image with url, convenient method with block @@ -313,14 +451,14 @@ extension WebImage { } // Animated Image -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension WebImage { /// Total loop count for animated image rendering. Defaults to nil. /// - Note: Pass nil to disable customization, use the image itself loop count (`animatedImageLoopCount`) instead /// - Parameter loopCount: The animation loop count public func customLoopCount(_ loopCount: UInt?) -> WebImage { - self.imagePlayer.customLoopCount = loopCount + self.imageConfiguration.customLoopCount = loopCount return self } @@ -331,7 +469,7 @@ extension WebImage { /// `UInt.max` means cache all the buffer. (Lowest CPU and Highest Memory) /// - Parameter bufferSize: The max buffer size public func maxBufferSize(_ bufferSize: UInt?) -> WebImage { - self.imagePlayer.maxBufferSize = bufferSize + self.imageConfiguration.maxBufferSize = bufferSize return self } @@ -340,7 +478,7 @@ extension WebImage { /// - Note: This is useful for some cases, for example, always specify NSDefaultRunLoopMode, if you want to pause the animation when user scroll (for Mac user, drag the mouse or touchpad) /// - Parameter runLoopMode: The runLoopMode for animation public func runLoopMode(_ runLoopMode: RunLoop.Mode) -> WebImage { - self.imagePlayer.runLoopMode = runLoopMode + self.imageConfiguration.runLoopMode = runLoopMode return self } @@ -348,18 +486,16 @@ extension WebImage { /// - Note: For some of use case, you may want to reset the frame index to 0 when stop, but some other want to keep the current frame index. /// - Parameter pausable: Whether or not to pause the animation instead of stop the animation. public func pausable(_ pausable: Bool) -> WebImage { - var result = self - result.pausable = pausable - return result + self.imageConfiguration.pausable = pausable + return self } /// Whether or not to clear frame buffer cache when stopped. Defaults is false. /// Note: This is useful when you want to limit the memory usage during frequently visibility changes (such as image view inside a list view, then push and pop) /// - Parameter purgeable: Whether or not to clear frame buffer cache when stopped. public func purgeable(_ purgeable: Bool) -> WebImage { - var result = self - result.purgeable = purgeable - return result + self.imageConfiguration.purgeable = purgeable + return self } /// Control the animation playback rate. Default is 1.0. @@ -370,20 +506,20 @@ extension WebImage { /// `< 0.0` is not supported currently and stop animation. (may support reverse playback in the future) /// - Parameter playbackRate: The animation playback rate. public func playbackRate(_ playbackRate: Double) -> WebImage { - self.imagePlayer.playbackRate = playbackRate + self.imageConfiguration.playbackRate = playbackRate return self } /// Control the animation playback mode. Default is .normal /// - Parameter playbackMode: The playback mode, including normal order, reverse order, bounce order and reversed bounce order. public func playbackMode(_ playbackMode: SDAnimatedImagePlaybackMode) -> WebImage { - self.imagePlayer.playbackMode = playbackMode + self.imageConfiguration.playbackMode = playbackMode return self } } #if DEBUG -@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) +@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) struct WebImage_Previews : PreviewProvider { static var previews: some View { Group { diff --git a/SDWebImageSwiftUI/Module/Info.plist b/SDWebImageSwiftUI/Module/Info.plist index 7a13a88f..df2e4bee 100644 --- a/SDWebImageSwiftUI/Module/Info.plist +++ b/SDWebImageSwiftUI/Module/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.0.2 + 3.1.3 CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/SDWebImageSwiftUI/Resources/PrivacyInfo.xcprivacy b/SDWebImageSwiftUI/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..df2b2442 --- /dev/null +++ b/SDWebImageSwiftUI/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyTracking + + NSPrivacyAccessedAPITypes + + NSPrivacyCollectedDataTypes + + NSPrivacyTrackingDomains + + + \ No newline at end of file diff --git a/Tests/AnimatedImageTests.swift b/Tests/AnimatedImageTests.swift index 60091848..613539e8 100644 --- a/Tests/AnimatedImageTests.swift +++ b/Tests/AnimatedImageTests.swift @@ -142,7 +142,9 @@ class AnimatedImageTests: XCTestCase { func testAnimatedImageModifier() throws { let expectation = self.expectation(description: "WebImage modifier") let imageUrl = URL(string: "https://assets.sbnation.com/assets/2512203/dogflops.gif") - let imageView = AnimatedImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1]) + let imageView = AnimatedImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1]) { + Circle() + } let introspectView = imageView .onSuccess { _, _, _ in expectation.fulfill() @@ -161,11 +163,7 @@ class AnimatedImageTests: XCTestCase { XCTAssert(view.isKind(of: SDAnimatedImageView.self)) XCTAssertEqual(context.coordinator.userInfo?["foo"] as? String, "bar") } - .placeholder(PlatformImage()) - .placeholder { - Circle() - } - .indicator(SDWebImageActivityIndicator.medium) + .indicator(.activity) // Image .resizable() .renderingMode(.original) @@ -182,7 +180,7 @@ class AnimatedImageTests: XCTestCase { .animation(.easeInOut) _ = try introspectView.inspect() ViewHosting.host(view: introspectView) - self.waitForExpectations(timeout: 5, handler: nil) + self.waitForExpectations(timeout: 10, handler: nil) ViewHosting.expel() } } diff --git a/Tests/ImageManagerTests.swift b/Tests/ImageManagerTests.swift index e5d4a099..8222f5b2 100644 --- a/Tests/ImageManagerTests.swift +++ b/Tests/ImageManagerTests.swift @@ -17,8 +17,8 @@ class ImageManagerTests: XCTestCase { func testImageManager() throws { let expectation = self.expectation(description: "ImageManager usage with Combine") - let imageUrl = URL(string: "https://via.placeholder.com/500x500.jpg") - let imageManager = ImageManager(url: imageUrl) + let imageUrl = URL(string: "https://placehold.co/500x500.jpg") + let imageManager = ImageManager() imageManager.setOnSuccess { image, cacheType, data in XCTAssertNotNil(image) expectation.fulfill() @@ -29,7 +29,7 @@ class ImageManagerTests: XCTestCase { imageManager.setOnProgress { receivedSize, expectedSize in } - imageManager.load() + imageManager.load(url: imageUrl) XCTAssertNotNil(imageManager.currentOperation) let sub = imageManager.objectWillChange .subscribe(on: RunLoop.main) @@ -38,6 +38,6 @@ class ImageManagerTests: XCTestCase { print(value) } sub.cancel() - self.waitForExpectations(timeout: 5, handler: nil) + self.waitForExpectations(timeout: 10, handler: nil) } } diff --git a/Tests/IndicatorTests.swift b/Tests/IndicatorTests.swift deleted file mode 100644 index cef4ae4c..00000000 --- a/Tests/IndicatorTests.swift +++ /dev/null @@ -1,84 +0,0 @@ -import XCTest -import SwiftUI -import ViewInspector -@testable import SDWebImageSwiftUI - -extension ActivityIndicator : Inspectable {} -extension ProgressIndicator : Inspectable {} - -#if os(iOS) || os(tvOS) -typealias ActivityIndicatorViewType = UIActivityIndicatorView -typealias ProgressIndicatorViewType = UIProgressView -#else -typealias ActivityIndicatorViewType = NSProgressIndicator -typealias ProgressIndicatorViewType = NSProgressIndicator -#endif - -class IndicatorTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - SDImageCache.shared.clear(with: .all) - } - - func testActivityIndicator() throws { - let expectation = self.expectation(description: "Activity indicator") - let binding = Binding(wrappedValue: true) - let indicator = ActivityIndicator(binding, style: .medium) - ViewHosting.host(view: indicator) - let indicatorView = try indicator.inspect().actualView().platformView() - #if os(iOS) || os(tvOS) - XCTAssertTrue(indicatorView.isAnimating) - #endif - binding.wrappedValue = false - XCTAssertFalse(binding.wrappedValue) - XCTAssertFalse(indicator.isAnimating) - #if os(iOS) || os(tvOS) - indicatorView.stopAnimating() - #else - indicatorView.stopAnimation(nil) - #endif - #if os(iOS) || os(tvOS) - XCTAssertFalse(indicatorView.isAnimating) - #endif - expectation.fulfill() - self.waitForExpectations(timeout: 5, handler: nil) - ViewHosting.expel() - } - - func testProgressIndicator() throws { - let expectation = self.expectation(description: "Progress indicator") - let binding = Binding(wrappedValue: true) - let progress = Binding(wrappedValue: 0) - let indicator = ProgressIndicator(binding, progress: progress) - ViewHosting.host(view: indicator) - let indicatorView = try indicator.inspect().actualView().platformView().wrapped - #if os(iOS) || os(tvOS) - XCTAssertEqual(indicatorView.progress, 0.0) - #else - XCTAssertEqual(indicatorView.doubleValue, 0.0) - #endif - progress.wrappedValue = 1.0 - XCTAssertEqual(indicator.progress, 1.0) - #if os(iOS) || os(tvOS) - indicatorView.setProgress(1.0, animated: true) - #else - indicatorView.increment(by: 1.0) - #endif - #if os(iOS) || os(tvOS) - XCTAssertEqual(indicatorView.progress, 1.0) - #else - XCTAssertEqual(indicatorView.doubleValue, 1.0) - #endif - expectation.fulfill() - self.waitForExpectations(timeout: 5, handler: nil) - ViewHosting.expel() - } - -} diff --git a/Tests/WebImageTests.swift b/Tests/WebImageTests.swift index 1ba29b2a..2270dfa6 100644 --- a/Tests/WebImageTests.swift +++ b/Tests/WebImageTests.swift @@ -23,9 +23,9 @@ class WebImageTests: XCTestCase { let imageView = WebImage(url: imageUrl) let introspectView = imageView.onSuccess { image, data, cacheType in #if os(macOS) - let displayImage = try? imageView.inspect().group().image(0).nsImage() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().nsImage() #else - let displayImage = try? imageView.inspect().group().image(0).cgImage() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().cgImage() #endif XCTAssertNotNil(displayImage) expectation.fulfill() @@ -47,10 +47,10 @@ class WebImageTests: XCTestCase { if let animatedImage = image as? SDAnimatedImage { XCTAssertTrue(imageView.isAnimating) #if os(macOS) - let displayImage = try? imageView.inspect().group().image(0).nsImage() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().nsImage() let size = displayImage?.size #else - let displayImage = try? imageView.inspect().group().image(0).cgImage() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().cgImage() let size = CGSize(width: displayImage?.width ?? 0, height: displayImage?.height ?? 0) #endif XCTAssertNotNil(displayImage) @@ -73,7 +73,11 @@ class WebImageTests: XCTestCase { func testWebImageModifier() throws { let expectation = self.expectation(description: "WebImage modifier") let imageUrl = URL(string: "https://raw.githubusercontent.com/ibireme/YYImage/master/Demo/YYImageDemo/mew_baseline.jpg") - let imageView = WebImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1]) + let imageView = WebImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1]) { image in + image.resizable() + } placeholder: { + Circle() + } let introspectView = imageView .onSuccess { _, _, _ in expectation.fulfill() @@ -83,10 +87,6 @@ class WebImageTests: XCTestCase { } .onProgress { _, _ in - } - .placeholder(.init(platformImage: PlatformImage())) - .placeholder { - Circle() } // Image .resizable() @@ -137,7 +137,7 @@ class WebImageTests: XCTestCase { func testWebImageOnSuccessWhenCacheMiss() throws { let expectation = self.expectation(description: "WebImage onSuccess when cache miss") - let imageUrl = URL(string: "http://via.placeholder.com/100x100.png") + let imageUrl = URL(string: "https://placehold.co/100x100.png") let cacheKey = SDWebImageManager.shared.cacheKey(for: imageUrl) SDImageCache.shared.removeImageFromMemory(forKey: cacheKey) SDImageCache.shared.removeImageFromDisk(forKey: cacheKey) @@ -161,11 +161,11 @@ class WebImageTests: XCTestCase { let imageView = WebImage(url: imageUrl) let introspectView = imageView.onSuccess { image, data, cacheType in #if os(macOS) - let displayImage = try? imageView.inspect().group().image(0).nsImage() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().nsImage() XCTAssertNotNil(displayImage) #else - let displayImage = try? imageView.inspect().group().image(0).cgImage() - let orientation = try? imageView.inspect().group().image(0).orientation() + let displayImage = try? imageView.inspect().zStack().image(0).actualImage().cgImage() + let orientation = try? imageView.inspect().zStack().image(0).actualImage().orientation() XCTAssertNotNil(displayImage) XCTAssertEqual(orientation, .leftMirrored) #endif diff --git a/carthage.sh b/carthage.sh index 4e004da4..04be82b2 100755 --- a/carthage.sh +++ b/carthage.sh @@ -2,18 +2,16 @@ # Usage example: ./carthage.sh build --platform iOS set -euo pipefail - + xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX) trap 'rm -f "$xcconfig"' INT TERM HUP EXIT - + # For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise # the build will fail on lipo due to duplicate architectures. -CURRENT_XCODE_VERSION=$(xcodebuild -version | grep "Build version" | cut -d' ' -f3) -echo "EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$CURRENT_XCODE_VERSION = arm64 arm64e armv7 armv7s armv6 armv8" >> $xcconfig - -echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$(XCODE_PRODUCT_BUILD_VERSION))' >> $xcconfig -echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig +echo 'EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64 arm64e armv7 armv7s armv6 armv8' >> $xcconfig +echo 'EXCLUDED_ARCHS[sdk=appletvsimulator*] = arm64 arm64e armv7 armv7s armv6 armv8' >> $xcconfig +echo 'EXCLUDED_ARCHS[sdk=watchsimulator*] = arm64 arm64e armv7 armv7s armv6 armv8' >> $xcconfig export XCODE_XCCONFIG_FILE="$xcconfig" carthage "$@" \ No newline at end of file