diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8da09972..e727f14a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,6 +74,23 @@ jobs: - name: Build Project Integration run: pushd Examples/ExampleMultiProjectIntegration; xcrun xcodebuild build -skipPackagePluginValidation -skipMacroValidation -scheme ExampleMultiProjectIntegration; popd + pod-project-integration: + name: Build CocoaPods Integration on Xcode 16 + runs-on: macos-15 + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3.5' + bundler-cache: true + - name: Select Xcode Version + run: sudo xcode-select --switch /Applications/Xcode_16.2.0.app/Contents/Developer + - name: Install Pod + run: bundle exec pod install --project-directory=Examples/ExampleCocoaPodsIntegration + - name: Build CocoaPods Integration + run: xcrun xcodebuild build -scheme ExampleCocoaPodsIntegration -configuration Debug -workspace Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration.xcworkspace -destination 'platform=iOS Simulator,OS=18.2,name=iPad (10th generation)' # Explicitly test the Debug build. Our pod lint jobs are already testing the Release build. + spm: name: Build and Test on Xcode 16 runs-on: macos-15 @@ -129,7 +146,7 @@ jobs: sudo xcodebuild -downloadPlatform visionOS sudo xcodebuild -runFirstLaunch - name: Lint Podspec - run: bundle exec pod lib lint --verbose --fail-fast --swift-version=6.0 --allow-warnings --platforms=${{ matrix.platforms }} # We can stop allowing warnings once ZippyJSON creates a release that contains https://github.com/michaeleisel/ZippyJSON/pull/67 + run: bundle exec pod lib lint --verbose --fail-fast --swift-version=6.0 --platforms=${{ matrix.platforms }} linux: name: Build and Test on Linux diff --git a/Examples/ExampleCocoaPodsIntegration/.gitignore b/Examples/ExampleCocoaPodsIntegration/.gitignore new file mode 100644 index 00000000..3a273ae8 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/.gitignore @@ -0,0 +1,4 @@ +ExampleCocoaPodsIntegration/SafeDI.swift +ExampleCocoaPodsIntegration.xcodeproj/xcuserdata +ExampleCocoaPodsIntegration.xcworkspace/ +Pods/ diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration.xcodeproj/project.pbxproj b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration.xcodeproj/project.pbxproj new file mode 100644 index 00000000..62828afe --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration.xcodeproj/project.pbxproj @@ -0,0 +1,402 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 001683DA816535E0BA21C523 /* libPods-ExampleCocoaPodsIntegration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA03B1B1D70965940D873662 /* libPods-ExampleCocoaPodsIntegration.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 286F3DB0181A38EBFB5A11C2 /* Pods-ExampleCocoaPodsIntegration.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExampleCocoaPodsIntegration.release.xcconfig"; path = "../../ExampleCocoapodsIntegration/Pods/Target Support Files/Pods-ExampleCocoaPodsIntegration/Pods-ExampleCocoaPodsIntegration.release.xcconfig"; sourceTree = ""; }; + 324DF6F62D27BD28007A980D /* ExampleCocoaPodsIntegration.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleCocoaPodsIntegration.app; sourceTree = BUILT_PRODUCTS_DIR; }; + AA03B1B1D70965940D873662 /* libPods-ExampleCocoaPodsIntegration.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExampleCocoaPodsIntegration.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + D4FD8A2B660A7F91670D20AC /* Pods-ExampleCocoaPodsIntegration.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExampleCocoaPodsIntegration.debug.xcconfig"; path = "../../ExampleCocoapodsIntegration/Pods/Target Support Files/Pods-ExampleCocoaPodsIntegration/Pods-ExampleCocoaPodsIntegration.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 324DF6F82D27BD28007A980D /* ExampleCocoaPodsIntegration */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = ExampleCocoaPodsIntegration; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 324DF6F32D27BD28007A980D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 001683DA816535E0BA21C523 /* libPods-ExampleCocoaPodsIntegration.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 324DF6ED2D27BD28007A980D = { + isa = PBXGroup; + children = ( + 324DF6F82D27BD28007A980D /* ExampleCocoaPodsIntegration */, + 324DF6F72D27BD28007A980D /* Products */, + B5A0477003AF79C3877E5921 /* Pods */, + E49C4DEEF8009D272E214E10 /* Frameworks */, + ); + sourceTree = ""; + }; + 324DF6F72D27BD28007A980D /* Products */ = { + isa = PBXGroup; + children = ( + 324DF6F62D27BD28007A980D /* ExampleCocoaPodsIntegration.app */, + ); + name = Products; + sourceTree = ""; + }; + B5A0477003AF79C3877E5921 /* Pods */ = { + isa = PBXGroup; + children = ( + D4FD8A2B660A7F91670D20AC /* Pods-ExampleCocoaPodsIntegration.debug.xcconfig */, + 286F3DB0181A38EBFB5A11C2 /* Pods-ExampleCocoaPodsIntegration.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + E49C4DEEF8009D272E214E10 /* Frameworks */ = { + isa = PBXGroup; + children = ( + AA03B1B1D70965940D873662 /* libPods-ExampleCocoaPodsIntegration.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 324DF6F52D27BD28007A980D /* ExampleCocoaPodsIntegration */ = { + isa = PBXNativeTarget; + buildConfigurationList = 324DF7042D27BD29007A980D /* Build configuration list for PBXNativeTarget "ExampleCocoaPodsIntegration" */; + buildPhases = ( + 436E472576A4DD3983B97EFD /* [CP] Check Pods Manifest.lock */, + 324DF7102D27BDED007A980D /* Run SafeDITool */, + 324DF6F22D27BD28007A980D /* Sources */, + 324DF6F32D27BD28007A980D /* Frameworks */, + 324DF6F42D27BD28007A980D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 324DF6F82D27BD28007A980D /* ExampleCocoaPodsIntegration */, + ); + name = ExampleCocoaPodsIntegration; + productName = ExampleCocoaPodsIntegration; + productReference = 324DF6F62D27BD28007A980D /* ExampleCocoaPodsIntegration.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 324DF6EE2D27BD28007A980D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1620; + LastUpgradeCheck = 1620; + TargetAttributes = { + 324DF6F52D27BD28007A980D = { + CreatedOnToolsVersion = 16.2; + }; + }; + }; + buildConfigurationList = 324DF6F12D27BD28007A980D /* Build configuration list for PBXProject "ExampleCocoaPodsIntegration" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 324DF6ED2D27BD28007A980D; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 324DF6F72D27BD28007A980D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 324DF6F52D27BD28007A980D /* ExampleCocoaPodsIntegration */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 324DF6F42D27BD28007A980D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 324DF7102D27BDED007A980D /* Run SafeDITool */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run SafeDITool"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "$PROJECT_DIR/safeditool.sh\n"; + }; + 436E472576A4DD3983B97EFD /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + 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-ExampleCocoaPodsIntegration-checkManifestLockResult.txt", + ); + 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 */ + 324DF6F22D27BD28007A980D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 324DF7022D27BD29007A980D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 324DF7032D27BD29007A980D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 324DF7052D27BD29007A980D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D4FD8A2B660A7F91670D20AC /* Pods-ExampleCocoaPodsIntegration.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ExampleCocoaPodsIntegration/Preview Content\""; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.dfed.safedi.ExampleCocoaPodsIntegration; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 324DF7062D27BD29007A980D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 286F3DB0181A38EBFB5A11C2 /* Pods-ExampleCocoaPodsIntegration.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ExampleCocoaPodsIntegration/Preview Content\""; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.dfed.safedi.ExampleCocoaPodsIntegration; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 324DF6F12D27BD28007A980D /* Build configuration list for PBXProject "ExampleCocoaPodsIntegration" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 324DF7022D27BD29007A980D /* Debug */, + 324DF7032D27BD29007A980D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 324DF7042D27BD29007A980D /* Build configuration list for PBXNativeTarget "ExampleCocoaPodsIntegration" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 324DF7052D27BD29007A980D /* Debug */, + 324DF7062D27BD29007A980D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 324DF6EE2D27BD28007A980D /* Project object */; +} diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..23058801 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Assets.xcassets/Contents.json b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/AnyObservableObject.swift b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/AnyObservableObject.swift new file mode 100644 index 00000000..1dc6a2c3 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/AnyObservableObject.swift @@ -0,0 +1,32 @@ +// Distributed under the MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import Combine + +final class AnyObservableObject: ObservableObject { + init(_ observable: some ObservableObject) { + objectWillChange = observable + .objectWillChange + .map { _ in () } + .eraseToAnyPublisher() + } + + let objectWillChange: AnyPublisher +} diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/StringStorage.swift b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/StringStorage.swift new file mode 100644 index 00000000..e16d0c65 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/StringStorage.swift @@ -0,0 +1,42 @@ +// Distributed under the MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import Foundation +import SafeDI + +public protocol StringStorage { + func string(forKey key: String) -> String? + func setString(_ string: String?, forKey key: String) +} + +@Instantiable(fulfillingAdditionalTypes: [StringStorage.self]) +extension UserDefaults: @retroactive Instantiable, StringStorage { + public static func instantiate() -> UserDefaults { + .standard + } + + public func string(forKey key: String) -> String? { + object(forKey: key) as? String + } + + public func setString(_ string: String?, forKey key: String) { + set(string, forKey: key) + } +} diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/UserService.swift b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/UserService.swift new file mode 100644 index 00000000..0e6ab673 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/UserService.swift @@ -0,0 +1,51 @@ +// Distributed under the MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import Combine +import SafeDI + +public protocol UserService: ObservableObject { + var userName: String? { get set } + var observableObjectPublisher: ObservableObjectPublisher { get } +} + +@Instantiable(fulfillingAdditionalTypes: [UserService.self]) +public final class DefaultUserService: Instantiable, UserService { + public init(stringStorage: StringStorage) { + self.stringStorage = stringStorage + } + + public var userName: String? { + get { + stringStorage.string(forKey: #function) + } + set { + objectWillChange.send() + stringStorage.setString(newValue, forKey: #function) + } + } + + public var observableObjectPublisher: ObservableObjectPublisher { + objectWillChange + } + + @Received + @Published private var stringStorage: StringStorage +} diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/ExampleApp.swift b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/ExampleApp.swift new file mode 100644 index 00000000..dfde07af --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/ExampleApp.swift @@ -0,0 +1,70 @@ +// Distributed under the MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import Combine +import SafeDI +import SwiftUI + +// @Instantiable macro marks this type as capable of being instantiated by SafeDI. The `isRoot` parameter marks this type as being the root of the dependency tree. +@Instantiable(isRoot: true) +@MainActor +@main +public struct NotesApp: Instantiable, App { + public var body: some Scene { + WindowGroup { + if let userName = userService.userName { + // Returns a new instance of `NoteView`. + noteViewBuilder.instantiate(userName) + } else { + // Returns a new instance of a `NameEntryView`. + nameEntryViewBuilder.instantiate() + } + } + } + + // Memberwise initializer to satisfy SafeDI. + // `public init()` will be generated for this type because this type is a root. + // SafeDI identifies this type as a root because: + // 1. It only has `@Instantiated` properties (nothing `@Received` or `@Forwarded`) + // 2. No other type has an `@Instantiated` property of this type + public init( + userService: any UserService, + stringStorage: StringStorage, + nameEntryViewBuilder: Instantiator, + noteViewBuilder: Instantiator + ) { + self.userService = userService + self.stringStorage = stringStorage + self.nameEntryViewBuilder = nameEntryViewBuilder + self.noteViewBuilder = noteViewBuilder + observedUserService = AnyObservableObject(userService) + } + + /// A private property that is instantiated when the app is instantiated and manages the User state. + @Instantiated private let userService: any UserService + /// A private property that is instantiated when the app is instantiated and manages the persistence of strings. + @Instantiated private let stringStorage: StringStorage + /// A private property that is instantiated when the app is instantiated and can create a NameEntryView on demand. + @Instantiated private let nameEntryViewBuilder: Instantiator + /// A private property that is instantiated when the app is instantiated and can create a NoteView on demand. + @Instantiated private let noteViewBuilder: Instantiator + /// A mechanism for observing updates to the user service. + @ObservedObject private var observedUserService: AnyObservableObject +} diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/NameEntryView.swift b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/NameEntryView.swift new file mode 100644 index 00000000..d17f4c07 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/NameEntryView.swift @@ -0,0 +1,54 @@ +// Distributed under the MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import SafeDI +import SwiftUI + +@MainActor +@Instantiable +public struct NameEntryView: Instantiable, View { + public init(userService: any UserService) { + self.userService = userService + } + + public var body: some View { + VStack { + TextField( + text: $name, + prompt: Text("Enter your name"), + label: {} + ) + Button(action: { + userService.userName = name + }, label: { + Text("Log in") + }) + } + .padding() + } + + @State private var name: String = "" + + @Received private let userService: any UserService +} + +#Preview { + NameEntryView(userService: DefaultUserService(stringStorage: UserDefaults.standard)) +} diff --git a/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/NoteView.swift b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/NoteView.swift new file mode 100644 index 00000000..ba305bae --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/NoteView.swift @@ -0,0 +1,63 @@ +// Distributed under the MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import SafeDI +import SwiftUI + +@MainActor +@Instantiable +public struct NoteView: Instantiable, View { + public init(userName: String, userService: any UserService, stringStorage: StringStorage) { + self.userName = userName + self.userService = userService + self.stringStorage = stringStorage + _note = State(initialValue: stringStorage.string(forKey: userName) ?? "") + } + + public var body: some View { + VStack { + Text("\(userName)’s note") + TextEditor(text: $note) + .onChange(of: note) { _, newValue in + stringStorage.setString(newValue, forKey: userName) + } + Button(action: { + userService.userName = nil + }, label: { + Text("Log out") + }) + } + .padding() + } + + @Forwarded private let userName: String + @Received private let userService: any UserService + @Received private let stringStorage: StringStorage + + @State private var note: String = "" +} + +#Preview { + NoteView( + userName: "dfed", + userService: DefaultUserService(stringStorage: UserDefaults.standard), + stringStorage: UserDefaults.standard + ) +} diff --git a/Examples/ExampleCocoaPodsIntegration/Podfile b/Examples/ExampleCocoaPodsIntegration/Podfile new file mode 100644 index 00000000..6d6a23e0 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/Podfile @@ -0,0 +1,5 @@ +platform :ios, '18.0' + +target 'ExampleCocoaPodsIntegration' do + pod 'SafeDI', :path => '../../' +end diff --git a/Examples/ExampleCocoaPodsIntegration/Podfile.lock b/Examples/ExampleCocoaPodsIntegration/Podfile.lock new file mode 100644 index 00000000..f5952547 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/Podfile.lock @@ -0,0 +1,16 @@ +PODS: + - SafeDI (1.0.0) + +DEPENDENCIES: + - SafeDI (from `../../`) + +EXTERNAL SOURCES: + SafeDI: + :path: "../../" + +SPEC CHECKSUMS: + SafeDI: dd07c5fb89bf6efad61af270149fdf99b04f0093 + +PODFILE CHECKSUM: d4bab0152714182925f8589c33858a062c3158b9 + +COCOAPODS: 1.16.2 diff --git a/Examples/ExampleCocoaPodsIntegration/safeditool.sh b/Examples/ExampleCocoaPodsIntegration/safeditool.sh new file mode 100755 index 00000000..d77d2a32 --- /dev/null +++ b/Examples/ExampleCocoaPodsIntegration/safeditool.sh @@ -0,0 +1,32 @@ +#!/bin/zsh + +set -e + +VERSION='1.0.0-beta-2' +SAFEDI_LOCATION="$BUILD_DIR/SafeDITool-Release/$VERSION/safeditool" + +# Download the tool from Github releases. +if [ -f "$SAFEDI_LOCATION" ]; then + if [ ! -x "$SAFEDI_LOCATION" ]; then + chmod +x "$SAFEDI_LOCATION" + fi +else + mkdir -p "$(dirname "$SAFEDI_LOCATION")" + + ARCH=$(uname -m) + if [ "$ARCH" = "arm64" ]; then + ARCH_PATH="SafeDITool-arm64" + elif [ "$ARCH" = "x86_64" ]; then + ARCH_PATH="SafeDITool-x86_64" + else + echo "Unsupported architecture: $ARCH" + exit 1 + fi + curl -L -o "$SAFEDI_LOCATION" "https://github.com/dfed/SafeDI/releases/download/$VERSION/$ARCH_PATH" + chmod +x "$SAFEDI_LOCATION" +fi + +# Run the tool. +SOURCE_DIR="$PROJECT_DIR/ExampleCocoaPodsIntegration" +SAFEDI_OUTPUT="$PROJECT_DIR/ExampleCocoaPodsIntegration/SafeDI.swift" +$SAFEDI_LOCATION --include "$SOURCE_DIR" --dependency-tree-output "$SAFEDI_OUTPUT" diff --git a/README.md b/README.md index 457bfc23..63af1f7c 100644 --- a/README.md +++ b/README.md @@ -96,11 +96,13 @@ pod 'SafeDI', '~> 1.0.0' SafeDI provides a code generation plugin named `SafeDIGenerator`. This plugin works out of the box on a limited number of project configurations. If your project does not fall into these well-supported configurations, you can configure your build to utilize the `SafeDITool` command-line executable directly. -#### Single-module Xcode projects +#### Swift package manager + +##### Single-module Xcode projects If your first-party code comprises a single module in an `.xcodeproj`, once your Xcode project depends on the SafeDI package you can integrate the Swift Package Plugin simply by going to your target’s `Build Phases`, expanding the `Run Build Tool Plug-ins` drop-down, and adding the `SafeDIGenerator` as a build tool plug-in. You can see this integration in practice in the [ExampleProjectIntegration](Examples/ExampleProjectIntegration) project. -#### Swift package +##### Swift package If your first-party code is entirely contained in a Swift Package with one or more modules, you can add the following lines to your root target’s definition: @@ -112,44 +114,17 @@ If your first-party code is entirely contained in a Swift Package with one or mo You can see this integration in practice in the [ExamplePackageIntegration](Examples/ExamplePackageIntegration) package. -#### Additional configurations +#### CocoaPods -If your first-party code comprises multiple modules in Xcode, or a mix of Xcode Projects and Swift Packages, or some other configuration, once your Xcode project depends on the SafeDI package you will need to utilize the `SafeDITool` command-line executable directly in a pre-build script. - -```sh -set -e - -VERSION='<>' -DESTINATION="$BUILD_DIR/SafeDITool-Release/$VERSION/safeditool" - -# Download the tool from Github releases. -if [ -f "$DESTINATION" ]; then - if [ ! -x "$DESTINATION" ]; then - chmod +x "$DESTINATION" - fi -else - mkdir -p "$(dirname "$DESTINATION")" - - ARCH=$(uname -m) - if [ "$ARCH" = "arm64" ]; then - ARCH_PATH="SafeDITool-arm64" - elif [ "$ARCH" = "x86_64" ]; then - ARCH_PATH="SafeDITool-x86_64" - else - echo "Unsupported architecture: $ARCH" - exit 1 - fi - curl -L -o "$DESTINATION" "https://github.com/dfed/SafeDI/releases/download/$VERSION/$ARCH_PATH" - chmod +x "$DESTINATION" -fi - -# Run the tool. -$DESTINATION --include "$PROJECT_DIR/<>" "$PROJECT_DIR/<>" --dependency-tree-output "$PROJECT_DIR/<>" -``` +Use a pre-build script ([example](Examples/ExampleCocoaPodsIntegration/safeditool.sh)) to download the `SafeDITool` binary and generate your SafeDI dependency tree. Make sure to set `ENABLE_USER_SCRIPT_SANDBOXING` to `NO` in the target running the pre-build script. + +You can see this integration in practice in the [ExampleCocoaPodsIntegration](Examples/ExampleCocoaPodsIntegration) package. Run `bundle exec pod install --project-directory=Examples/ExampleCocoaPodsIntegration` to create the `ExampleCocoaPodsIntegration.xcworkspace`. + +#### Additional configurations -Make sure to set `ENABLE_USER_SCRIPT_SANDBOXING` to `NO` in your target, and to replace the `<>`, `<>`, `<>`, and `<>` with the appropriate values. Also ensure that you add `$PROJECT_DIR/<>` to the build script’s `Output Files` list. +If your first-party code comprises multiple modules in Xcode, or a mix of Xcode Projects and Swift Packages, or some other configuration, once your Xcode project depends on the SafeDI package you will need to utilize the `SafeDITool` command-line executable directly in a pre-build script similar to the CocoaPods integration described above. -You can see this in integration in practice in the [ExampleMultiProjectIntegration](Examples/ExampleMultiProjectIntegration) package. +You can see this integration in practice in the [ExampleMultiProjectIntegration](Examples/ExampleMultiProjectIntegration) package. `SafeDITool` is designed to integrate into projects of any size or shape. diff --git a/SafeDI.podspec b/SafeDI.podspec index a1627660..6f753126 100644 --- a/SafeDI.podspec +++ b/SafeDI.podspec @@ -19,7 +19,7 @@ Pod::Spec.new do |s| # The below scripts and flags were inspired by https://soumyamahunt.medium.com/support-swift-macros-with-cocoapods-3911f9317042 script = <<-SCRIPT.squish env -i PATH="$PATH" "$SHELL" -l -c - "swift build -c $(echo ${CONFIGURATION} | tr '[:upper:]' '[:lower:]') --product SafeDIMacros + "SAFEDI_COCOAPODS_PROTOCOL_PLUGIN=true swift build -c $(echo ${CONFIGURATION} | tr '[:upper:]' '[:lower:]') --product SafeDIMacros --sdk \\"`xcrun --show-sdk-path`\\" --package-path \\"$PODS_TARGET_SRCROOT\\" --scratch-path \\"${PODS_BUILD_DIR}/Macros/SafeDIMacros\\""