Compare commits

...

3 Commits

Author SHA1 Message Date
a1bfc8f5d3 Commit automatico: Aggiornamento dei file 2023-12-17 10:31:36 +01:00
07e9807316 - add method - (void) setBackgroundLayerWithColors:(NSArray *) colors startPoint:(CGPoint) startPoint angle:(CGFloat) angle
- add method - (void) setBackgroundLayerWithColors:(NSArray *) colors startPoint:(CGPoint) startPoint endPoint:(CGPoint) endPoint
2018-03-21 19:56:43 +01:00
2bc7881ba0 - Fix 2016-04-13 16:13:51 +02:00
99 changed files with 12572 additions and 1820 deletions

4
.gitattributes vendored Normal file
View File

@ -0,0 +1,4 @@
*.framework filter=lfs diff=lfs merge=lfs -text
libavcodec.a filter=lfs diff=lfs merge=lfs -text
GoogleMapsBase filter=lfs diff=lfs merge=lfs -text
GoogleMapsCore filter=lfs diff=lfs merge=lfs -text

View File

@ -7,7 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
3735765942C92DFF386546C9 /* Pods_PNGradientView_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B6FC9D963B92695B921DF96 /* Pods_PNGradientView_Example.framework */; };
23C7E232205C2D45CAB997C5 /* Pods_PNGradientView_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 870C10D07996D64E37D43186 /* Pods_PNGradientView_Tests.framework */; };
6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; };
6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
@ -22,8 +22,8 @@
6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; };
6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; };
687207931C45207D0062BDEF /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 687207921C45207D0062BDEF /* QuartzCore.framework */; };
7B292E6333798F6CF1875617 /* Pods_PNGradientView_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 639D62CD5105D2FB1311CF6C /* Pods_PNGradientView_Tests.framework */; };
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; };
DA435EAB7EBADF1E4514C851 /* Pods_PNGradientView_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3ECFB9B4E3A7249535F0CC2 /* Pods_PNGradientView_Example.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -37,9 +37,9 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
2B6FC9D963B92695B921DF96 /* Pods_PNGradientView_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PNGradientView_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2FE460B6A0468ACFF2967E9D /* Pods-PNGradientView_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNGradientView_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PNGradientView_Tests/Pods-PNGradientView_Tests.release.xcconfig"; sourceTree = "<group>"; };
3805F31C8115CA8E6230D5F2 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
56C5384844A9C776A152F9A1 /* Pods-PNGradientView_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNGradientView_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-PNGradientView_Example/Pods-PNGradientView_Example.release.xcconfig"; sourceTree = "<group>"; };
3C09E0D66235582FF2E562EB /* Pods-PNGradientView_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNGradientView_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-PNGradientView_Example/Pods-PNGradientView_Example.release.xcconfig"; sourceTree = "<group>"; };
6003F58A195388D20070C39A /* PNGradientView_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PNGradientView_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
@ -59,14 +59,14 @@
6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = "<group>"; };
606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = "<group>"; };
639D62CD5105D2FB1311CF6C /* Pods_PNGradientView_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PNGradientView_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
687207921C45207D0062BDEF /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
77BF0B8F84FC0C090AA5E4E4 /* Pods-PNGradientView_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNGradientView_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PNGradientView_Tests/Pods-PNGradientView_Tests.debug.xcconfig"; sourceTree = "<group>"; };
870C10D07996D64E37D43186 /* Pods_PNGradientView_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PNGradientView_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
A239C4D589940246CC5DC957 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
B5EC290C37FF2FD2B44F36D4 /* Pods-PNGradientView_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNGradientView_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PNGradientView_Tests/Pods-PNGradientView_Tests.release.xcconfig"; sourceTree = "<group>"; };
BB58A17ABCC08A9C0CBC1746 /* Pods-PNGradientView_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNGradientView_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PNGradientView_Example/Pods-PNGradientView_Example.debug.xcconfig"; sourceTree = "<group>"; };
BDA9272291DEEAB307029A1B /* Pods-PNGradientView_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNGradientView_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PNGradientView_Tests/Pods-PNGradientView_Tests.debug.xcconfig"; sourceTree = "<group>"; };
B3ECFB9B4E3A7249535F0CC2 /* Pods_PNGradientView_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PNGradientView_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C946A6D019B0F23F14758B0F /* PNGradientView.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = PNGradientView.podspec; path = ../PNGradientView.podspec; sourceTree = "<group>"; };
D81B6350FDFC86A4077FC757 /* Pods-PNGradientView_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNGradientView_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PNGradientView_Example/Pods-PNGradientView_Example.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -78,7 +78,7 @@
6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */,
6003F592195388D20070C39A /* UIKit.framework in Frameworks */,
6003F58E195388D20070C39A /* Foundation.framework in Frameworks */,
3735765942C92DFF386546C9 /* Pods_PNGradientView_Example.framework in Frameworks */,
DA435EAB7EBADF1E4514C851 /* Pods_PNGradientView_Example.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -89,13 +89,24 @@
6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */,
6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */,
6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */,
7B292E6333798F6CF1875617 /* Pods_PNGradientView_Tests.framework in Frameworks */,
23C7E232205C2D45CAB997C5 /* Pods_PNGradientView_Tests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
393A880D672758E195917D2B /* Pods */ = {
isa = PBXGroup;
children = (
D81B6350FDFC86A4077FC757 /* Pods-PNGradientView_Example.debug.xcconfig */,
3C09E0D66235582FF2E562EB /* Pods-PNGradientView_Example.release.xcconfig */,
77BF0B8F84FC0C090AA5E4E4 /* Pods-PNGradientView_Tests.debug.xcconfig */,
2FE460B6A0468ACFF2967E9D /* Pods-PNGradientView_Tests.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
6003F581195388D10070C39A = {
isa = PBXGroup;
children = (
@ -104,7 +115,7 @@
6003F5B5195388D20070C39A /* Tests */,
6003F58C195388D20070C39A /* Frameworks */,
6003F58B195388D20070C39A /* Products */,
87032DB5317E025A454AF1A7 /* Pods */,
393A880D672758E195917D2B /* Pods */,
);
sourceTree = "<group>";
};
@ -125,8 +136,8 @@
6003F58F195388D20070C39A /* CoreGraphics.framework */,
6003F591195388D20070C39A /* UIKit.framework */,
6003F5AF195388D20070C39A /* XCTest.framework */,
2B6FC9D963B92695B921DF96 /* Pods_PNGradientView_Example.framework */,
639D62CD5105D2FB1311CF6C /* Pods_PNGradientView_Tests.framework */,
B3ECFB9B4E3A7249535F0CC2 /* Pods_PNGradientView_Example.framework */,
870C10D07996D64E37D43186 /* Pods_PNGradientView_Tests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -186,17 +197,6 @@
name = "Podspec Metadata";
sourceTree = "<group>";
};
87032DB5317E025A454AF1A7 /* Pods */ = {
isa = PBXGroup;
children = (
BB58A17ABCC08A9C0CBC1746 /* Pods-PNGradientView_Example.debug.xcconfig */,
56C5384844A9C776A152F9A1 /* Pods-PNGradientView_Example.release.xcconfig */,
BDA9272291DEEAB307029A1B /* Pods-PNGradientView_Tests.debug.xcconfig */,
B5EC290C37FF2FD2B44F36D4 /* Pods-PNGradientView_Tests.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -204,12 +204,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "PNGradientView_Example" */;
buildPhases = (
64FF49B673FD33CB7FB9A1C1 /* Check Pods Manifest.lock */,
D8E29BB91CF367F3F0F1DE5F /* [CP] Check Pods Manifest.lock */,
6003F586195388D20070C39A /* Sources */,
6003F587195388D20070C39A /* Frameworks */,
6003F588195388D20070C39A /* Resources */,
8BF5CEAC8AEA37C053AED557 /* Embed Pods Frameworks */,
958F4C0CBE5AF250F2A32885 /* Copy Pods Resources */,
C71329DBE47E914B7A6AA37A /* [CP] Embed Pods Frameworks */,
637A99967A45EB17DDFB54E8 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -224,12 +224,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "PNGradientView_Tests" */;
buildPhases = (
C805B4923787CE84EF9795B6 /* Check Pods Manifest.lock */,
3E089AA3F68B2357A17AA6E0 /* [CP] Check Pods Manifest.lock */,
6003F5AA195388D20070C39A /* Sources */,
6003F5AB195388D20070C39A /* Frameworks */,
6003F5AC195388D20070C39A /* Resources */,
B4F757BFEFE5CEA10E006C00 /* Embed Pods Frameworks */,
49E99BB4DFD223B91FC00094 /* Copy Pods Resources */,
A41F150D6282997619D85E30 /* [CP] Embed Pods Frameworks */,
31C1EAE0E15E3D6C77F136EF /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -297,14 +297,14 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
49E99BB4DFD223B91FC00094 /* Copy Pods Resources */ = {
31C1EAE0E15E3D6C77F136EF /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@ -312,44 +312,32 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNGradientView_Tests/Pods-PNGradientView_Tests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
64FF49B673FD33CB7FB9A1C1 /* Check Pods Manifest.lock */ = {
3E089AA3F68B2357A17AA6E0 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "Check Pods Manifest.lock";
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-PNGradientView_Tests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
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;
};
8BF5CEAC8AEA37C053AED557 /* Embed Pods Frameworks */ = {
637A99967A45EB17DDFB54E8 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNGradientView_Example/Pods-PNGradientView_Example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
958F4C0CBE5AF250F2A32885 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@ -357,34 +345,70 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNGradientView_Example/Pods-PNGradientView_Example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
B4F757BFEFE5CEA10E006C00 /* Embed Pods Frameworks */ = {
A41F150D6282997619D85E30 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-PNGradientView_Tests/Pods-PNGradientView_Tests-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/PNGradientView/PNGradientView.framework",
"${BUILT_PRODUCTS_DIR}/RZDataBinding/RZDataBinding.framework",
"${BUILT_PRODUCTS_DIR}/Expecta/Expecta.framework",
"${BUILT_PRODUCTS_DIR}/Expecta+Snapshots/Expecta_Snapshots.framework",
"${BUILT_PRODUCTS_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework",
"${BUILT_PRODUCTS_DIR}/Specta/Specta.framework",
);
name = "Embed Pods Frameworks";
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PNGradientView.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RZDataBinding.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Expecta.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Expecta_Snapshots.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSnapshotTestCase.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Specta.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNGradientView_Tests/Pods-PNGradientView_Tests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
C805B4923787CE84EF9795B6 /* Check Pods Manifest.lock */ = {
C71329DBE47E914B7A6AA37A /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-PNGradientView_Example/Pods-PNGradientView_Example-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/PNGradientView/PNGradientView.framework",
"${BUILT_PRODUCTS_DIR}/RZDataBinding/RZDataBinding.framework",
);
name = "Check Pods Manifest.lock";
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PNGradientView.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RZDataBinding.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNGradientView_Example/Pods-PNGradientView_Example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
D8E29BB91CF367F3F0F1DE5F /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-PNGradientView_Example-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 */
@ -513,7 +537,7 @@
};
6003F5C0195388D20070C39A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = BB58A17ABCC08A9C0CBC1746 /* Pods-PNGradientView_Example.debug.xcconfig */;
baseConfigurationReference = D81B6350FDFC86A4077FC757 /* Pods-PNGradientView_Example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
@ -529,7 +553,7 @@
};
6003F5C1195388D20070C39A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 56C5384844A9C776A152F9A1 /* Pods-PNGradientView_Example.release.xcconfig */;
baseConfigurationReference = 3C09E0D66235582FF2E562EB /* Pods-PNGradientView_Example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
@ -545,7 +569,7 @@
};
6003F5C3195388D20070C39A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = BDA9272291DEEAB307029A1B /* Pods-PNGradientView_Tests.debug.xcconfig */;
baseConfigurationReference = 77BF0B8F84FC0C090AA5E4E4 /* Pods-PNGradientView_Tests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
@ -569,7 +593,7 @@
};
6003F5C4195388D20070C39A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B5EC290C37FF2FD2B44F36D4 /* Pods-PNGradientView_Tests.release.xcconfig */;
baseConfigurationReference = 2FE460B6A0468ACFF2967E9D /* Pods-PNGradientView_Tests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (

View File

@ -19,8 +19,8 @@
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[(PNGradientView*)self.view setBackgroundLayerWithColors:@[[UIColor greenColor],[UIColor whiteColor],[UIColor redColor]] startPoint:CGPointMake(0, 0)];
self.view.translatesAutoresizingMaskIntoConstraints = NO;
[(PNGradientView*)self.view setBackgroundLayerWithColors:@[[UIColor greenColor],[UIColor redColor]] startPoint:CGPointMake(0, 0) angle:0.5];
}
- (void)didReceiveMemoryWarning

View File

@ -1,15 +1,18 @@
PODS:
- Expecta (1.0.5)
- Expecta+Snapshots (2.0.0):
- Expecta (1.0.6)
- Expecta+Snapshots (3.1.1):
- Expecta (~> 1.0)
- FBSnapshotTestCase/Core (~> 2.0.3)
- FBSnapshotTestCase (2.0.7):
- FBSnapshotTestCase/SwiftSupport (= 2.0.7)
- FBSnapshotTestCase/Core (2.0.7)
- FBSnapshotTestCase/SwiftSupport (2.0.7):
- FBSnapshotTestCase/Core (~> 2.0)
- Specta (~> 1.0)
- FBSnapshotTestCase (2.1.4):
- FBSnapshotTestCase/SwiftSupport (= 2.1.4)
- FBSnapshotTestCase/Core (2.1.4)
- FBSnapshotTestCase/SwiftSupport (2.1.4):
- FBSnapshotTestCase/Core
- PNGradientView (0.1.0)
- Specta (1.0.5)
- PNGradientView (0.2.0):
- RZDataBinding
- RZDataBinding (2.1.0)
- Specta (1.0.7)
DEPENDENCIES:
- Expecta
@ -20,15 +23,16 @@ DEPENDENCIES:
EXTERNAL SOURCES:
PNGradientView:
:path: "../"
:path: ../
SPEC CHECKSUMS:
Expecta: e1c022fcd33910b6be89c291d2775b3fe27a89fe
Expecta+Snapshots: 29b38dd695bc72a0ed2bea833937d78df41943ba
FBSnapshotTestCase: 7e85180d0d141a0cf472352edda7e80d7eaeb547
PNGradientView: 21d127e76f77674eecfa0dc9859751004f485e38
Specta: ac94d110b865115fe60ff2c6d7281053c6f8e8a2
Expecta: 3b6bd90a64b9a1dcb0b70aa0e10a7f8f631667d5
Expecta+Snapshots: dcff217eef506dabd6dfdc7864ea2da321fafbb8
FBSnapshotTestCase: 094f9f314decbabe373b87cc339bea235a63e07a
PNGradientView: 3c8f5b3ddbb7b957b8ecf0c72b5ceff2b36ee24f
RZDataBinding: 6981e90ddaae2f5e02028323b1043f8c31013109
Specta: 3e1bd89c3517421982dc4d1c992503e48bd5fe66
PODFILE CHECKSUM: cd7860932a2fb17c979c3a4af42606b114244376
COCOAPODS: 1.0.0.beta.2
COCOAPODS: 1.4.0

View File

@ -1,11 +1,4 @@
//
// EXPMatchers+FBSnapshotTest.h
// Artsy
//
// Created by Daniel Doubrovkine on 1/14/14.
// Copyright (c) 2014 Artsy Inc. All rights reserved.
//
#import <CoreGraphics/CoreGraphics.h>
#import <Expecta/Expecta.h>
#import "ExpectaObject+FBSnapshotTest.h"
@ -20,3 +13,6 @@ EXPMatcherInterface(recordSnapshot, (void));
EXPMatcherInterface(haveValidSnapshotNamed, (NSString *snapshot));
EXPMatcherInterface(recordSnapshotNamed, (NSString *snapshot));
EXPMatcherInterface(haveValidSnapshotNamedWithTolerance, (NSString *snapshot, CGFloat tolerance));
EXPMatcherInterface(haveValidSnapshotWithTolerance, (CGFloat tolerance));

View File

@ -1,11 +1,3 @@
//
// EXPMatchers+FBSnapshotTest.h
// Artsy
//
// Created by Daniel Doubrovkine on 1/14/14.
// Copyright (c) 2014 Artsy Inc. All rights reserved.
//
#import "EXPMatchers+FBSnapshotTest.h"
#import <Expecta/EXPMatcherHelpers.h>
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
@ -26,13 +18,14 @@
return instance;
}
+ (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer snapshot:(NSString *)snapshot testCase:(id)testCase record:(BOOL)record referenceDirectory:(NSString *)referenceDirectory error:(NSError **)error
+ (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer snapshot:(NSString *)snapshot testCase:(id)testCase record:(BOOL)record referenceDirectory:(NSString *)referenceDirectory tolerance:(CGFloat)tolerance error:(NSError **)error
{
FBSnapshotTestController *snapshotController = [[FBSnapshotTestController alloc] initWithTestClass:[testCase class]];
snapshotController.recordMode = record;
snapshotController.referenceImagesDirectory = referenceDirectory;
snapshotController.usesDrawViewHierarchyInRect = [Expecta usesDrawViewHierarchyInRect];
snapshotController.deviceAgnostic = [Expecta isDeviceAgnostic];
if (! snapshotController.referenceImagesDirectory) {
[NSException raise:@"Missing value for referenceImagesDirectory" format:@"Call [[EXPExpectFBSnapshotTest instance] setReferenceImagesDirectory"];
@ -41,7 +34,7 @@
return [snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
selector:NSSelectorFromString(snapshot)
identifier:nil
tolerance:0
tolerance:tolerance
error:error];
}
@ -87,26 +80,39 @@ void setGlobalReferenceImageDir(char *reference) {
NSString *testFileName = [NSString stringWithCString:self.fileName encoding:NSUTF8StringEncoding];
NSArray *pathComponents = [testFileName pathComponents];
for (NSString *folder in pathComponents) {
NSString *firstFolderFound = nil;
for (NSString *folder in pathComponents.reverseObjectEnumerator) {
if ([folder.lowercaseString rangeOfString:@"tests"].location != NSNotFound) {
NSArray *folderPathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents indexOfObject:folder] + 1)];
return [NSString stringWithFormat:@"%@/ReferenceImages", [folderPathComponents componentsJoinedByString:@"/"]];
NSString *referenceImagesPath = [NSString stringWithFormat:@"%@/ReferenceImages", [folderPathComponents componentsJoinedByString:@"/"]];
if (!firstFolderFound) {
firstFolderFound = referenceImagesPath;
}
BOOL isDirectory = NO;
BOOL referenceDirExists = [[NSFileManager defaultManager] fileExistsAtPath:referenceImagesPath isDirectory:&isDirectory];
// if the folder exists, this is the reference dir for sure
if (referenceDirExists && isDirectory) {
return referenceImagesPath;
}
}
}
// if a reference folder wasn't found, we should create one
if (firstFolderFound) {
return firstFolderFound;
}
[NSException raise:@"Could not infer reference image folder" format:@"You should provide a reference dir using setGlobalReferenceImageDir(FB_REFERENCE_IMAGE_DIR);"];
return nil;
}
@end
// If you're bringing in Speca via CocoaPods
// use the test path to get the test's image file URL
#if __has_include(<Specta/Specta.h>)
#import <Specta/Specta.h>
#import <Specta/SpectaUtility.h>
#import <Specta/SPTExample.h>
@ -127,9 +133,14 @@ NSString *sanitizedTestPath(){
return name;
}
EXPMatcherImplementationBegin(haveValidSnapshot, (void)){
EXPMatcherImplementationBegin(haveValidSnapshotWithTolerance, (CGFloat tolerance)){
__block NSError *error = nil;
prerequisite(^BOOL{
return actual != nil;
});
match(^BOOL{
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
NSString *name = sanitizedTestPath();
@ -140,10 +151,14 @@ EXPMatcherImplementationBegin(haveValidSnapshot, (void)){
actual = [actual view];
}
return [EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:name testCase:[self testCase] record:NO referenceDirectory:referenceImageDir error:&error];
return [EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:name testCase:[self testCase] record:NO referenceDirectory:referenceImageDir tolerance:tolerance error:&error];
});
failureMessageForTo(^NSString *{
if (!actual) {
return [EXPExpectFBSnapshotTest combinedError:@"Nil was passed into haveValidSnapshot." test:sanitizedTestPath() error:nil];
}
return [EXPExpectFBSnapshotTest combinedError:@"expected a matching snapshot in" test:sanitizedTestPath() error:error];
});
@ -153,18 +168,22 @@ EXPMatcherImplementationBegin(haveValidSnapshot, (void)){
}
EXPMatcherImplementationEnd
EXPMatcherImplementationBegin(haveValidSnapshot, (void)) {
return self.haveValidSnapshotWithTolerance(0);
}
EXPMatcherImplementationEnd
EXPMatcherImplementationBegin(recordSnapshot, (void)) {
__block NSError *error = nil;
BOOL actualIsViewLayerOrViewController = ([actual isKindOfClass:UIView.class] || [actual isKindOfClass:CALayer.class] || [actual isKindOfClass:UIViewController.class]);
prerequisite(^BOOL{
return actualIsViewLayerOrViewController;
return actual != nil && actualIsViewLayerOrViewController;
});
match(^BOOL{
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
// For view controllers do the viewWill/viewDid dance, then pass view through
if ([actual isKindOfClass:UIViewController.class]) {
@ -173,11 +192,15 @@ EXPMatcherImplementationBegin(recordSnapshot, (void)) {
actual = [actual view];
}
[EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:sanitizedTestPath() testCase:[self testCase] record:YES referenceDirectory:referenceImageDir error:&error];
[EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:sanitizedTestPath() testCase:[self testCase] record:YES referenceDirectory:referenceImageDir tolerance:0 error:&error];
return NO;
});
failureMessageForTo(^NSString *{
if (!actual) {
return [EXPExpectFBSnapshotTest combinedError:@"Nil was passed into recordSnapshot." test:sanitizedTestPath() error:nil];
}
if (!actualIsViewLayerOrViewController) {
return [EXPExpectFBSnapshotTest combinedError:@"Expected a View, Layer or View Controller." test:sanitizedTestPath() error:nil];
}
@ -198,54 +221,12 @@ EXPMatcherImplementationBegin(recordSnapshot, (void)) {
}
EXPMatcherImplementationEnd
#else
// If you don't have Speca stub the functions
EXPMatcherImplementationBegin(haveValidSnapshot, (void)){
prerequisite(^BOOL{
return NO;
});
failureMessageForTo(^NSString *{
return @"you need Specta installed via CocoaPods to use haveValidSnapshot, use haveValidSnapshotNamed instead";
});
failureMessageForNotTo(^NSString *{
return @"you need Specta installed via CocoaPods to use haveValidSnapshot, use haveValidSnapshotNamed instead";
});
}
EXPMatcherImplementationEnd
EXPMatcherImplementationBegin(recordSnapshot, (void)) {
prerequisite(^BOOL{
return NO;
});
failureMessageForTo(^NSString *{
return @"you need Specta installed via CocoaPods to use recordSnapshot, use recordSnapshotNamed instead";
});
failureMessageForNotTo(^NSString *{
return @"you need Specta installed via CocoaPods to use recordSnapshot, use recordSnapshotNamed instead";
});
}
EXPMatcherImplementationEnd
#endif
EXPMatcherImplementationBegin(haveValidSnapshotNamed, (NSString *snapshot)){
EXPMatcherImplementationBegin(haveValidSnapshotNamedWithTolerance, (NSString *snapshot, CGFloat tolerance)) {
BOOL snapshotIsNil = (snapshot == nil);
__block NSError *error = nil;
prerequisite(^BOOL{
return !(snapshotIsNil);
return actual != nil && !(snapshotIsNil);
});
match(^BOOL{
@ -256,10 +237,14 @@ EXPMatcherImplementationBegin(haveValidSnapshotNamed, (NSString *snapshot)){
actual = [actual view];
}
return [EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:snapshot testCase:[self testCase] record:NO referenceDirectory:referenceImageDir error:&error];
return [EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:snapshot testCase:[self testCase] record:NO referenceDirectory:referenceImageDir tolerance:tolerance error:&error];
});
failureMessageForTo(^NSString *{
if (!actual) {
return [EXPExpectFBSnapshotTest combinedError:@"Nil was passed into haveValidSnapshotNamed." test:sanitizedTestPath() error:nil];
}
return [EXPExpectFBSnapshotTest combinedError:@"expected a matching snapshot named" test:snapshot error:error];
});
@ -270,6 +255,11 @@ EXPMatcherImplementationBegin(haveValidSnapshotNamed, (NSString *snapshot)){
}
EXPMatcherImplementationEnd
EXPMatcherImplementationBegin(haveValidSnapshotNamed, (NSString *snapshot)) {
return self.haveValidSnapshotNamedWithTolerance(snapshot, 0);
}
EXPMatcherImplementationEnd
EXPMatcherImplementationBegin(recordSnapshotNamed, (NSString *snapshot)) {
BOOL snapshotExists = (snapshot != nil);
BOOL actualIsViewLayerOrViewController = ([actual isKindOfClass:UIView.class] || [actual isKindOfClass:CALayer.class] || [actual isKindOfClass:UIViewController.class]);
@ -277,12 +267,11 @@ EXPMatcherImplementationBegin(recordSnapshotNamed, (NSString *snapshot)) {
id actualRef = actual;
prerequisite(^BOOL{
return actualRef && snapshotExists && actualIsViewLayerOrViewController;
return actualRef != nil && snapshotExists && actualIsViewLayerOrViewController;
});
match(^BOOL{
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
// For view controllers do the viewWill/viewDid dance, then pass view through
if ([actual isKindOfClass:UIViewController.class]) {
[actual beginAppearanceTransition:YES animated:NO];
@ -290,11 +279,14 @@ EXPMatcherImplementationBegin(recordSnapshotNamed, (NSString *snapshot)) {
actual = [actual view];
}
[EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:snapshot testCase:[self testCase] record:YES referenceDirectory:referenceImageDir error:&error];
[EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:snapshot testCase:[self testCase] record:YES referenceDirectory:referenceImageDir tolerance:0 error:&error];
return NO;
});
failureMessageForTo(^NSString *{
if (!actual) {
return [EXPExpectFBSnapshotTest combinedError:@"Nil was passed into recordSnapshotNamed." test:sanitizedTestPath() error:nil];
}
if (!actualIsViewLayerOrViewController) {
return [EXPExpectFBSnapshotTest combinedError:@"Expected a View, Layer or View Controller." test:snapshot error:nil];
}

View File

@ -14,4 +14,8 @@
+ (BOOL)usesDrawViewHierarchyInRect;
+ (void)setDeviceAgnostic:(BOOL)deviceAgnostic;
+ (BOOL)isDeviceAgnostic;
@end

View File

@ -22,4 +22,15 @@ static NSString const *kUsesDrawViewHierarchyInRectKey = @"ExpectaObject+FBSnaps
return usesDrawViewHierarchyInRect.boolValue;
}
+ (void)setDeviceAgnostic:(BOOL)deviceAgnostic
{
objc_setAssociatedObject(self, @selector(isDeviceAgnostic), @(deviceAgnostic), OBJC_ASSOCIATION_ASSIGN);
}
+ (BOOL)isDeviceAgnostic
{
NSNumber *isDeviceAgnostic = objc_getAssociatedObject(self, @selector(isDeviceAgnostic));
return isDeviceAgnostic.boolValue;
}
@end

View File

@ -3,7 +3,7 @@ Expecta Matchers for FBSnapshotTestCase
[Expecta](https://github.com/specta/expecta) matchers for [ios-snapshot-test-case](https://github.com/facebook/ios-snapshot-test-case).
[![Build Status](https://travis-ci.org/dblock/ios-snapshot-test-case-expecta.png)](https://travis-ci.org/dblock/ios-snapshot-test-case-expecta)
[![Build Status](https://travis-ci.org/dblock/ios-snapshot-test-case-expecta.svg)](https://travis-ci.org/dblock/ios-snapshot-test-case-expecta)
### Usage
@ -63,6 +63,15 @@ describe(@"test name derived matching", ^{
SpecEnd
```
### Approximation support
If for some reason you want to specify a tolerance for your test, you can use either named or unnamed matchers, where the `tolerance` parameter is a `CGFloat` in the interval `[0, 1]` and it represents the minimum ratio of unmatched points by the total number of points in your snapshot. In the example below, a tolerance of `0.01` means our `view` could be up to `1%` different from the reference image.
``` Objective-C
expect(view).to.haveValidSnapshotWithTolerance(0.01);
expect(view).to.haveValidSnapshotNamedWithTolerance(@"unique snapshot name", 0.01);
```
### Sane defaults
`EXPMatchers+FBSnapshotTest` will automatically figure out the tests folder, and [add a reference image](https://github.com/dblock/ios-snapshot-test-case-expecta/blob/master/EXPMatchers%2BFBSnapshotTest.m#L84-L85) directory, if you'd like to override this, you should include a `beforeAll` block setting the `setGlobalReferenceImageDir` in each file containing tests.

View File

@ -9,9 +9,9 @@
#ifndef Expecta_EXPDefines_h
#define Expecta_EXPDefines_h
typedef void (^EXPBasicBlock)();
typedef id (^EXPIdBlock)();
typedef BOOL (^EXPBoolBlock)();
typedef NSString *(^EXPStringBlock)();
typedef void (^EXPBasicBlock)(void);
typedef id (^EXPIdBlock)(void);
typedef BOOL (^EXPBoolBlock)(void);
typedef NSString *(^EXPStringBlock)(void);
#endif

View File

@ -10,6 +10,10 @@ EXPMatcherImplementationBegin(respondTo, (SEL expected)) {
});
match(^BOOL {
if ([actual respondsToSelector:@selector(instancesRespondToSelector:)] &&
[actual instancesRespondToSelector:expected]) {
return YES;
}
return [actual respondsToSelector:expected];
});

View File

@ -1,13 +1,8 @@
#Expecta
[![Build Status](http://img.shields.io/travis/specta/expecta/master.svg?style=flat)](https://travis-ci.org/specta/expecta)
[![Pod Version](http://img.shields.io/cocoapods/v/Expecta.svg?style=flat)](http://cocoadocs.org/docsets/Expecta/)
[![Pod Platform](http://img.shields.io/cocoapods/p/Expecta.svg?style=flat)](http://cocoadocs.org/docsets/Expecta/)
[![Pod License](http://img.shields.io/cocoapods/l/Expecta.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0.html)
# Expecta [![Build Status](http://img.shields.io/travis/specta/expecta/master.svg?style=flat)](https://travis-ci.org/specta/expecta) [![Pod Version](http://img.shields.io/cocoapods/v/Expecta.svg?style=flat)](http://cocoadocs.org/docsets/Expecta/)
A matcher framework for Objective-C and Cocoa.
## Introduction
## FEATURES
The main advantage of using Expecta over other matcher frameworks is that you do not have to specify the data types. Also, the syntax of Expecta matchers is much more readable and does not suffer from parenthesitis.
@ -18,63 +13,10 @@ expect([bar isBar]).to.equal(YES);
expect(baz).to.equal(3.14159);
```
Expecta is framework-agnostic: it works well with XCTest and XCTest-compatible test frameworks such as [Specta](http://github.com/petejkim/specta/).
Expecta is framework-agnostic: it works well with XCTest and XCTest-compatible test frameworks such as [Specta](http://github.com/petejkim/specta/), or [Kiwi](https://github.com/kiwi-bdd/Kiwi/).
## Installation
You can setup Expecta using [Carthage](https://github.com/Carthage/Carthage), [CocoaPods](http://github.com/CocoaPods/CocoaPods) or [completely manually](#setting-up-manually).
### Carthage
1. Add Expecta to your project's `Cartfile.private`:
```ruby
github "specta/expecta" "master"
```
2. Run `carthage update` in your project directory.
3. Drag the appropriate **Expecta.framework** for your platform (located in `Carthage/Build/`) into your applications Xcode project, and add it to your test target(s).
### CocoaPods
1. Add Expecta to your project's `Podfile`:
```ruby
target :MyApp do
# Your app's dependencies
end
target :MyAppTests do
pod 'Expecta', '~> 1.0.0'
end
```
2. Run `pod update` or `pod install` in your project directory.
### Setting Up Manually
1. Clone Expecta from Github.
2. Run `rake` in your project directory to build the frameworks and libraries.
3. Add a Cocoa or Cocoa Touch Unit Testing Bundle target to your Xcode project if you don't already have one.
4. For **OS X projects**, copy and add `Expecta.framework` in the `Products/osx` folder to your project's test target.
For **iOS projects**, copy and add `Expecta.framework` in the `Products/ios` folder to your project's test target.
You can also use `libExpecta.a` if you prefer to link Expecta as a static library — iOS 7.x and below require this.
6. Add `-ObjC` and `-all_load` to the **Other Linker Flags** build setting for the test target in your Xcode project.
7. You can now use Expecta in your test classes by adding the following import:
```objective-c
@import Expecta; // If you're using Expecta.framework
// OR
#import <Expecta/Expecta.h> // If you're using the static library, or the framework
```
## Built-in Matchers
## MATCHERS
> `expect(x).to.equal(y);` compares objects or primitives x and y and passes if they are identical (==) or equivalent isEqual:).
@ -132,13 +74,13 @@ You can setup Expecta using [Carthage](https://github.com/Carthage/Carthage), [C
> `expect(x).to.match(y);` passes if an instance of NSString `x` matches regular expression (given as NSString) `y` one or more times.
## Inverting Matchers
### Inverting Matchers
Every matcher's criteria can be inverted by prepending `.notTo` or `.toNot`:
>`expect(x).notTo.equal(y);` compares objects or primitives x and y and passes if they are *not* equivalent.
## Asynchronous Testing
### Asynchronous Testing
Every matcher can be made to perform asynchronous testing by prepending `.will`, `.willNot` or `after(...)`:
@ -172,14 +114,14 @@ describe(@"Foo", ^{
});
```
## Forced Failing
### Forced Failing
You can fail a test by using the `failure` attribute. This can be used to test branching.
> `failure(@"This should not happen");` outright fails a test.
## Writing New Matchers
### WRITING NEW MATCHERS
Writing a new matcher is easy with special macros provided by Expecta. Take a look at how `.beKindOf()` matcher is defined:
@ -246,7 +188,7 @@ EXPMatcherImplementationBegin(beKindOf, (Class expected)) {
EXPMatcherImplementationEnd
```
## Dynamic Predicate Matchers
## DYNAMIC PREDICATE MATCHERS
It is possible to add predicate matchers by simply defining the matcher interface, with the matcher implementation being handled at runtime by delegating to the predicate method on your object.
@ -282,6 +224,68 @@ You can now write your assertion as follows:
expect(lightSwitch).isTurnedOn();
```
## INSTALLATION
You can setup Expecta using [CocoaPods](http://github.com/CocoaPods/CocoaPods), [Carthage](https://github.com/Carthage/Carthage) or [completely manually](#setting-up-manually).
### CocoaPods
1. Add Expecta to your project's `Podfile`:
```ruby
target :MyApp do
# your app dependencies
target :MyAppTests do
inherit! search_paths
pod 'Expecta', '~> 1.0'
end
end
```
### Carthage
1. Add Expecta to your project's `Cartfile.private`:
```ruby
github "specta/expecta" "master"
```
2. Run `carthage update` in your project directory.
3. Drag the appropriate **Expecta.framework** for your platform (located in `Carthage/Build/`) into your applications Xcode project, and add it to your test target(s).
2. Run `pod update` or `pod install` in your project directory.
### Setting Up Manually
1. Clone Expecta from Github.
2. Run `rake` in your project directory to build the frameworks and libraries.
3. Add a Cocoa or Cocoa Touch Unit Testing Bundle target to your Xcode project if you don't already have one.
4. For **OS X projects**, copy and add `Expecta.framework` in the `Products/osx` folder to your project's test target.
For **iOS projects**, copy and add `Expecta.framework` in the `Products/ios` folder to your project's test target.
You can also use `libExpecta.a` if you prefer to link Expecta as a static library — iOS 7.x and below require this.
6. Add `-ObjC` and `-all_load` to the **Other Linker Flags** build setting for the test target in your Xcode project.
7. You can now use Expecta in your test classes by adding the following import:
```objective-c
@import Expecta; // If you're using Expecta.framework
// OR
#import <Expecta/Expecta.h> // If you're using the static library, or the framework
```
## STATUS
Expecta, and Specta are considered done projects, there are no plans for _active_ development on the project at the moment aside from ensuring future Xcode compatability.
Therefore it is a stable dependency, but will not be moving into the Swift world. If you are looking for that, we recommend you consider [Quick](https://github.com/quick/quick) and [Nimble](https://github.com/quick/nimble).
## Contribution Guidelines
* Please use only spaces and indent 2 spaces at a time.
@ -290,4 +294,4 @@ expect(lightSwitch).isTurnedOn();
## License
Copyright (c) 2012-2015 [Specta Team](https://github.com/specta?tab=members). This software is licensed under the [MIT License](http://github.com/specta/specta/raw/master/LICENSE).
Copyright (c) 2012-2016 [Specta Team](https://github.com/specta?tab=members). This software is licensed under the [MIT License](http://github.com/specta/specta/raw/master/LICENSE).

View File

@ -1,15 +0,0 @@
framework module FBSnapshotTestCase {
umbrella header "FBSnapshotTestCase.h"
export *
module * { export * }
header "FBSnapshotTestCase.h"
header "FBSnapshotTestCasePlatform.h"
header "FBSnapshotTestController.h"
private header "UIImage+Compare.h"
private header "UIImage+Diff.h"
private header "UIImage+Snapshot.h"
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <UIKit/UIKit.h>
@interface UIApplication (StrictKeyWindow)
/**
@return The receiver's @c keyWindow. Raises an assertion if @c nil.
*/
- (UIWindow *)fb_strictKeyWindow;
@end

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
@implementation UIApplication (StrictKeyWindow)
- (UIWindow *)fb_strictKeyWindow
{
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
if (!keyWindow) {
[NSException raise:@"FBSnapshotTestCaseNilKeyWindowException"
format:@"Snapshot tests must be hosted by an application with a key window. Please ensure your test"
" host sets up a key window at launch (either via storyboards or programmatically) and doesn't"
" do anything to remove it while snapshot tests are running."];
}
return keyWindow;
}
@end

View File

@ -9,6 +9,7 @@
*/
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
@implementation UIImage (Snapshot)
@ -43,11 +44,16 @@
NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view);
NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view);
UIWindow *window = view.window;
if (window == nil) {
window = [[UIWindow alloc] initWithFrame:bounds];
// If the input view is already a UIWindow, then just use that. Otherwise wrap in a window.
UIWindow *window = [view isKindOfClass:[UIWindow class]] ? (UIWindow *)view : view.window;
BOOL removeFromSuperview = NO;
if (!window) {
window = [[UIApplication sharedApplication] fb_strictKeyWindow];
}
if (!view.window && view != window) {
[window addSubview:view];
[window makeKeyAndVisible];
removeFromSuperview = YES;
}
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
@ -56,6 +62,11 @@
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (removeFromSuperview) {
[view removeFromSuperview];
}
return snapshot;
}

View File

@ -9,6 +9,7 @@
*/
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
#import <QuartzCore/QuartzCore.h>
@ -48,7 +49,7 @@
/**
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
@param layer The layer to snapshot
@param identifier An optional identifier, used is there are multiple snapshot tests in a given -test method.
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param suffixes An NSOrderedSet of strings for the different suffixes
@param tolerance The percentage of pixels that can differ and still count as an 'identical' layer
*/
@ -61,43 +62,9 @@
#define FBSnapshotVerifyViewOrLayerWithOptions(what__, viewOrLayer__, identifier__, suffixes__, tolerance__) \
{ \
NSString *referenceImageDirectory = [self getReferenceImageDirectoryWithDefault:(@ FB_REFERENCE_IMAGE_DIR)]; \
XCTAssertNotNil(referenceImageDirectory, @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.");\
XCTAssertTrue((suffixes__.count > 0), @"Suffixes set cannot be empty %@", suffixes__); \
\
BOOL testSuccess__ = NO; \
NSError *error__ = nil; \
NSMutableArray *errors__ = [NSMutableArray array]; \
\
if (self.recordMode) { \
\
NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffixes__.firstObject]; \
BOOL referenceImageSaved__ = [self compareSnapshotOf ## what__ :(viewOrLayer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \
if (!referenceImageSaved__) { \
[errors__ addObject:error__]; \
} \
} else { \
\
for (NSString *suffix__ in suffixes__) { \
NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffix__]; \
BOOL referenceImageAvailable = [self referenceImageRecordedInDirectory:referenceImagesDirectory__ identifier:(identifier__) error:&error__]; \
\
if (referenceImageAvailable) { \
BOOL comparisonSuccess__ = [self compareSnapshotOf ## what__ :(viewOrLayer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \
[errors__ removeAllObjects]; \
if (comparisonSuccess__) { \
testSuccess__ = YES; \
break; \
} else { \
[errors__ addObject:error__]; \
} \
} else { \
[errors__ addObject:error__]; \
} \
} \
} \
XCTAssertTrue(testSuccess__, @"Snapshot comparison failed: %@", errors__.firstObject); \
XCTAssertFalse(self.recordMode, @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!"); \
NSString *errorDescription = [self snapshotVerifyViewOrLayer:viewOrLayer__ identifier:identifier__ suffixes:suffixes__ tolerance:tolerance__]; \
BOOL noErrors = (errorDescription == nil); \
XCTAssertTrue(noErrors, @"%@", errorDescription); \
}
@ -147,6 +114,19 @@
- (void)setUp NS_REQUIRES_SUPER;
- (void)tearDown NS_REQUIRES_SUPER;
/**
Performs the comparison or records a snapshot of the layer if recordMode is YES.
@param viewOrLayer The UIView or CALayer to snapshot
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param suffixes An NSOrderedSet of strings for the different suffixes
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
@returns nil if the comparison (or saving of the reference image) succeeded. Otherwise it contains an error description.
*/
- (NSString *)snapshotVerifyViewOrLayer:(id)viewOrLayer
identifier:(NSString *)identifier
suffixes:(NSOrderedSet *)suffixes
tolerance:(CGFloat)tolerance;
/**
Performs the comparison or records a snapshot of the layer if recordMode is YES.
@param layer The Layer to snapshot

View File

@ -65,6 +65,62 @@
#pragma mark - Public API
- (NSString *)snapshotVerifyViewOrLayer:(id)viewOrLayer
identifier:(NSString *)identifier
suffixes:(NSOrderedSet *)suffixes
tolerance:(CGFloat)tolerance
{
if (nil == viewOrLayer) {
return @"Object to be snapshotted must not be nil";
}
NSString *referenceImageDirectory = [self getReferenceImageDirectoryWithDefault:(@ FB_REFERENCE_IMAGE_DIR)];
if (referenceImageDirectory == nil) {
return @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.";
}
if (suffixes.count == 0) {
return [NSString stringWithFormat:@"Suffixes set cannot be empty %@", suffixes];
}
BOOL testSuccess = NO;
NSError *error = nil;
NSMutableArray *errors = [NSMutableArray array];
if (self.recordMode) {
NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffixes.firstObject];
BOOL referenceImageSaved = [self _compareSnapshotOfViewOrLayer:viewOrLayer referenceImagesDirectory:referenceImagesDirectory identifier:(identifier) tolerance:tolerance error:&error];
if (!referenceImageSaved) {
[errors addObject:error];
}
} else {
for (NSString *suffix in suffixes) {
NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffix];
BOOL referenceImageAvailable = [self referenceImageRecordedInDirectory:referenceImagesDirectory identifier:(identifier) error:&error];
if (referenceImageAvailable) {
BOOL comparisonSuccess = [self _compareSnapshotOfViewOrLayer:viewOrLayer referenceImagesDirectory:referenceImagesDirectory identifier:identifier tolerance:tolerance error:&error];
[errors removeAllObjects];
if (comparisonSuccess) {
testSuccess = YES;
break;
} else {
[errors addObject:error];
}
} else {
[errors addObject:error];
}
}
}
if (!testSuccess) {
return [NSString stringWithFormat:@"Snapshot comparison failed: %@", errors.firstObject];
}
if (self.recordMode) {
return @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!";
}
return nil;
}
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
referenceImagesDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier

View File

@ -9,6 +9,7 @@
*/
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
#import <UIKit/UIKit.h>
BOOL FBSnapshotTestCaseIs64Bit(void)
@ -34,7 +35,8 @@ NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void)
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName)
{
UIDevice *device = [UIDevice currentDevice];
CGSize screenSize = [[UIApplication sharedApplication] keyWindow].bounds.size;
UIWindow *keyWindow = [[UIApplication sharedApplication] fb_strictKeyWindow];
CGSize screenSize = keyWindow.bounds.size;
NSString *os = device.systemVersion;
fileName = [NSString stringWithFormat:@"%@_%@%@_%.0fx%.0f", fileName, device.model, os, screenSize.width, screenSize.height];

View File

@ -28,6 +28,21 @@ extern NSString *const FBSnapshotTestControllerErrorDomain;
*/
extern NSString *const FBReferenceImageFilePathKey;
/**
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
*/
extern NSString *const FBReferenceImageKey;
/**
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
*/
extern NSString *const FBCapturedImageKey;
/**
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
*/
extern NSString *const FBDiffedImageKey;
/**
Provides the heavy-lifting for FBSnapshotTestCase. It loads and saves images, along with performing the actual pixel-
by-pixel comparison of images.

View File

@ -18,6 +18,9 @@
NSString *const FBSnapshotTestControllerErrorDomain = @"FBSnapshotTestControllerErrorDomain";
NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
NSString *const FBReferenceImageKey = @"FBReferenceImageKey";
NSString *const FBCapturedImageKey = @"FBCapturedImageKey";
NSString *const FBDiffedImageKey = @"FBDiffedImageKey";
typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
FBTestSnapshotFileNameTypeReference,
@ -126,25 +129,25 @@ typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
if (CGSizeEqualToSize(referenceImage.size, image.size)) {
BOOL imagesEqual = [referenceImage fb_compareWithImage:image tolerance:tolerance];
if (NULL != errorPtr) {
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
code:FBSnapshotTestControllerErrorCodeImagesDifferent
userInfo:@{
NSLocalizedDescriptionKey: @"Images different",
}];
}
return imagesEqual;
BOOL sameImageDimensions = CGSizeEqualToSize(referenceImage.size, image.size);
if (sameImageDimensions && [referenceImage fb_compareWithImage:image tolerance:tolerance]) {
return YES;
}
if (NULL != errorPtr) {
NSString *errorDescription = sameImageDimensions ? @"Images different" : @"Images different sizes";
NSString *errorReason = sameImageDimensions ? [NSString stringWithFormat:@"image pixels differed by more than %.2f%% from the reference image", tolerance * 100]
: [NSString stringWithFormat:@"referenceImage:%@, image:%@", NSStringFromCGSize(referenceImage.size), NSStringFromCGSize(image.size)];
FBSnapshotTestControllerErrorCode errorCode = sameImageDimensions ? FBSnapshotTestControllerErrorCodeImagesDifferent : FBSnapshotTestControllerErrorCodeImagesDifferentSizes;
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
code:FBSnapshotTestControllerErrorCodeImagesDifferentSizes
code:errorCode
userInfo:@{
NSLocalizedDescriptionKey: @"Images different sizes",
NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"referenceImage:%@, image:%@",
NSStringFromCGSize(referenceImage.size),
NSStringFromCGSize(image.size)],
NSLocalizedDescriptionKey: errorDescription,
NSLocalizedFailureReasonErrorKey: errorReason,
FBReferenceImageKey: referenceImage,
FBCapturedImageKey: image,
FBDiffedImageKey: [referenceImage fb_diffWithImage:image],
}];
}
return NO;
@ -279,11 +282,10 @@ typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr];
if (!imagesSame) {
[self saveFailedReferenceImage:referenceImage
testImage:snapshot
selector:selector
identifier:identifier
error:errorPtr];
NSError *saveError = nil;
if ([self saveFailedReferenceImage:referenceImage testImage:snapshot selector:selector identifier:identifier error:&saveError] == NO) {
NSLog(@"Error saving test images: %@", saveError);
}
}
return imagesSame;
}

View File

@ -8,34 +8,35 @@
*
*/
#if swift(>=3)
public extension FBSnapshotTestCase {
public func FBSnapshotVerifyView(view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes)
public func FBSnapshotVerifyView(_ view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
}
public func FBSnapshotVerifyLayer(layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes)
public func FBSnapshotVerifyLayer(_ layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
}
private func FBSnapshotVerifyViewOrLayer(viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
let envReferenceImageDirectory = self.getReferenceImageDirectoryWithDefault(FB_REFERENCE_IMAGE_DIR)
private func FBSnapshotVerifyViewOrLayer(_ viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
let envReferenceImageDirectory = self.getReferenceImageDirectory(withDefault: FB_REFERENCE_IMAGE_DIR)
var error: NSError?
var comparisonSuccess = false
if let envReferenceImageDirectory = envReferenceImageDirectory {
for suffix in suffixes {
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
if viewOrLayer.isKindOfClass(UIView) {
if viewOrLayer.isKind(of: UIView.self) {
do {
try compareSnapshotOfView(viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0)
try compareSnapshot(of: viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
comparisonSuccess = true
} catch let error1 as NSError {
error = error1
comparisonSuccess = false
}
} else if viewOrLayer.isKindOfClass(CALayer) {
} else if viewOrLayer.isKind(of: CALayer.self) {
do {
try compareSnapshotOfLayer(viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0)
try compareSnapshot(of: viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
comparisonSuccess = true
} catch let error1 as NSError {
error = error1
@ -58,9 +59,67 @@ public extension FBSnapshotTestCase {
}
}
func assert(assertion: Bool, message: String, file: String, line: UInt) {
func assert(_ assertion: Bool, message: String, file: StaticString, line: UInt) {
if !assertion {
XCTFail(message, file: file, line: line)
}
}
}
#else
public extension FBSnapshotTestCase {
public func FBSnapshotVerifyView(view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
}
public func FBSnapshotVerifyLayer(layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
}
private func FBSnapshotVerifyViewOrLayer(viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
let envReferenceImageDirectory = self.getReferenceImageDirectoryWithDefault(FB_REFERENCE_IMAGE_DIR)
var error: NSError?
var comparisonSuccess = false
if let envReferenceImageDirectory = envReferenceImageDirectory {
for suffix in suffixes {
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
if viewOrLayer.isKindOfClass(UIView) {
do {
try compareSnapshotOfView(viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
comparisonSuccess = true
} catch let error1 as NSError {
error = error1
comparisonSuccess = false
}
} else if viewOrLayer.isKindOfClass(CALayer) {
do {
try compareSnapshotOfLayer(viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
comparisonSuccess = true
} catch let error1 as NSError {
error = error1
comparisonSuccess = false
}
} else {
assertionFailure("Only UIView and CALayer classes can be snapshotted")
}
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
if comparisonSuccess || recordMode {
break
}
assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
}
} else {
XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.")
}
}
func assert(assertion: Bool, message: String, file: StaticString, line: UInt) {
if !assertion {
XCTFail(message, file: file, line: line)
}
}
}
#endif

View File

@ -1,25 +1,25 @@
{
"name": "PNGradientView",
"version": "0.1.0",
"version": "0.2.0",
"summary": "PNGradientView is a UIView subclassing with gradient support",
"homepage": "https://github.com/<GITHUB_USERNAME>/PNGradientView",
"homepage": "https://github.com/giuseppenucifora/PNGradientView",
"license": "MIT",
"authors": {
"Giuseppe Nucifora": "me@giuseppenucifora.com"
},
"source": {
"git": "https://github.com/<GITHUB_USERNAME>/PNGradientView.git",
"tag": "0.1.0"
"git": "https://github.com/giuseppenucifora/PNGradientView.git",
"tag": "0.2.0"
},
"platforms": {
"ios": "7.0"
},
"requires_arc": true,
"source_files": "Pod/Classes/**/*",
"resource_bundles": {
"PNGradientView": [
"Pod/Assets/*.png"
"frameworks": "QuartzCore",
"dependencies": {
"RZDataBinding": [
]
},
"frameworks": "QuartzCore"
}
}

View File

@ -1,15 +1,18 @@
PODS:
- Expecta (1.0.5)
- Expecta+Snapshots (2.0.0):
- Expecta (1.0.6)
- Expecta+Snapshots (3.1.1):
- Expecta (~> 1.0)
- FBSnapshotTestCase/Core (~> 2.0.3)
- FBSnapshotTestCase (2.0.7):
- FBSnapshotTestCase/SwiftSupport (= 2.0.7)
- FBSnapshotTestCase/Core (2.0.7)
- FBSnapshotTestCase/SwiftSupport (2.0.7):
- FBSnapshotTestCase/Core (~> 2.0)
- Specta (~> 1.0)
- FBSnapshotTestCase (2.1.4):
- FBSnapshotTestCase/SwiftSupport (= 2.1.4)
- FBSnapshotTestCase/Core (2.1.4)
- FBSnapshotTestCase/SwiftSupport (2.1.4):
- FBSnapshotTestCase/Core
- PNGradientView (0.1.0)
- Specta (1.0.5)
- PNGradientView (0.2.0):
- RZDataBinding
- RZDataBinding (2.1.0)
- Specta (1.0.7)
DEPENDENCIES:
- Expecta
@ -20,15 +23,16 @@ DEPENDENCIES:
EXTERNAL SOURCES:
PNGradientView:
:path: "../"
:path: ../
SPEC CHECKSUMS:
Expecta: e1c022fcd33910b6be89c291d2775b3fe27a89fe
Expecta+Snapshots: 29b38dd695bc72a0ed2bea833937d78df41943ba
FBSnapshotTestCase: 7e85180d0d141a0cf472352edda7e80d7eaeb547
PNGradientView: 21d127e76f77674eecfa0dc9859751004f485e38
Specta: ac94d110b865115fe60ff2c6d7281053c6f8e8a2
Expecta: 3b6bd90a64b9a1dcb0b70aa0e10a7f8f631667d5
Expecta+Snapshots: dcff217eef506dabd6dfdc7864ea2da321fafbb8
FBSnapshotTestCase: 094f9f314decbabe373b87cc339bea235a63e07a
PNGradientView: 3c8f5b3ddbb7b957b8ecf0c72b5ceff2b36ee24f
RZDataBinding: 6981e90ddaae2f5e02028323b1043f8c31013109
Specta: 3e1bd89c3517421982dc4d1c992503e48bd5fe66
PODFILE CHECKSUM: cd7860932a2fb17c979c3a4af42606b114244376
COCOAPODS: 1.0.0.beta.2
COCOAPODS: 1.4.0

File diff suppressed because it is too large Load Diff

22
Example/Pods/RZDataBinding/LICENSE generated Normal file
View File

@ -0,0 +1,22 @@
Copyright 2014 Raizlabs and other contributors
http://raizlabs.com/
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.

172
Example/Pods/RZDataBinding/README.md generated Normal file
View File

@ -0,0 +1,172 @@
# RZDataBinding
[![Version](https://img.shields.io/cocoapods/v/RZDataBinding.svg?style=flat)](http://cocoadocs.org/docsets/RZDataBinding)
<p align="center">
<img src="http://cl.ly/image/1r0I0x401W2m/chain.png"
alt="RZDataBinding">
</p>
## Overview
RZDataBinding is a framework designed to help maintain data integrity in your iOS or OS X app. It is built using the standard Key-Value Observation (KVO) framework, but is safer and provides additional functionality. Like KVO, RZDataBinding helps to avoid endless delegate chains by establishing direct callbacks for when an object changes state.
## Installation
Install using [CocoaPods](http://cocoapods.org) (recommended) by adding the following line to your Podfile:
`pod "RZDataBinding"`
Alternatively, download the repository and add the contents of the RZDataBinding directory to your project.
## Demo Project
An example project is available in the Example directory. You can quickly check it out with
`pod try RZDataBinding`
Or download the zip from github and run it manually.
<p align="center">
<img src="http://cl.ly/image/152x112l0i2n/rzdb.gif"
alt="RZDataBinding">
</p>
The demo shows a basic usage of RZDataBinding, but is by no means the canonical or most advanced use case.
##Usage
**Register a callback for when the keypath of an object changes:**
``` obj-c
// Register a selector to be called on a given target whenever keyPath changes on the receiver.
// Action must take either zero or exactly one parameter, an NSDictionary.
// If the method has a parameter, the dictionary will contain values for the appropriate
// RZDBChangeKeys. If keys are absent, they can be assumed to be nil. Values will not be NSNull.
- (void)rz_addTarget:(id)target
action:(SEL)action
forKeyPathChange:(NSString *)keyPath;
```
**Bind values of two objects together either directly or with a function:**
``` obj-c
// Binds the value of a given key of the receiver to the value of a key path of another object.
// When the key path of the object changes, the bound key of the receiver is also changed.
- (void)rz_bindKey:(NSString *)key
toKeyPath:(NSString *)foreignKeyPath
ofObject:(id)object;
// Same as the above method, but the binding function is first applied
// to the changed value before setting the value of the bound key.
// If nil, the identity function is assumed, making it identical to regular rz_bindKey.
- (void)rz_bindKey:(NSString *)key
toKeyPath:(NSString *)foreignKeyPath
ofObject:(id)object
withFunction:(RZDBKeyBindingFunction)bindingFunction;
```
Targets can be removed and keys unbound with corresponding removal methods, but unlike with standard KVO, you are not obligated to do so. RZDataBinding will automatically cleanup observers before objects are deallocated.
## Why not use plain KVO?
Consider the following code, which calls `nameChanged:` when a user object's name changes, and reload a collection view when the user's preferences change:
**Using KVO:**
``` obj-c
static void* const MyKVOContext = (void *)&MyKVOContext;
- (void)setupKVO
{
[self.user addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
context:MyKVOContext];
[self.user addObserver:self
forKeyPath:@"preferences"
options:kNilOptions
context:MyKVOContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object change:(NSDictionary *)change
context:(void *)context
{
if ( context == MyKVOContext ) {
if ( [object isEqual:self.user] ) {
if ( [keyPath isEqualToString:@"name"] ) {
[self nameChanged:change];
}
else if ( [keyPath isEqualToString:@"preferences"] ) {
[self.collectionView reloadData];
}
}
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)dealloc
{
[self.user removeObserver:self forKeyPath:@"name" context:MyKVOContext];
[self.user removeObserver:self forKeyPath:@"preferences" context:MyKVOContext];
}
```
**Using RZDataBinding:**
``` obj-c
- (void)setupKVO
{
[self.user rz_addTarget:self
action:@selector(nameChanged:)
forKeyPathChange:@"name"];
[self.user rz_addTarget:self.collectionView
action:@selector(reloadData)
forKeyPathChange:@"preferences"];
}
```
Aside from the obvious reduction in code, the RZDataBinding implementation demonstrates several other wins:
1. No need to manage different KVO contexts and check which object/keypath changed
2. No need to implement an instance method, meaning *any* object can be added as a target
3. No need to teardown before deallocation (standard KVO crashes if you fail to do this)
## Safe Keypaths
RZDataBinding also provides [several convenience macros](RZDataBinding/RZDBMacros.h) to create type-safe keypaths. When running in `DEBUG` mode, invalid keypaths will generate a compiler error:
``` obj-c
// Creates the keypath @"text", ensuring it exists on objects of type UILabel
RZDB_KP(UILabel, text);
// Creates @"layer.cornerRadius", ensuring the keypath exists on myView
RZDB_KP_OBJ(myView, layer.cornerRadius);
// Creates @"session.user.name", ensuring the keypath exists on self
RZDB_KP_SELF(session.user.name);
```
You should *always* use these macros instead of literal strings, because of the additional type checks they provide. Note that in production these macros simplify to literal string generation to avoid any additional overhead.
## Callback Coalescing (Advanced)
RZDataBinding also provides a coalescing mechanism for fine-tuning areas of your application that receive or send a high number of KVO notifications, which may incur a performance cost. For example, a complex view might trigger an expensive layout operation whenever one of several properties changes. Or, some work may require changing properties several times before they settle to final values. In these cases, it may be beneficial to have RZDataBinding treat a block of work as an "atomic" event. That is, supported callbacks should be coalesced and sent once, when the work completes.
[`RZDBCoalesce`](RZDataBinding/RZDBCoalesce.h) provides a block interface:
``` obj-c
[RZDBCoalesce coalesceBlock:^{
// Callbacks within this block are coalesced,
// and sent only once, after the block completes
}];
```
Callbacks are not coalesced by default, even within an `RZDBCoalesce` event. The `rz_addTarget:action:` methods can opt in to support coalescing by specifying a coalesce proxy as the callback target:
``` obj-c
[object rz_addTarget:[self rz_coalesceProxy]
action:@selector(expensiveCallback)
forKeyPathChange:RZDB_KP_OBJ(object, key.path)];
```
In this example, the intended target is `self`, but if a coalesce event is in progress these messages will be coalesced and deferred until the event completes. Note that only `rz_addTarget:action:` callbacks may support coalescing; bindings established with the `rz_bindKey:` methods will never be coalesced.
## Author
Rob Visentin, rob.visentin@raizlabs.com
## License
RZDataBinding is available under the MIT license. See the LICENSE file for more info.

View File

@ -0,0 +1,192 @@
//
// NSObject+RZDataBinding.h
//
// Created by Rob Visentin on 9/17/14.
// Copyright 2014 Raizlabs and other contributors
// http://raizlabs.com/
//
// 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 "RZDBTransforms.h"
#pragma mark - Constants and Definitions
/**
* The value for this key is the object that changed. This key is always present.
*/
OBJC_EXTERN NSString* const kRZDBChangeKeyObject;
/**
* If present, the value for this key is the previous value on a key path change.
*/
OBJC_EXTERN NSString* const kRZDBChangeKeyOld;
/**
* If present, the value for this key is the new value on a key path change, or the exisiting value for the key path for the initial call.
*/
OBJC_EXTERN NSString* const kRZDBChangeKeyNew;
/**
* The value for this key is the key path that changed value. This key is always present.
*/
OBJC_EXTERN NSString* const kRZDBChangeKeyKeyPath;
/**
* Set this to 1 (recommended) to enable automatic cleanup of observers on object deallocation.
* If enabled, it is safe to observe or bind to weak references, and there is no need to call rz_removeTarget
* or rz_unbindKey before targets or observed objects are deallocated. To achieve automatic cleanup,
* RZDB swizzles the dealloc method to ensure observers are properly invalidated. There are other ways of implementing similar
* behavior, but this has been found to be both the safest and most reliable in production.
*
* If set to 0 (not recommended), objects MUST remove themselves as targets and unbind their keys from any observed objects before being deallocated.
* Failure to do so will result in crashes (just like standard KVO). Additionally, you should not add a target to or bind keys to
* objects without first establishing a strong reference. Otherwise, the foreign object might be deallocated before the observer, causing in a crash.
* If you choose to disable global automatic cleanup by setting this to 0, you must cleanup observers manually.
*
* @see rz_cleanupObservers
*/
#ifndef RZDB_AUTOMATIC_CLEANUP
#define RZDB_AUTOMATIC_CLEANUP 1
#endif
#pragma mark - NSObject+RZDataBinding interface
@interface NSObject (RZDataBinding)
/**
* Register a selector to be called on a given target whenever keyPath changes on the receiver. The selector is not called immediately.
*
* @param target The object on which to call the action selector. Must be non-nil. This object is not retained.
* @param action The selector to call on the target. Must not be NULL. The method must take either zero or exactly one parameter, an NSDictionary, and have a void return type. If the method has an NSDictionary parameter, the dictionary will contain values for the appropriate RZDBChangeKeys. If keys are absent, they can be assumed to be nil. Values will not be NSNull.
* @param keyPath The key path of the receiver for which changes should trigger an action. Must be KVC compliant.
*
* @see RZDB_KP macro for creating keypaths.
*/
- (void)rz_addTarget:(id)target action:(SEL)action forKeyPathChange:(NSString *)keyPath;
/**
* Register a selector to be called on a given target whenever keyPath changes on the receiver.
*
* @param target The object on which to call the action selector. Must be non-nil. This object is not retained.
* @param action The selector to call on the target. Must not be NULL. The method must take either zero or exactly one parameter, an NSDictionary, and have a void return type. If the method has an NSDictionary parameter, the dictionary will contain values for the appropriate RZDBChangeKeys. If keys are absent, they can be assumed to be nil. Values will not be NSNull.
* @param keyPath The key path of the receiver for which changes should trigger an action. Must be KVC compliant.
* @param callImmediately If YES, the action is also called immediately before this method returns. In this case the change dictionary, if present, will not contain a value for kRZDBChangeKeyOld.
*
* @see RZDB_KP macro for creating keypaths.
*/
- (void)rz_addTarget:(id)target action:(SEL)action forKeyPathChange:(NSString *)keyPath callImmediately:(BOOL)callImmediately;
/**
* A convenience method that calls rz_addTarget:action:forKeyPathChange: for each keyPath in the keyPaths array.
*
* @param target The object on which to call the action selector. Must be non-nil. This object is not retained.
* @param action The selector to call on the target. Must not be NULL. See rz_addTarget documentation for more details.
* @param keyPaths An array of key paths that should trigger an action. Each key path must be KVC compliant.
*
* @note The action is not called immediately.
*
* @see RZDB_KP macro for creating keypaths.
*/
- (void)rz_addTarget:(id)target action:(SEL)action forKeyPathChanges:(NSArray *)keyPaths;
/**
* A convenience method that calls rz_addTarget:action:forKeyPathChange: for each keyPath in the keyPaths array.
*
* @param target The object on which to call the action selector. Must be non-nil. This object is not retained.
* @param action The selector to call on the target. Must not be NULL. See rz_addTarget documentation for more details.
* @param keyPaths An array of key paths that should trigger an action. Each key path must be KVC compliant.
* @param callImmediately If YES, the action is also called immediately before this method returns. If the action takes no arguments, it will be called once. If the action takes a change dictonary parameter, it will be called once for each keypath, with the appropriate change dict. Note that in this case the dictionary will not contain a value for kRZDBChangeKeyOld.
*
* @see RZDB_KP macro for creating keypaths.
*/
- (void)rz_addTarget:(id)target action:(SEL)action forKeyPathChanges:(NSArray *)keyPaths callImmediately:(BOOL)callImmediately;
/**
* Removes previously registered target/action pairs so that the actions are no longer called when the receiver changes value for keyPath.
*
* @param target The target to remove. Must be non-nil.
* @param action The action to remove. Pass NULL to remove all actions registered for the target.
* @param keyPath The key path to remove the target/action pair for.
*
* @note If RZDB_AUTOMATIC_CLEANUP is enabled, then there is obligation to call this method before either the target or receiver are deallocated.
*
* @see RZDB_KP macro for creating keypaths.
*/
- (void)rz_removeTarget:(id)target action:(SEL)action forKeyPathChange:(NSString *)keyPath;
/**
* Binds the value of a given key of the receiver to the value of a key path of another object. When the key path of the object changes, the bound key of the receiver is set to the same value. The receiver's value for the key will match the value for the object's foreign key path before this method returns.
*
* @param key The receiver's key whose value should be bound to the value of a foreign key path. Must be KVC compliant.
* @param foreignKeyPath A key path of another object to which the receiver's key value should be bound. Must be KVC compliant.
* @param object An object with a key path that the receiver should bind to.
*
* @note If binding a primitive-type key to a keypath that may hit a nil object, you MUST use
* rz_bindKey:toKeyPath:ofObject:withTransform: instead. This is because attempting to set a nil
* value to a primitive-type using KVC causes a crash. In this case it may be helpful to use
* one of the default RZDBTransforms.
*
* @see RZDB_KP macro for creating keypaths.
*/
- (void)rz_bindKey:(NSString *)key toKeyPath:(NSString *)foreignKeyPath ofObject:(id)object;
/**
* Binds the value of a given key of the receiver to the value of a key path of another object. When the key path of the object changes, the binding transform is invoked and the bound key of the receiver is set to the transform's return value. The receiver's value for the key will be set before this method returns.
*
* @param key The receiver's key whose value should be bound to the value of a foreign key path. Must be KVC compliant.
* @param foreignKeyPath A key path of another object to which the receiver's key value should be bound. Must be KVC compliant.
* @param object An object with a key path that the receiver should bind to.
* @param bindingTransform The transform to apply to changed values before setting the value of the bound key. If nil, the identity transform is assumed, making this method identical to regular rz_bindKey:.
* You may use the constant RZDBTransforms provided for convenience.
*
* @see RZDB_KP macro for creating keypaths, and RZDBTransforms for constant transforms.
*/
- (void)rz_bindKey:(NSString *)key toKeyPath:(NSString *)foreignKeyPath ofObject:(id)object withTransform:(RZDBKeyBindingTransform)bindingTransform;
/**
* Unbinds the given key of the receiver from the key path of another object.
*
* @param key The key to unbind.
* @param foreignKeyPath The key path that the key should be unbound from.
* @param object The object that the receiver should be unbound from.
*
* @note If RZDB_AUTOMATIC_CLEANUP is enabled, then there is no obligation to call this method before either the receiver or the foreign object are deallocated.
*
* @see RZDB_KP macro for creating keypaths.
*/
- (void)rz_unbindKey:(NSString *)key fromKeyPath:(NSString *)foreignKeyPath ofObject:(id)object;
#if !RZDB_AUTOMATIC_CLEANUP
/**
* Removes all callbacks and bindings both to and from the receiver.
* This method essentially resets the receiver to a pristine state with respect to RZDataBinding.
*
* If RZDB_AUTOMATIC_CLEANUP is not enabled, you MUST call this method on any
* observing or observed object it is deallocated. Failure to do so will result in a crash (just like standard KVO).
*
* @note This method breaks all bindings and callbacks on the receiver, including those created
* by other clients. Therefore it is only safe to call this method from the receiver's dealloc method.
*/
- (void)rz_cleanupObservers;
#endif
@end

View File

@ -0,0 +1,543 @@
//
// NSObject+RZDataBinding.m
//
// Created by Rob Visentin on 9/17/14.
// Copyright 2014 Raizlabs and other contributors
// http://raizlabs.com/
//
// 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 <objc/runtime.h>
#import <objc/message.h>
#import "NSObject+RZDataBinding.h"
#import "RZDBMacros.h"
@class RZDBObserver;
@class RZDBObserverContainer;
// public change keys
NSString* const kRZDBChangeKeyObject = @"RZDBChangeObject";
NSString* const kRZDBChangeKeyOld = @"RZDBChangeOld";
NSString* const kRZDBChangeKeyNew = @"RZDBChangeNew";
NSString* const kRZDBChangeKeyKeyPath = @"RZDBChangeKeyPath";
// private change keys
static NSString* const kRZDBChangeKeyBoundKey = @"_RZDBChangeBoundKey";
static NSString* const kRZDBChangeKeyBindingTransformKey = @"_RZDBChangeBindingTransform";
static void* const kRZDBSwizzledDeallocKey = (void *)&kRZDBSwizzledDeallocKey;
static void* const kRZDBKVOContext = (void *)&kRZDBKVOContext;
static void* const kRZDBRegisteredObserversKey = (void *)&kRZDBRegisteredObserversKey;
static void* const kRZDBDependentObserversKey = (void *)&kRZDBDependentObserversKey;
#define RZDBNotNull(obj) ((obj) != nil && ![(obj) isEqual:[NSNull null]])
#define rz_registeredObservers(obj) objc_getAssociatedObject(obj, kRZDBRegisteredObserversKey)
#define rz_setRegisteredObservers(obj, observers) objc_setAssociatedObject(obj, kRZDBRegisteredObserversKey, observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
#define rz_dependentObservers(obj) objc_getAssociatedObject(obj, kRZDBDependentObserversKey)
#define rz_setDependentObservers(obj, observers) objc_setAssociatedObject(obj, kRZDBDependentObserversKey, observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
#pragma mark - RZDataBinding_Private interface
// methods used to implement RZDB_AUTOMATIC_CLEANUP
BOOL rz_requiresDeallocSwizzle(Class class);
void rz_swizzleDeallocIfNeeded(Class class);
@interface NSObject (RZDataBinding_Private)
- (void)rz_addTarget:(id)target action:(SEL)action boundKey:(NSString *)boundKey bindingTransform:(RZDBKeyBindingTransform)bindingTransform forKeyPath:(NSString *)keyPath withOptions:(NSKeyValueObservingOptions)options;
- (void)rz_removeTarget:(id)target action:(SEL)action boundKey:(NSString *)boundKey forKeyPath:(NSString *)keyPath;
- (void)rz_observeBoundKeyChange:(NSDictionary *)change;
- (void)rz_setBoundKey:(NSString *)key withValue:(id)value transform:(RZDBKeyBindingTransform)transform;
@end
#pragma mark - RZDBObserver interface
@interface RZDBObserver : NSObject;
@property (assign, nonatomic) __unsafe_unretained NSObject *observedObject;
@property (copy, nonatomic) NSString *keyPath;
@property (copy, nonatomic) NSString *boundKey;
@property (assign, nonatomic) NSKeyValueObservingOptions observationOptions;
@property (assign, nonatomic) __unsafe_unretained id target;
@property (assign, nonatomic) SEL action;
@property (strong, nonatomic) NSMethodSignature *methodSignature;
@property (copy, nonatomic) RZDBKeyBindingTransform bindingTransform;
- (instancetype)initWithObservedObject:(NSObject *)observedObject keyPath:(NSString *)keyPath observationOptions:(NSKeyValueObservingOptions)observingOptions;
- (void)setTarget:(id)target action:(SEL)action boundKey:(NSString *)boundKey bindingTransform:(RZDBKeyBindingTransform)bindingTransform;
- (void)invalidate;
@end
#pragma mark - RZDBObserverContainer interface
@interface RZDBObserverContainer : NSObject
+ (instancetype)strongContainer;
+ (instancetype)weakContainer;
- (void)addObserver:(RZDBObserver *)observer;
- (void)removeObserver:(RZDBObserver *)observer;
- (void)enumerateObserversUsingBlock:(void (^)(RZDBObserver *observer, BOOL *stop))block;
@end
#pragma mark - RZDataBinding implementation
@implementation NSObject (RZDataBinding)
- (void)rz_addTarget:(id)target action:(SEL)action forKeyPathChange:(NSString *)keyPath
{
[self rz_addTarget:target action:action forKeyPathChange:keyPath callImmediately:NO];
}
- (void)rz_addTarget:(id)target action:(SEL)action forKeyPathChange:(NSString *)keyPath callImmediately:(BOOL)callImmediately
{
NSParameterAssert(target);
NSParameterAssert(action);
NSKeyValueObservingOptions observationOptions = kNilOptions;
if ( [target methodSignatureForSelector:action].numberOfArguments > 2 ) {
observationOptions |= NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
}
if ( callImmediately ) {
observationOptions |= NSKeyValueObservingOptionInitial;
}
[self rz_addTarget:target action:action boundKey:nil bindingTransform:nil forKeyPath:keyPath withOptions:observationOptions];
}
- (void)rz_addTarget:(id)target action:(SEL)action forKeyPathChanges:(NSArray *)keyPaths
{
[self rz_addTarget:target action:action forKeyPathChanges:keyPaths callImmediately:NO];
}
- (void)rz_addTarget:(id)target action:(SEL)action forKeyPathChanges:(NSArray *)keyPaths callImmediately:(BOOL)callImmediately
{
BOOL callMultiple = NO;
if ( callImmediately ) {
callMultiple = [target methodSignatureForSelector:action].numberOfArguments > 2;
}
[keyPaths enumerateObjectsUsingBlock:^(NSString *keyPath, NSUInteger idx, BOOL *stop) {
[self rz_addTarget:target action:action forKeyPathChange:keyPath callImmediately:callMultiple];
}];
if ( callImmediately && !callMultiple ) {
((void(*)(id, SEL))objc_msgSend)(target, action);
}
}
- (void)rz_removeTarget:(id)target action:(SEL)action forKeyPathChange:(NSString *)keyPath
{
[self rz_removeTarget:target action:action boundKey:nil forKeyPath:keyPath];
}
- (void)rz_bindKey:(NSString *)key toKeyPath:(NSString *)foreignKeyPath ofObject:(id)object
{
[self rz_bindKey:key toKeyPath:foreignKeyPath ofObject:object withTransform:nil];
}
- (void)rz_bindKey:(NSString *)key toKeyPath:(NSString *)foreignKeyPath ofObject:(id)object withTransform:(RZDBKeyBindingTransform)bindingTransform
{
NSParameterAssert(key);
NSParameterAssert(foreignKeyPath);
if ( object != nil ) {
@try {
id val = [object valueForKeyPath:foreignKeyPath];
[self rz_setBoundKey:key withValue:val transform:bindingTransform];
}
@catch (NSException *exception) {
[NSException raise:NSInvalidArgumentException format:@"RZDataBinding cannot bind key:%@ to key path:%@ of object:%@. Reason: %@", key, foreignKeyPath, [object description], exception.reason];
}
[object rz_addTarget:self action:@selector(rz_observeBoundKeyChange:) boundKey:key bindingTransform:bindingTransform forKeyPath:foreignKeyPath withOptions:NSKeyValueObservingOptionNew];
}
}
- (void)rz_unbindKey:(NSString *)key fromKeyPath:(NSString *)foreignKeyPath ofObject:(id)object
{
[object rz_removeTarget:self action:@selector(rz_observeBoundKeyChange:) boundKey:key forKeyPath:foreignKeyPath];
}
@end
#pragma mark - RZDataBinding_Private implementation
@implementation NSObject (RZDataBinding_Private)
- (void)rz_addTarget:(id)target action:(SEL)action boundKey:(NSString *)boundKey bindingTransform:(RZDBKeyBindingTransform)bindingTransform forKeyPath:(NSString *)keyPath withOptions:(NSKeyValueObservingOptions)options
{
RZDBObserverContainer *registeredObservers = nil;
RZDBObserverContainer *dependentObservers = nil;
RZDBObserver *observer = [[RZDBObserver alloc] initWithObservedObject:self keyPath:keyPath observationOptions:options];
[observer setTarget:target action:action boundKey:boundKey bindingTransform:bindingTransform];
@synchronized (self) {
registeredObservers = rz_registeredObservers(self);
if ( registeredObservers == nil ) {
registeredObservers = [RZDBObserverContainer strongContainer];
rz_setRegisteredObservers(self, registeredObservers);
}
}
[registeredObservers addObserver:observer];
@synchronized (target) {
dependentObservers = rz_dependentObservers(target);
if ( dependentObservers == nil ) {
dependentObservers = [RZDBObserverContainer weakContainer];
rz_setDependentObservers(target, dependentObservers);
}
}
[dependentObservers addObserver:observer];
#if RZDB_AUTOMATIC_CLEANUP
rz_swizzleDeallocIfNeeded([self class]);
rz_swizzleDeallocIfNeeded([target class]);
#endif
}
- (void)rz_removeTarget:(id)target action:(SEL)action boundKey:(NSString *)boundKey forKeyPath:(NSString *)keyPath
{
[rz_registeredObservers(self) enumerateObserversUsingBlock:^(RZDBObserver *observer, BOOL *stop) {
BOOL targetsEqual = (target == observer.target);
BOOL actionsEqual = (action == NULL || action == observer.action);
BOOL boundKeysEqual = (boundKey == observer.boundKey || [boundKey isEqualToString:observer.boundKey]);
BOOL keyPathsEqual = [keyPath isEqualToString:observer.keyPath];
BOOL allEqual = (targetsEqual && actionsEqual && boundKeysEqual && keyPathsEqual);
if ( allEqual ) {
[observer invalidate];
}
}];
}
- (void)rz_observeBoundKeyChange:(NSDictionary *)change
{
NSString *boundKey = change[kRZDBChangeKeyBoundKey];
if ( boundKey != nil ) {
id value = change[kRZDBChangeKeyNew];
[self rz_setBoundKey:boundKey withValue:value transform:change[kRZDBChangeKeyBindingTransformKey]];
}
}
- (void)rz_setBoundKey:(NSString *)key withValue:(id)value transform:(RZDBKeyBindingTransform)transform
{
id currentValue = [self valueForKey:key];
if ( transform != nil ) {
value = transform(value);
}
if ( currentValue != value && ![currentValue isEqual:value] ) {
[self setValue:value forKey:key];
}
}
- (void)rz_cleanupObservers
{
[rz_registeredObservers(self) enumerateObserversUsingBlock:^(RZDBObserver *obs, BOOL *stop) {
[obs invalidate];
}];
[rz_dependentObservers(self) enumerateObserversUsingBlock:^(RZDBObserver *obs, BOOL *stop) {
[obs invalidate];
}];
}
@end
#pragma mark - RZDBObserver implementation
@implementation RZDBObserver
- (instancetype)initWithObservedObject:(NSObject *)observedObject keyPath:(NSString *)keyPath observationOptions:(NSKeyValueObservingOptions)observingOptions
{
self = [super init];
if ( self != nil ) {
_observedObject = observedObject;
_keyPath = keyPath;
_observationOptions = observingOptions;
}
return self;
}
- (void)setTarget:(id)target action:(SEL)action boundKey:(NSString *)boundKey bindingTransform:(RZDBKeyBindingTransform)bindingTransform
{
self.target = target;
self.action = action;
self.methodSignature = [target methodSignatureForSelector:action];
self.boundKey = boundKey;
self.bindingTransform = bindingTransform;
[self.observedObject addObserver:self forKeyPath:self.keyPath options:self.observationOptions context:kRZDBKVOContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ( context == kRZDBKVOContext ) {
id target = nil;
SEL action = NULL;
NSDictionary *changeDict = nil;
@synchronized (self) {
target = self.target;
action = self.action;
if ( self.methodSignature.numberOfArguments > 2 ) {
changeDict = [self changeDictForKVOChange:change];
}
}
if ( changeDict != nil ) {
((void(*)(id, SEL, NSDictionary *))objc_msgSend)(target, action, changeDict);
}
else {
((void(*)(id, SEL))objc_msgSend)(target, action);
}
}
}
- (NSDictionary *)changeDictForKVOChange:(NSDictionary *)kvoChange
{
NSMutableDictionary *changeDict = [NSMutableDictionary dictionary];
if ( self.observedObject != nil ) {
changeDict[kRZDBChangeKeyObject] = self.observedObject;
}
if ( RZDBNotNull(kvoChange[NSKeyValueChangeOldKey]) ) {
changeDict[kRZDBChangeKeyOld] = kvoChange[NSKeyValueChangeOldKey];
}
if ( RZDBNotNull(kvoChange[NSKeyValueChangeNewKey]) ) {
changeDict[kRZDBChangeKeyNew] = kvoChange[NSKeyValueChangeNewKey];
}
if ( self.keyPath != nil ) {
changeDict[kRZDBChangeKeyKeyPath] = self.keyPath;
}
if ( self.boundKey != nil ) {
changeDict[kRZDBChangeKeyBoundKey] = self.boundKey;
}
if ( self.bindingTransform != nil ) {
changeDict[kRZDBChangeKeyBindingTransformKey] = self.bindingTransform;
}
return [changeDict copy];
}
- (void)invalidate
{
id observedObject = self.observedObject;
[rz_dependentObservers(self.target) removeObserver:self];
[rz_registeredObservers(observedObject) removeObserver:self];
// KVO throws an exception when removing an observer that was never added.
// This should never be a problem given how things are setup, but make sure to avoid a crash.
@try {
[observedObject removeObserver:self forKeyPath:self.keyPath context:kRZDBKVOContext];
}
@catch (__unused NSException *exception) {
RZDBLog(@"RZDataBinding attempted to remove an observer from object:%@, but the observer was never added. This shouldn't have happened, but won't affect anything going forward.", observedObject);
}
@synchronized (self) {
self.observedObject = nil;
self.target = nil;
self.action = NULL;
self.methodSignature = nil;
}
}
@end
#pragma mark - RZDBObserverContainer implementation
@implementation RZDBObserverContainer {
NSHashTable *_observers;
}
+ (instancetype)strongContainer
{
return [[self alloc] initWithBackingStore:[NSHashTable hashTableWithOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality]];
}
+ (instancetype)weakContainer
{
return [[self alloc] initWithBackingStore:[NSHashTable weakObjectsHashTable]];
}
- (instancetype)initWithBackingStore:(NSHashTable *)backingStore
{
self = [super init];
if ( self != nil ) {
_observers = backingStore;
}
return self;
}
- (void)addObserver:(RZDBObserver *)observer
{
@synchronized (self) {
[_observers addObject:observer];
}
}
- (void)removeObserver:(RZDBObserver *)observer
{
@synchronized (self) {
[_observers removeObject:observer];
}
}
- (void)enumerateObserversUsingBlock:(void (^)(RZDBObserver *, BOOL *))block
{
@synchronized (self) {
NSHashTable *observers = ^{
@synchronized (_observers) {
return [_observers copy];
}
}();
BOOL stop = NO;
for ( RZDBObserver *observer in observers ) {
if ( observer != nil ) {
block(observer, &stop);
}
if ( stop ) {
break;
}
}
}
}
@end
// a class doesn't need dealloc swizzled if it or a superclass has been swizzled already
BOOL rz_requiresDeallocSwizzle(Class class)
{
BOOL swizzled = NO;
for ( Class currentClass = class; !swizzled && currentClass != nil; currentClass = class_getSuperclass(currentClass) ) {
swizzled = [objc_getAssociatedObject(currentClass, kRZDBSwizzledDeallocKey) boolValue];
}
return !swizzled;
}
// In order for automatic cleanup to work, observers must be removed before deallocation.
// This method ensures that rz_cleanupObservers is called in the dealloc of classes of objects
// that are used in RZDataBinding.
void rz_swizzleDeallocIfNeeded(Class class)
{
static SEL deallocSEL = NULL;
static SEL cleanupSEL = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
deallocSEL = sel_getUid("dealloc");
cleanupSEL = sel_getUid("rz_cleanupObservers");
});
@synchronized (class) {
if ( !rz_requiresDeallocSwizzle(class) ) {
// dealloc swizzling already resolved
return;
}
objc_setAssociatedObject(class, kRZDBSwizzledDeallocKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
Method dealloc = NULL;
// search instance methods of the class (does not search superclass methods)
unsigned int n;
Method *methods = class_copyMethodList(class, &n);
for ( unsigned int i = 0; i < n; i++ ) {
if ( method_getName(methods[i]) == deallocSEL ) {
dealloc = methods[i];
break;
}
}
free(methods);
if ( dealloc == NULL ) {
Class superclass = class_getSuperclass(class);
// class does not implement dealloc, so implement it directly
class_addMethod(class, deallocSEL, imp_implementationWithBlock(^(__unsafe_unretained id self) {
// cleanup RZDB observers
((void(*)(id, SEL))objc_msgSend)(self, cleanupSEL);
// ARC automatically calls super when dealloc is implemented in code,
// but when provided our own dealloc IMP we have to call through to super manually
struct objc_super superStruct = (struct objc_super){ self, superclass };
((void (*)(struct objc_super*, SEL))objc_msgSendSuper)(&superStruct, deallocSEL);
}), method_getTypeEncoding(dealloc));
}
else {
// class implements dealloc, so extend the existing implementation
__block IMP deallocIMP = method_setImplementation(dealloc, imp_implementationWithBlock(^(__unsafe_unretained id self) {
// cleanup RZDB observers
((void(*)(id, SEL))objc_msgSend)(self, cleanupSEL);
// invoke the original dealloc IMP
((void(*)(id, SEL))deallocIMP)(self, deallocSEL);
}));
}
}

View File

@ -0,0 +1,111 @@
//
// RZDBCoalesce.h
//
// Created by Rob Visentin on 4/1/15.
// Copyright 2014 Raizlabs and other contributors
// http://raizlabs.com/
//
// 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/Foundation.h>
#pragma mark - NSObject+RZDBCoalesce interface
/**
* Provides an interface for supporting coalesced data binding callbacks.
*/
@interface NSObject (RZDBCoalesce)
/**
* Returns a proxy object that will participate in coalescing events.
*
* Use [target rz_coalesceProxy] as the target of the rz_addTarget:action: methods to support
* coalescing for a target/action pair. If the callback occurs during a an RZDBCoalesce event, it will be
* coalesced and deferred until the end RZDBCoalesce is committed.
*
* @note You should generally not call methods on the coalesce proxy directly.
* The proxy will treat any 0-arg or 1-dictionary-arg methods with void return type as RZDB callbacks,
* and coalesce them accordingly.
*
* @see RZDBCoalesce for how to begin/end coalesce events.
*
* @return A proxy object to use as a data binding target in the rz_addTarget:action: methods.
*/
- (id)rz_coalesceProxy;
@end
#pragma mark - RZDBCoalesce interface
/**
* There may be chunks of work that should be completed "atomically" with respect to RZDataBinding.
* That is, actions registered using the rz_addTarget:action: methods that support coalescing
* will be fired once, when the work is completed.
*
* The RZDBCoalesce object provides class methods to manage coalescing events.
*
* Coalescing is an advanced feature that should generally only be used if you encounter a performance issue,
* or find some other requirement for it.
*
* @see NSObject+RZDBCoalesce for how to support coalesced callbacks.
*/
@interface RZDBCoalesce : NSObject
/**
* Begin coalescing events for the current thread.
* Changes that occur during the coalesce that would trigger actions registered to coalesce proxies with
* the rz_addTarget:action: methods are instead coalesced and executed once when the coalesce ends.
*
* Every call to +begin MUST be balanced by a call to +commit on the same thread.
* It is fine to begin a coalesce while already coalescing--
* the coalesce will simply not end until both matching commits are hit.
*
* @note Bindings that occur during a coalesce still occur immediately.
*
* @see NSObject+RZDBCoalesce for how to support coalesced callbacks.
*/
+ (void)begin;
/**
* Commit the current coalesce, sending the coalesced change callbacks.
* If this commit closes a nested coalesce, callbacks are not sent until the outermost coalesce is committed.
*
* Calling this method from outside a coalesce has no effect.
*/
+ (void)commit;
/**
* Convenience method that first calls +begin, then executes the block, then calls +commit.
* You should prefer this method where possible to avoid programmer error (i.e. forgetting to call +commit).
*
* @param coalesceBlock The block to execute inside a coalesce. Must be non-nil.
*
* @see NSObject+RZDBCoalesce for how to support coalesced callbacks.
*/
+ (void)coalesceBlock:(void (^)())coalesceBlock;
/**
* Cannot instantiate RZDBCoalesce directly. Use the class methods instead.
*/
- (instancetype)init __attribute__((unavailable("Cannot instantiate RZDBCoalesce directly. Use the class methods instead.")));
@end

View File

@ -0,0 +1,297 @@
//
// RZDBCoalesce.m
//
// Created by Rob Visentin on 4/1/15.
// Copyright 2014 Raizlabs and other contributors
// http://raizlabs.com/
//
// 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 <objc/runtime.h>
#import <objc/message.h>
#import "RZDBCoalesce.h"
#import "NSObject+RZDataBinding.h"
static NSString* const kRZDBCoalesceStorageKey = @"RZDBCoalesce";
#pragma mark - RZDBNotification interface
@interface RZDBNotification : NSObject
@property (weak, nonatomic) id target;
@property (assign, nonatomic) SEL action;
@property (strong, nonatomic) NSMutableDictionary *changeDict;
+ (instancetype)notificationWithInvocation:(NSInvocation *)invocation;
- (void)send;
@end
#pragma mark - RZDBCoalesceProxy interface
@interface RZDBCoalesceProxy : NSProxy
@property (assign, nonatomic) __unsafe_unretained id representedObject;
+ (instancetype)proxyForObject:(id)object;
@end
#pragma mark - RZDBCoalesce private interface
@interface RZDBCoalesce ()
@property (assign, nonatomic) NSInteger beginCount;
@property (strong, nonatomic, readonly) NSMutableArray *notifications;
@end
#pragma mark - RZDBCoalesce implementation
@implementation RZDBCoalesce
+ (void)begin
{
RZDBCoalesce *current = [self currentCoalesce];
if ( current == nil ) {
[self setCurrentCoalesce:[[self alloc] init]];
}
else {
current.beginCount++;
}
}
+ (void)commit
{
RZDBCoalesce *current = [self currentCoalesce];
current.beginCount--;
if ( current.beginCount == 0 ) {
[self setCurrentCoalesce:nil];
[current.notifications enumerateObjectsUsingBlock:^(RZDBNotification *notification, NSUInteger idx, BOOL *stop) {
[notification send];
}];
}
}
+ (void)coalesceBlock:(void (^)())coalesceBlock
{
NSParameterAssert(coalesceBlock);
[RZDBCoalesce begin];
coalesceBlock();
[RZDBCoalesce commit];
}
#pragma mark - private methods
+ (RZDBCoalesce *)currentCoalesce
{
return [NSThread currentThread].threadDictionary[kRZDBCoalesceStorageKey];
}
+ (void)setCurrentCoalesce:(RZDBCoalesce *)current
{
if ( current != nil ) {
[NSThread currentThread].threadDictionary[kRZDBCoalesceStorageKey] = current;
}
else {
[[NSThread currentThread].threadDictionary removeObjectForKey:kRZDBCoalesceStorageKey];
}
}
- (instancetype)init
{
self = [super init];
if ( self ) {
_beginCount = 1;
_notifications = [NSMutableArray array];
}
return self;
}
- (void)addNotification:(RZDBNotification *)notification
{
NSUInteger existingIdx = [self.notifications indexOfObjectPassingTest:^BOOL(RZDBNotification *existing, NSUInteger idx, BOOL *stop) {
return [notification isEqual:existing];
}];
if ( existingIdx != NSNotFound ) {
RZDBNotification *existing = self.notifications[existingIdx];
if ( notification.changeDict[kRZDBChangeKeyNew] != nil ) {
existing.changeDict[kRZDBChangeKeyNew] = notification.changeDict[kRZDBChangeKeyNew];
}
else {
[existing.changeDict removeObjectForKey:kRZDBChangeKeyNew];
}
}
else {
[self.notifications addObject:notification];
}
}
@end
#pragma mark - RZDBNotification implementation
@implementation RZDBNotification
+ (instancetype)notificationWithInvocation:(NSInvocation *)invocation
{
RZDBNotification *notification = nil;
NSMethodSignature *methodSig = invocation.methodSignature;
// rzdb callbacks must have void return type, and
// if there are any arguments, the first and only argument must be a dictionary
if ( methodSig.methodReturnLength == 0 && (methodSig.numberOfArguments == 2 || methodSig.numberOfArguments == 3) ) {
notification = [[self alloc] init];
notification.target = invocation.target;
notification.action = invocation.selector;
if ( methodSig.numberOfArguments == 3 && strcmp([methodSig getArgumentTypeAtIndex:2], @encode(id)) == 0 ) {
__unsafe_unretained id arg1;
[invocation getArgument:&arg1 atIndex:2];
if ( [arg1 isKindOfClass:[NSDictionary class]] ) {
notification.changeDict = [arg1 mutableCopy];
}
else {
notification = nil;
}
}
}
return notification;
}
- (BOOL)isEqual:(id)object
{
BOOL equal = [super isEqual:object];
if ( !equal && [object isKindOfClass:[RZDBNotification class]] ) {
RZDBNotification *other = (RZDBNotification *)object;
equal = (self.target == other.target) &&
(self.action == other.action) &&
(self.changeDict == other.changeDict ||
(self.changeDict[kRZDBChangeKeyObject] == other.changeDict[kRZDBChangeKeyObject] &&
[self.changeDict[kRZDBChangeKeyKeyPath] isEqualToString:other.changeDict[kRZDBChangeKeyKeyPath]]
)
);
}
return equal;
}
- (void)send
{
if ( self.target != nil && self.action != NULL ) {
if ( self.changeDict != nil ) {
((void(*)(id, SEL, NSDictionary *))objc_msgSend)(self.target, self.action, [self.changeDict copy]);
}
else {
((void(*)(id, SEL))objc_msgSend)(self.target, self.action);
}
}
}
@end
#pragma mark - NSObject+RZDBCoalesce implementation
@implementation NSObject (RZDBCoalesce)
- (id)rz_coalesceProxy
{
@synchronized (self) {
id proxy = objc_getAssociatedObject(self, _cmd);
if ( proxy == nil ) {
proxy = [RZDBCoalesceProxy proxyForObject:self];
objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return proxy;
}
}
@end
#pragma mark - RZDBCoalesceProxy implementation
@implementation RZDBCoalesceProxy
+ (instancetype)proxyForObject:(id)object
{
RZDBCoalesceProxy *proxy = [self alloc];
proxy.representedObject = object;
return proxy;
}
- (Class)class
{
return [self.representedObject class];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.representedObject methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
invocation.target = self.representedObject;
RZDBCoalesce *currentCoalesce = [RZDBCoalesce currentCoalesce];
if ( currentCoalesce != nil ) {
// notificaton will be non-nil for a valid RZDataBinding callback
RZDBNotification *notification = [RZDBNotification notificationWithInvocation:invocation];
if ( notification != nil ) {
[currentCoalesce addNotification:notification];
}
else {
[invocation invoke];
}
}
else {
[invocation invoke];
}
}
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
[self.representedObject doesNotRecognizeSelector:aSelector];
}
@end

View File

@ -0,0 +1,103 @@
//
// RZDBMacros.h
//
// Created by Rob Visentin on 3/18/15.
// Copyright 2014 Raizlabs and other contributors
// http://raizlabs.com/
//
// 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.
#ifndef __RZDBMacros__
#define __RZDBMacros__
////////////////////////////////////////
//// ////
//// Logging ////
//// ////
////////////////////////////////////////
/**
* The method that should be used to log errors in RZDataBinding that are non-fatal,
* but may be useful in debugging. You might choose for example to `#define RZDBLog DDLogInfo`.
* The method defined must take exactly two parameters, a format string, and a variable list of arguments.
* By default, log messages are sent using NSLog.
*/
#ifndef RZDBLog
#define RZDBLog NSLog
#endif
////////////////////////////////////////
//// ////
//// Key Path creation Macros ////
//// ////
////////////////////////////////////////
/**
* Convenience macros for creating keypaths. An invalid keypath will throw a compile-time error when compiling in debug mode.
*
* The first parameter of these macros is used only for compile-time validation of the keypath.
*
* @return An NSString containing the keypath.
*
* @example RZDB_KP(NSObject, description.length) -> @"description.length"
* RZDB_KP_OBJ(MyLabel, text) -> @"text"
* RZDB_KP_SELF(user.firstName) -> @"user.firstName"
*/
#if DEBUG
#define RZDB_KP(Classname, keypath) ({\
Classname *_rzdb_keypath_obj; \
__unused __typeof(_rzdb_keypath_obj.keypath) _rzdb_keypath_prop; \
@#keypath; \
})
#define RZDB_KP_OBJ(object, keypath) ({\
__typeof(object) _rzdb_keypath_obj; \
__unused __typeof(_rzdb_keypath_obj.keypath) _rzdb_keypath_prop; \
@#keypath; \
})
#define RZDB_KP_PROTOCOL(ProtocolName, keypath) ({\
id<ProtocolName> _rzdb_keypath_obj; \
__unused __typeof(_rzdb_keypath_obj.keypath) _rzdb_keypath_prop; \
@#keypath; \
})
#else
#define RZDB_KP(Classname, keypath) (@#keypath)
#define RZDB_KP_OBJ(self, keypath) (@#keypath)
#define RZDB_KP_PROTOCOL(ProtocolName, keypath) (@#keypath)
#endif
/**
* @note This macro will implicitly retain self from within blocks while running in debug mode.
* The safe way to generate a keypath on self from within a block
* is to define a weak reference to self outside the block, and then use RZDB_KP_OBJ(weakSelf, keypath).
*/
#define RZDB_KP_SELF(keypath) RZDB_KP_OBJ(self, keypath)
/**
* Concatenate two keypath strings.
*
* keypath1 and keypath2 must be NSStrings like, for example, the ones created by the other RZDB keypath generation macros.
* The result is another NSString concatenating the two keypaths, but there is no implicit check that the key path exists.
*/
#define RZDB_KP_CONCAT(keypath1, keypath2) [keypath1 stringByAppendingFormat:@".%@", keypath2]
#endif /** __RZDBMacros__ defined */

View File

@ -0,0 +1,81 @@
//
// RZDataBinding.h
//
// Created by Rob Visentin on 4/7/15.
// Copyright 2014 Raizlabs and other contributors
// http://raizlabs.com/
//
// 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/Foundation.h>
#pragma mark - Definitions
/**
* A transform that takes a value as a parameter and returns an object.
*
* @param value The value that just changed on a foreign object for a bound key path.
*
* @return The value to set for the bound key. Ideally the returned value should depend solely on the input value.
*/
typedef id (^RZDBKeyBindingTransform)(id value);
#pragma mark - Convenience Constants
/**
* If value is nil, returns @(0), otherwise returns value.
*/
OBJC_EXTERN RZDBKeyBindingTransform const kRZDBNilToZeroTransform;
/**
* If value is nil, returns @(1), otherwise returns value.
*/
OBJC_EXTERN RZDBKeyBindingTransform const kRZDBNilToOneTransform;
/**
* If value is nil, returns NSValue with CGSizeZero, otherwise returns value.
*/
OBJC_EXTERN RZDBKeyBindingTransform const kRZDBNilToCGSizeZeroTransform;
/**
* If value is nil, returns NSValue with CGRectZero, otherwise returns value.
*/
OBJC_EXTERN RZDBKeyBindingTransform const kRZDBNilToCGRectZeroTransform;
/**
* If value is nil, returns NSValue with CGRectNull, otherwise returns value.
*/
OBJC_EXTERN RZDBKeyBindingTransform const kRZDBNilToCGRectNullTransform;
/**
* Returns @(![value boolValue])
*/
OBJC_EXTERN RZDBKeyBindingTransform const kRZDBLogicalNegateTransform;
/**
* Returns @(1.0 - [value doubleValue])
*/
OBJC_EXTERN RZDBKeyBindingTransform const kRZDBOneMinusTransform;
/**
* Returns @(~[value longLongValue])
*/
OBJC_EXTERN RZDBKeyBindingTransform const kRZDBBitwiseComplementTransform;

View File

@ -0,0 +1,71 @@
//
// RZDataBinding.m
//
// Created by Rob Visentin on 4/7/15.
// Copyright 2014 Raizlabs and other contributors
// http://raizlabs.com/
//
// 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 <CoreGraphics/CGGeometry.h>
#import "RZDBTransforms.h"
RZDBKeyBindingTransform const kRZDBNilToZeroTransform = ^(id value) {
return (value == nil) ? @(0) : value;
};
RZDBKeyBindingTransform const kRZDBNilToOneTransform = ^(id value) {
return (value == nil) ? @(1) : value;
};
RZDBKeyBindingTransform const kRZDBNilToCGSizeZeroTransform = ^(id value) {
if ( value == nil ) {
value = [NSValue valueWithBytes:&CGSizeZero objCType:@encode(CGSize)];
}
return value;
};
RZDBKeyBindingTransform const kRZDBNilToCGRectZeroTransform = ^(id value) {
if ( value == nil ) {
value = [NSValue valueWithBytes:&CGRectZero objCType:@encode(CGRect)];
}
return value;
};
RZDBKeyBindingTransform const kRZDBNilToCGRectNullTransform = ^(id value) {
if ( value == nil ) {
value = [NSValue valueWithBytes:&CGRectNull objCType:@encode(CGRect)];
}
return value;
};
RZDBKeyBindingTransform const kRZDBLogicalNegateTransform = ^(id value) {
return @(![value boolValue]);
};
RZDBKeyBindingTransform const kRZDBOneMinusTransform = ^(id value) {
return @(1.0 - [value doubleValue]);
};
RZDBKeyBindingTransform const kRZDBBitwiseComplementTransform = ^(id value) {
return @(~[value longLongValue]);
};

View File

@ -0,0 +1,31 @@
//
// RZDataBinding.h
//
// Created by Rob Visentin on 4/7/15.
// Copyright 2014 Raizlabs and other contributors
// http://raizlabs.com/
//
// 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 "RZDBMacros.h"
#import "RZDBTransforms.h"
#import "RZDBCoalesce.h"
#import "NSObject+RZDataBinding.h"

View File

@ -1,11 +1,7 @@
# Specta
# Specta [![Build Status](https://travis-ci.org/specta/specta.svg)](https://travis-ci.org/specta/specta) [![Coverage Status](https://coveralls.io/repos/specta/specta/badge.svg)](https://coveralls.io/r/specta/specta)
A light-weight TDD / BDD framework for Objective-C.
### Status
[![Build Status](https://travis-ci.org/specta/specta.png)](https://travis-ci.org/specta/specta)
[![Coverage Status](https://coveralls.io/repos/specta/specta/badge.svg)](https://coveralls.io/r/specta/specta)
## FEATURES
* An Objective-C RSpec-like BDD DSL
@ -15,56 +11,7 @@ A light-weight TDD / BDD framework for Objective-C.
## SCREENSHOT
![Specta Screenshot](https://raw.githubusercontent.com/specta/specta/master/misc/specta_screenshot.jpg)
## SETUP
Use [CocoaPods](http://github.com/CocoaPods/CocoaPods), [Carthage](https://github.com/carthage/carthage) or [Set up manually](#setting-up-manually)
### CocoaPods
1. Add Specta to your project's `Podfile`:
```ruby
target :MyApp do
# your app dependencies
end
target :MyAppTests do
pod 'Specta', '~> 1.0'
# pod 'Expecta', '~> 1.0' # expecta matchers
# pod 'OCMock', '~> 2.2' # OCMock
# pod 'OCHamcrest', '~> 3.0' # hamcrest matchers
# pod 'OCMockito', '~> 1.0' # OCMock
# pod 'LRMocky', '~> 0.9' # LRMocky
end
```
2. Run `pod update` or `pod install` in your project directory.
### Carthage
1. Add Specta to your project's `Cartfile.private`
```
github "specta/specta" ~> 1.0
```
2. Run `carthage update` in your project directory
3. Drag the appropriate `Specta.framework` for your platform (located in Carthage/Build/) into your applications Xcode project, and add it to your test target(s).
4. If you are building for iOS, a new `Run Script Phase` must be added to copy the framework. The instructions can be found on [Carthage's getting started instructions](https://github.com/carthage/carthage#getting-started)
### SETTING UP MANUALLY
1. Clone from Github.
2. Run `rake` in project root to build.
3. Add a "Cocoa/Cocoa Touch Unit Testing Bundle" target if you don't already have one.
4. Copy and add all header files in `Products` folder to the Test target in your Xcode project.
5. For **OS X projects**, copy and add `Specta.framework` in `Products/osx` folder to the test target in your Xcode project.
For **iOS projects**, copy and add `Specta.framework` in `Products/ios` folder to the test target in your Xcode project.
You can alternatively use `libSpecta.a`, if you prefer to add it as a static library for your project. (iOS 7 and below require this)
6. Add `-ObjC` and `-all_load` to the "Other Linker Flags" build setting for the test target in your Xcode project.
7. If you encounter linking issues with `_llvm_*` symbols, ensure your target's "Generate Test Coverage Files" and "Instrument Program Flow" build settings are set to `Yes`.
<img src="https://raw.githubusercontent.com/specta/specta/master/misc/specta_screenshot.jpg" width="100%">
## EXAMPLE
@ -156,12 +103,17 @@ SpecEnd
* Do `#define SPT_CEDAR_SYNTAX` before importing Specta if you prefer to write `SPEC_BEGIN` and `SPEC_END` instead of `SpecBegin` and `SpecEnd`.
* Prepend `f` to your `describe`, `context`, `example`, `it`, and `specify` to set focus on examples or groups. When specs are focused, all unfocused specs are skipped.
* To use original XCTest reporter, set an environment variable named `SPECTA_REPORTER_CLASS` to `SPTXCTestReporter` in your test scheme.
* Set an environment variable `SPECTA_NO_SHUFFLE` with value `1` to disable test shuffling.
* Set an environment variable `SPECTA_SHUFFLE` with value `1` to enable test shuffling.
* Set an environment variable `SPECTA_SEED` to specify the random seed for test shuffling.
Standard XCTest matchers such as `XCTAssertEqualObjects` and `XCTAssertNil` work, but you probably want to add a nicer matcher framework - [Expecta](http://github.com/specta/expecta/) to your setup. Or if you really prefer, [OCHamcrest](https://github.com/jonreid/OCHamcrest) works fine too. Also, add a mocking framework: [OCMock](http://ocmock.org/).
Standard XCTest matchers such as `XCTAssertEqualObjects` and `XCTAssertNil` work, but you probably want to add a nicer matcher framework - [Expecta](https://github.com/specta/expecta/) to your setup. Or if you really prefer, [OCHamcrest](https://github.com/hamcrest/OCHamcrest) works fine too. Also, add a mocking framework: [OCMock](http://ocmock.org/).
## RUNNING TESTS IN COMMAND LINE
## STATUS
Specta is considered a done project, there are no plans for _active_ development on the project at the moment aside from ensuring future Xcode compatability.
Therefore it is a stable dependency, but will not be moving into the Swift world. If you are looking for that, we recommend you consider [Quick](https://github.com/quick/quick).
## RUNNING SPECTA'S TESTS IN COMMAND LINE
* Run `rake test` in the cloned folder.
@ -171,6 +123,57 @@ Standard XCTest matchers such as `XCTAssertEqualObjects` and `XCTAssertNil` work
* Please prefix instance variable names with a single underscore (`_`).
* Please prefix custom classes and functions defined in the global scope with `SPT`.
## Installation
Use [CocoaPods](https://github.com/CocoaPods/CocoaPods), [Carthage](https://github.com/carthage/carthage) or [Set up manually](#setting-up-manually)
### CocoaPods
1. Add Specta to your project's `Podfile`:
```ruby
target :MyApp do
# your app dependencies
target :MyAppTests do
inherit! :search_paths
pod 'Specta', '~> 1.0'
# pod 'Expecta', '~> 1.0' # expecta matchers
# pod 'OCMock', '~> 2.2' # OCMock
# pod 'OCHamcrest', '~> 3.0' # hamcrest matchers
# pod 'OCMockito', '~> 1.0' # OCMock
# pod 'LRMocky', '~> 0.9' # LRMocky
end
end
```
2. Run `pod install` in your project directory.
### Carthage
1. Add Specta to your project's `Cartfile.private`
```
github "specta/specta" ~> 1.0
```
2. Run `carthage update` in your project directory
3. Drag the appropriate `Specta.framework` for your platform (located in Carthage/Build/) into your applications Xcode project, and add it to your test target(s).
4. If you are building for iOS, a new `Run Script Phase` must be added to copy the framework. The instructions can be found on [Carthage's getting started instructions](https://github.com/carthage/carthage#getting-started)
### SETTING UP MANUALLY
1. Clone from Github.
2. Run `rake` in project root to build.
3. Add a "Cocoa/Cocoa Touch Unit Testing Bundle" target if you don't already have one.
4. Copy and add all header files in `Products` folder to the Test target in your Xcode project.
5. For **OS X projects**, copy and add `Specta.framework` in `Products/osx` folder to the test target in your Xcode project.
For **iOS projects**, copy and add `Specta.framework` in `Products/ios` folder to the test target in your Xcode project.
You can alternatively use `libSpecta.a`, if you prefer to add it as a static library for your project. (iOS 7 and below require this)
6. Add `-ObjC` and `-all_load` to the "Other Linker Flags" build setting for the test target in your Xcode project.
7. If you encounter linking issues with `_llvm_*` symbols, ensure your target's "Generate Test Coverage Files" and "Instrument Program Flow" build settings are set to `Yes`.
## LICENSE
Copyright (c) 2012-2015 [Specta Team](https://github.com/specta?tab=members). This software is licensed under the [MIT License](http://github.com/specta/specta/raw/master/LICENSE).
Copyright (c) 2012-2016 [Specta Team](https://github.com/orgs/specta/people). This software is licensed under the [MIT License](http://github.com/specta/specta/raw/master/LICENSE).

View File

@ -19,7 +19,7 @@
[spec spec];
}
@catch (NSException *exception) {
fprintf(stderr, "%s: An exception has occured outside of tests, aborting.\n\n%s (%s) \n", [specName UTF8String], [[exception name] UTF8String], [[exception reason] UTF8String]);
fprintf(stderr, "%s: An exception has occurred outside of tests, aborting.\n\n%s (%s) \n", [specName UTF8String], [[exception name] UTF8String], [[exception reason] UTF8String]);
if ([exception respondsToSelector:@selector(callStackSymbols)]) {
NSArray *callStackSymbols = [exception callStackSymbols];
if (callStackSymbols) {
@ -145,6 +145,7 @@
example.block(self);
} else if (!example.pending) {
self.spt_skipped = YES;
NSLog(@"Skipping '%@'", example.name);
}
[[[NSThread currentThread] threadDictionary] removeObjectForKey:spt_kCurrentSpecKey];

View File

@ -10,20 +10,20 @@
typedef void (^DoneCallback)(void);
OBJC_EXTERN void describe(NSString *name, void (^block)());
OBJC_EXTERN void fdescribe(NSString *name, void (^block)());
OBJC_EXTERN void describe(NSString *name, void (^block)(void));
OBJC_EXTERN void fdescribe(NSString *name, void (^block)(void));
OBJC_EXTERN void context(NSString *name, void (^block)());
OBJC_EXTERN void fcontext(NSString *name, void (^block)());
OBJC_EXTERN void context(NSString *name, void (^block)(void));
OBJC_EXTERN void fcontext(NSString *name, void (^block)(void));
OBJC_EXTERN void it(NSString *name, void (^block)());
OBJC_EXTERN void fit(NSString *name, void (^block)());
OBJC_EXTERN void it(NSString *name, void (^block)(void));
OBJC_EXTERN void fit(NSString *name, void (^block)(void));
OBJC_EXTERN void example(NSString *name, void (^block)());
OBJC_EXTERN void fexample(NSString *name, void (^block)());
OBJC_EXTERN void example(NSString *name, void (^block)(void));
OBJC_EXTERN void fexample(NSString *name, void (^block)(void));
OBJC_EXTERN void specify(NSString *name, void (^block)());
OBJC_EXTERN void fspecify(NSString *name, void (^block)());
OBJC_EXTERN void specify(NSString *name, void (^block)(void));
OBJC_EXTERN void fspecify(NSString *name, void (^block)(void));
#define pending(...) spt_pending_(__VA_ARGS__, nil)
#define xdescribe(...) spt_pending_(__VA_ARGS__, nil)
@ -32,14 +32,14 @@ OBJC_EXTERN void fspecify(NSString *name, void (^block)());
#define xit(...) spt_pending_(__VA_ARGS__, nil)
#define xspecify(...) spt_pending_(__VA_ARGS__, nil)
OBJC_EXTERN void beforeAll(void (^block)());
OBJC_EXTERN void afterAll(void (^block)());
OBJC_EXTERN void beforeAll(void (^block)(void));
OBJC_EXTERN void afterAll(void (^block)(void));
OBJC_EXTERN void beforeEach(void (^block)());
OBJC_EXTERN void afterEach(void (^block)());
OBJC_EXTERN void beforeEach(void (^block)(void));
OBJC_EXTERN void afterEach(void (^block)(void));
OBJC_EXTERN void before(void (^block)());
OBJC_EXTERN void after(void (^block)());
OBJC_EXTERN void before(void (^block)(void));
OBJC_EXTERN void after(void (^block)(void));
OBJC_EXTERN void sharedExamplesFor(NSString *name, void (^block)(NSDictionary *data));
OBJC_EXTERN void sharedExamples(NSString *name, void (^block)(NSDictionary *data));
@ -54,7 +54,7 @@ OBJC_EXTERN void waitUntil(void (^block)(DoneCallback done));
*
* @param timeout timeout for this @c block only; does not affect the global
* timeout, as @c setAsyncSpecTimeout() does.
* @param ^block runs test code
* @param block runs test code
*/
OBJC_EXTERN void waitUntilTimeout(NSTimeInterval timeout, void (^block)(DoneCallback done));
@ -83,8 +83,8 @@ OBJC_EXTERN void setAsyncSpecTimeout(NSTimeInterval timeout);
} \
@end
OBJC_EXTERN void spt_it_(NSString *name, NSString *fileName, NSUInteger lineNumber, void (^block)());
OBJC_EXTERN void spt_fit_(NSString *name, NSString *fileName, NSUInteger lineNumber, void (^block)());
OBJC_EXTERN void spt_it_(NSString *name, NSString *fileName, NSUInteger lineNumber, void (^block)(void));
OBJC_EXTERN void spt_fit_(NSString *name, NSString *fileName, NSUInteger lineNumber, void (^block)(void));
OBJC_EXTERN void spt_pending_(NSString *name, ...);
OBJC_EXTERN void spt_itShouldBehaveLike_(NSString *fileName, NSUInteger lineNumber, NSString *name, id dictionaryOrBlock);
OBJC_EXTERN void spt_itShouldBehaveLike_block(NSString *fileName, NSUInteger lineNumber, NSString *name, NSDictionary *(^block)());
OBJC_EXTERN void spt_itShouldBehaveLike_block(NSString *fileName, NSUInteger lineNumber, NSString *name, NSDictionary *(^block)(void));

View File

@ -52,12 +52,15 @@ void spt_itShouldBehaveLike_(NSString *fileName, NSUInteger lineNumber, NSString
beforeEach(^{
NSDictionary *blockData = dataBlock();
[dataDict removeAllObjects];
[dataDict addEntriesFromDictionary:blockData];
});
block(dataDict);
afterEach(^{
[dataDict removeAllObjects];
});
afterAll(^{
dataDict = nil;
});

View File

@ -1,5 +1,5 @@
@class SPTSpec;
typedef void (^SPTVoidBlock)();
typedef void (^SPTVoidBlock)(void);
typedef void (^SPTSpecBlock)(SPTSpec *spec);
typedef void (^SPTDictionaryBlock)(NSDictionary *dictionary);

View File

@ -15,4 +15,4 @@ BOOL spt_isSpecClass(Class aClass);
NSString *spt_underscorize(NSString *string);
NSArray *spt_map(NSArray *array, id (^block)(id obj, NSUInteger idx));
NSArray *spt_shuffle(NSArray *array);
unsigned int spt_seed();
unsigned int spt_seed(void);

View File

@ -42,7 +42,10 @@
[self spt_dequeueFailures];
};
if ([NSThread isMainThread]) {
BOOL isMainThread = [NSThread isMainThread];
BOOL isSpectaTest = [self isKindOfClass:[SPTSpec class]];
if (!isSpectaTest || isMainThread) {
dequeueFailures();
} else {
dispatch_sync(dispatch_get_main_queue(), dequeueFailures);

View File

@ -1,4 +1,12 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

View File

@ -1,4 +1,14 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "ExpectaObject+FBSnapshotTest.h"
#import "EXPMatchers+FBSnapshotTest.h"

View File

@ -1,7 +1,12 @@
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Expecta+Snapshots
ENABLE_BITCODE = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" "${PODS_CONFIGURATION_BUILD_DIR}/Expecta" "${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase" "${PODS_CONFIGURATION_BUILD_DIR}/Specta"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
OTHER_LDFLAGS = -framework "Foundation" -framework "XCTest"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Expecta+Snapshots
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.0.0</string>
<string>3.1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -1,4 +1,12 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

View File

@ -1,4 +1,14 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "EXPBlockDefinedMatcher.h"
#import "EXPDefines.h"

View File

@ -1,8 +1,12 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Expecta
ENABLE_BITCODE = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
OTHER_LDFLAGS = -framework "Foundation" -framework "XCTest"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Expecta
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.5</string>
<string>1.0.6</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -1,4 +1,12 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

View File

@ -0,0 +1,19 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "FBSnapshotTestCase.h"
#import "FBSnapshotTestCasePlatform.h"
#import "FBSnapshotTestController.h"
FOUNDATION_EXPORT double FBSnapshotTestCaseVersionNumber;
FOUNDATION_EXPORT const unsigned char FBSnapshotTestCaseVersionString[];

View File

@ -1,15 +1,6 @@
framework module FBSnapshotTestCase {
umbrella header "FBSnapshotTestCase.h"
umbrella header "FBSnapshotTestCase-umbrella.h"
export *
module * { export * }
header "FBSnapshotTestCase.h"
header "FBSnapshotTestCasePlatform.h"
header "FBSnapshotTestController.h"
private header "UIImage+Compare.h"
private header "UIImage+Diff.h"
private header "UIImage+Snapshot.h"
}

View File

@ -1,9 +1,13 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase
ENABLE_BITCODE = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
OTHER_LDFLAGS = -framework "Foundation" -framework "QuartzCore" -framework "UIKit" -framework "XCTest"
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/FBSnapshotTestCase
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.0.7</string>
<string>2.1.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<string>0.2.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -1,4 +1,12 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

View File

@ -1,4 +1,14 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "PNGradientView.h"

View File

@ -1,6 +1,11 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PNGradientView
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RZDataBinding"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
OTHER_LDFLAGS = -framework "QuartzCore"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES

View File

@ -13,11 +13,11 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<string>0.1.5</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<string>1</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>

View File

@ -23,4 +23,30 @@ 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.
## RZDataBinding
Copyright 2014 Raizlabs and other contributors
http://raizlabs.com/
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.
Generated by CocoaPods - https://cocoapods.org

View File

@ -34,11 +34,45 @@ 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.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>PNGradientView</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>
Copyright 2014 Raizlabs and other contributors
http://raizlabs.com/
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.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>RZDataBinding</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Generated by CocoaPods - https://cocoapods.org</string>

View File

@ -6,6 +6,14 @@ mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
# Copies and strips a vendored framework
install_framework()
{
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
@ -16,16 +24,16 @@ install_framework()
local source="$1"
fi
local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
if [ -L "${source}" ]; then
echo "Symlinked..."
source="$(readlink "${source}")"
fi
# use filter instead of exclude so missing patterns dont' throw errors
echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
# Use filter instead of exclude so missing patterns don't throw errors.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
local basename
basename="$(basename -s .framework "$1")"
@ -54,24 +62,65 @@ install_framework()
fi
}
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
if [ -r "$source" ]; then
# Copy the dSYM into a the targets temp dir.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
local basename
basename="$(basename -s .framework.dSYM "$source")"
binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
# Strip invalid architectures so "fat" simulator / device frameworks work on device
if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then
strip_invalid_archs "$binary"
fi
if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
# Move the stripped file into its final destination.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
else
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM"
fi
fi
}
# Signs a framework with the provided identity
code_sign_if_enabled() {
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
# Use the current code_sign_identitiy
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\""
/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1"
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
code_sign_cmd="$code_sign_cmd &"
fi
echo "$code_sign_cmd"
eval "$code_sign_cmd"
fi
}
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
# Get architectures for current file
archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
STRIP_BINARY_RETVAL=0
return
fi
stripped=""
for arch in $archs; do
if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary" || exit 1
stripped="$stripped $arch"
@ -80,12 +129,18 @@ strip_invalid_archs() {
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=1
}
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "Pods-PNGradientView_Example/PNGradientView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/PNGradientView/PNGradientView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RZDataBinding/RZDataBinding.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "Pods-PNGradientView_Example/PNGradientView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/PNGradientView/PNGradientView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RZDataBinding/RZDataBinding.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
wait
fi

View File

@ -1,18 +1,37 @@
#!/bin/sh
set -e
mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
> "$RESOURCES_TO_COPY"
XCASSET_FILES=()
realpath() {
DIRECTORY="$(cd "${1%/*}" && pwd)"
FILENAME="${1##*/}"
echo "$DIRECTORY/$FILENAME"
}
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
case "${TARGETED_DEVICE_FAMILY}" in
1,2)
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
;;
1)
TARGET_DEVICE_ARGS="--target-device iphone"
;;
2)
TARGET_DEVICE_ARGS="--target-device ipad"
;;
3)
TARGET_DEVICE_ARGS="--target-device tv"
;;
4)
TARGET_DEVICE_ARGS="--target-device watch"
;;
*)
TARGET_DEVICE_ARGS="--target-device mac"
;;
esac
install_resource()
{
@ -29,44 +48,44 @@ EOM
fi
case $RESOURCE_PATH in
*.storyboard)
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT}"
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}"
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
;;
*.xib)
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT}"
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}"
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
;;
*.framework)
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
echo "rsync -av $RESOURCE_PATH ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
rsync -av "$RESOURCE_PATH" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
;;
*.xcdatamodel)
echo "xcrun momc \"$RESOURCE_PATH\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
xcrun momc "$RESOURCE_PATH" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
;;
*.xcdatamodeld)
echo "xcrun momc \"$RESOURCE_PATH\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
xcrun momc "$RESOURCE_PATH" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
;;
*.xcmappingmodel)
echo "xcrun mapc \"$RESOURCE_PATH\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
xcrun mapc "$RESOURCE_PATH" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
;;
*.xcassets)
ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH")
ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
;;
*)
echo "$RESOURCE_PATH"
echo "$RESOURCE_PATH" || true
echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
;;
esac
}
mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
@ -75,25 +94,10 @@ rm -f "$RESOURCES_TO_COPY"
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
then
case "${TARGETED_DEVICE_FAMILY}" in
1,2)
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
;;
1)
TARGET_DEVICE_ARGS="--target-device iphone"
;;
2)
TARGET_DEVICE_ARGS="--target-device ipad"
;;
*)
TARGET_DEVICE_ARGS="--target-device mac"
;;
esac
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
while read line; do
if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
if [[ $line != "${PODS_ROOT}*" ]]; then
XCASSET_FILES+=("$line")
fi
done <<<"$OTHER_XCASSETS"

View File

@ -1,4 +1,14 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
FOUNDATION_EXPORT double Pods_PNGradientView_ExampleVersionNumber;

View File

@ -1,6 +1,9 @@
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PNGradientView" "${PODS_CONFIGURATION_BUILD_DIR}/RZDataBinding"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/PNGradientView.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "PNGradientView"
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-PNGradientView_Example
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/PNGradientView/PNGradientView.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RZDataBinding/RZDataBinding.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "PNGradientView" -framework "RZDataBinding"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods

View File

@ -1,6 +1,9 @@
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PNGradientView" "${PODS_CONFIGURATION_BUILD_DIR}/RZDataBinding"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/PNGradientView.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "PNGradientView"
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-PNGradientView_Example
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/PNGradientView/PNGradientView.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RZDataBinding/RZDataBinding.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "PNGradientView" -framework "RZDataBinding"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods

View File

@ -24,6 +24,32 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## RZDataBinding
Copyright 2014 Raizlabs and other contributors
http://raizlabs.com/
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.
## Expecta
Copyright (c) 2011-2015 Specta Team - https://github.com/specta

View File

@ -34,11 +34,45 @@ 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.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>PNGradientView</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>
Copyright 2014 Raizlabs and other contributors
http://raizlabs.com/
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.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>RZDataBinding</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Copyright (c) 2011-2015 Specta Team - https://github.com/specta
@ -61,6 +95,8 @@ 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.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>Expecta</string>
<key>Type</key>
@ -91,6 +127,8 @@ 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.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>Expecta+Snapshots</string>
<key>Type</key>
@ -128,6 +166,8 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</string>
<key>License</key>
<string>BSD</string>
<key>Title</key>
<string>FBSnapshotTestCase</string>
<key>Type</key>
@ -156,6 +196,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>Specta</string>
<key>Type</key>

View File

@ -6,6 +6,14 @@ mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
# Copies and strips a vendored framework
install_framework()
{
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
@ -16,16 +24,16 @@ install_framework()
local source="$1"
fi
local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
if [ -L "${source}" ]; then
echo "Symlinked..."
source="$(readlink "${source}")"
fi
# use filter instead of exclude so missing patterns dont' throw errors
echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
# Use filter instead of exclude so missing patterns don't throw errors.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
local basename
basename="$(basename -s .framework "$1")"
@ -54,24 +62,65 @@ install_framework()
fi
}
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
if [ -r "$source" ]; then
# Copy the dSYM into a the targets temp dir.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
local basename
basename="$(basename -s .framework.dSYM "$source")"
binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
# Strip invalid architectures so "fat" simulator / device frameworks work on device
if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then
strip_invalid_archs "$binary"
fi
if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
# Move the stripped file into its final destination.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
else
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM"
fi
fi
}
# Signs a framework with the provided identity
code_sign_if_enabled() {
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
# Use the current code_sign_identitiy
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\""
/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1"
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
code_sign_cmd="$code_sign_cmd &"
fi
echo "$code_sign_cmd"
eval "$code_sign_cmd"
fi
}
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
# Get architectures for current file
archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
STRIP_BINARY_RETVAL=0
return
fi
stripped=""
for arch in $archs; do
if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary" || exit 1
stripped="$stripped $arch"
@ -80,20 +129,26 @@ strip_invalid_archs() {
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=1
}
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "Pods-PNGradientView_Tests/PNGradientView.framework"
install_framework "Pods-PNGradientView_Tests/Expecta.framework"
install_framework "Pods-PNGradientView_Tests/Expecta_Snapshots.framework"
install_framework "Pods-PNGradientView_Tests/FBSnapshotTestCase.framework"
install_framework "Pods-PNGradientView_Tests/Specta.framework"
install_framework "${BUILT_PRODUCTS_DIR}/PNGradientView/PNGradientView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RZDataBinding/RZDataBinding.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Expecta/Expecta.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Expecta+Snapshots/Expecta_Snapshots.framework"
install_framework "${BUILT_PRODUCTS_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Specta/Specta.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "Pods-PNGradientView_Tests/PNGradientView.framework"
install_framework "Pods-PNGradientView_Tests/Expecta.framework"
install_framework "Pods-PNGradientView_Tests/Expecta_Snapshots.framework"
install_framework "Pods-PNGradientView_Tests/FBSnapshotTestCase.framework"
install_framework "Pods-PNGradientView_Tests/Specta.framework"
install_framework "${BUILT_PRODUCTS_DIR}/PNGradientView/PNGradientView.framework"
install_framework "${BUILT_PRODUCTS_DIR}/RZDataBinding/RZDataBinding.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Expecta/Expecta.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Expecta+Snapshots/Expecta_Snapshots.framework"
install_framework "${BUILT_PRODUCTS_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework"
install_framework "${BUILT_PRODUCTS_DIR}/Specta/Specta.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
wait
fi

View File

@ -1,18 +1,37 @@
#!/bin/sh
set -e
mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
> "$RESOURCES_TO_COPY"
XCASSET_FILES=()
realpath() {
DIRECTORY="$(cd "${1%/*}" && pwd)"
FILENAME="${1##*/}"
echo "$DIRECTORY/$FILENAME"
}
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
case "${TARGETED_DEVICE_FAMILY}" in
1,2)
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
;;
1)
TARGET_DEVICE_ARGS="--target-device iphone"
;;
2)
TARGET_DEVICE_ARGS="--target-device ipad"
;;
3)
TARGET_DEVICE_ARGS="--target-device tv"
;;
4)
TARGET_DEVICE_ARGS="--target-device watch"
;;
*)
TARGET_DEVICE_ARGS="--target-device mac"
;;
esac
install_resource()
{
@ -29,44 +48,44 @@ EOM
fi
case $RESOURCE_PATH in
*.storyboard)
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT}"
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}"
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
;;
*.xib)
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT}"
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}"
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
;;
*.framework)
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
echo "rsync -av $RESOURCE_PATH ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
rsync -av "$RESOURCE_PATH" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
;;
*.xcdatamodel)
echo "xcrun momc \"$RESOURCE_PATH\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
xcrun momc "$RESOURCE_PATH" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
;;
*.xcdatamodeld)
echo "xcrun momc \"$RESOURCE_PATH\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
xcrun momc "$RESOURCE_PATH" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
;;
*.xcmappingmodel)
echo "xcrun mapc \"$RESOURCE_PATH\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
xcrun mapc "$RESOURCE_PATH" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
;;
*.xcassets)
ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH")
ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
;;
*)
echo "$RESOURCE_PATH"
echo "$RESOURCE_PATH" || true
echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
;;
esac
}
mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
@ -75,25 +94,10 @@ rm -f "$RESOURCES_TO_COPY"
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
then
case "${TARGETED_DEVICE_FAMILY}" in
1,2)
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
;;
1)
TARGET_DEVICE_ARGS="--target-device iphone"
;;
2)
TARGET_DEVICE_ARGS="--target-device ipad"
;;
*)
TARGET_DEVICE_ARGS="--target-device mac"
;;
esac
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
while read line; do
if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
if [[ $line != "${PODS_ROOT}*" ]]; then
XCASSET_FILES+=("$line")
fi
done <<<"$OTHER_XCASSETS"

View File

@ -1,4 +1,14 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
FOUNDATION_EXPORT double Pods_PNGradientView_TestsVersionNumber;

View File

@ -1,8 +1,11 @@
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PLATFORM_DIR)/Developer/Library/Frameworks "${PODS_CONFIGURATION_BUILD_DIR}/Expecta" "${PODS_CONFIGURATION_BUILD_DIR}/Expecta+Snapshots" "${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase" "${PODS_CONFIGURATION_BUILD_DIR}/PNGradientView" "${PODS_CONFIGURATION_BUILD_DIR}/RZDataBinding" "${PODS_CONFIGURATION_BUILD_DIR}/Specta"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Expecta.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Expecta_Snapshots.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/FBSnapshotTestCase.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/PNGradientView.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Specta.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "Expecta" -framework "Expecta_Snapshots" -framework "FBSnapshotTestCase" -framework "PNGradientView" -framework "Specta"
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Expecta+Snapshots/Expecta_Snapshots.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Expecta/Expecta.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/PNGradientView/PNGradientView.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RZDataBinding/RZDataBinding.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Specta/Specta.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "Expecta" -framework "Expecta_Snapshots" -framework "FBSnapshotTestCase" -framework "PNGradientView" -framework "RZDataBinding" -framework "Specta"
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-PNGradientView_Tests
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods

View File

@ -1,8 +1,11 @@
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PLATFORM_DIR)/Developer/Library/Frameworks "${PODS_CONFIGURATION_BUILD_DIR}/Expecta" "${PODS_CONFIGURATION_BUILD_DIR}/Expecta+Snapshots" "${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase" "${PODS_CONFIGURATION_BUILD_DIR}/PNGradientView" "${PODS_CONFIGURATION_BUILD_DIR}/RZDataBinding" "${PODS_CONFIGURATION_BUILD_DIR}/Specta"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Expecta.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Expecta_Snapshots.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/FBSnapshotTestCase.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/PNGradientView.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Specta.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "Expecta" -framework "Expecta_Snapshots" -framework "FBSnapshotTestCase" -framework "PNGradientView" -framework "Specta"
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Expecta+Snapshots/Expecta_Snapshots.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Expecta/Expecta.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/PNGradientView/PNGradientView.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RZDataBinding/RZDataBinding.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Specta/Specta.framework/Headers"
OTHER_LDFLAGS = $(inherited) -framework "Expecta" -framework "Expecta_Snapshots" -framework "FBSnapshotTestCase" -framework "PNGradientView" -framework "RZDataBinding" -framework "Specta"
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-PNGradientView_Tests
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_RZDataBinding : NSObject
@end
@implementation PodsDummy_RZDataBinding
@end

View File

@ -0,0 +1,12 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

View File

@ -0,0 +1,21 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "NSObject+RZDataBinding.h"
#import "RZDataBinding.h"
#import "RZDBCoalesce.h"
#import "RZDBMacros.h"
#import "RZDBTransforms.h"
FOUNDATION_EXPORT double RZDataBindingVersionNumber;
FOUNDATION_EXPORT const unsigned char RZDataBindingVersionString[];

View File

@ -0,0 +1,6 @@
framework module RZDataBinding {
umbrella header "RZDataBinding-umbrella.h"
export *
module * { export * }
}

View File

@ -0,0 +1,9 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RZDataBinding
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/RZDataBinding
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.5</string>
<string>1.0.7</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -1,4 +1,12 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

View File

@ -1,4 +1,14 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "Specta.h"
#import "SpectaDSL.h"

View File

@ -1,8 +1,12 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Specta
ENABLE_BITCODE = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
OTHER_LDFLAGS = -framework "Foundation" -framework "XCTest"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Specta
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES

View File

@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = "PNGradientView"
s.version = "0.1.0"
s.version = "0.2.0"
s.summary = "PNGradientView is a UIView subclassing with gradient support"
# This description is used to generate tags and improve search results.
@ -29,11 +29,12 @@ s.platform = :ios, '7.0'
s.requires_arc = true
s.source_files = 'Pod/Classes/**/*'
s.resource_bundles = {
'PNGradientView' => ['Pod/Assets/*.png']
}
#s.resource_bundles = {
#'PNGradientView' => ['Pod/Assets/*.png']
#}
# s.public_header_files = 'Pod/Classes/**/*.h'
s.frameworks = 'QuartzCore'
# s.dependency 'AFNetworking', '~> 2.3'
s.dependency 'RZDataBinding'
end

View File

@ -12,4 +12,8 @@
- (void) setBackgroundLayerWithColors:(NSArray *) colors startPoint:(CGPoint) startPoint;
- (void) setBackgroundLayerWithColors:(NSArray *) colors startPoint:(CGPoint) startPoint angle:(CGFloat) angle;
- (void) setBackgroundLayerWithColors:(NSArray *) colors startPoint:(CGPoint) startPoint endPoint:(CGPoint) endPoint;
@end

View File

@ -7,6 +7,7 @@
//
#import "PNGradientView.h"
#import <RZDataBinding/RZDataBinding.h>
@interface PNGradientView()
@ -14,6 +15,7 @@
@property (nonatomic, strong) NSMutableArray *colors;
@property (nonatomic, strong) NSMutableArray *cgColors;
@property (nonatomic) CGPoint startPoint;
@property (nonatomic) CGPoint endPoint;
@property (nonatomic) BOOL didUpdateConstraints;
@ -24,9 +26,10 @@
- (instancetype) init {
self = [super init];
if (self) {
[self addObserver:self forKeyPath:@"bounds" options:0 context:nil];
_colors = [[NSMutableArray alloc] init];
_cgColors = [[NSMutableArray alloc] init];
[self rz_addTarget:self action:@selector(valueChanged:) forKeyPathChange:@"bounds"];
}
return self;
}
@ -34,9 +37,10 @@
- (instancetype) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self addObserver:self forKeyPath:@"bounds" options:0 context:nil];
_colors = [[NSMutableArray alloc] init];
_cgColors = [[NSMutableArray alloc] init];
[self rz_addTarget:self action:@selector(valueChanged:) forKeyPathChange:@"bounds"];
}
return self;
}
@ -44,14 +48,28 @@
- (instancetype) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addObserver:self forKeyPath:@"bounds" options:0 context:nil];
_colors = [[NSMutableArray alloc] init];
_cgColors = [[NSMutableArray alloc] init];
[self rz_addTarget:self action:@selector(valueChanged:) forKeyPathChange:@"bounds"];
}
return self;
}
- (void) setBackgroundLayerWithColors:(NSArray *) colors startPoint:(CGPoint) startPoint {
[self setBackgroundLayerWithColors:colors startPoint:startPoint endPoint:CGPointZero];
}
- (void) setBackgroundLayerWithColors:(NSArray *) colors startPoint:(CGPoint) startPoint angle:(CGFloat) angle {
float c = pow(sinf((2*M_PI*((angle+0.25)/2))),2);
float d = pow(sinf((2*M_PI*((angle+0.5)/2))),2);
[self setBackgroundLayerWithColors:colors startPoint:startPoint endPoint:CGPointMake(c, d)];
}
- (void) setBackgroundLayerWithColors:(NSArray *) colors startPoint:(CGPoint) startPoint endPoint:(CGPoint) endPoint {
if (colors && [colors count] > 0) {
_colors = [[NSMutableArray alloc] initWithArray:colors];
[_cgColors removeAllObjects];
@ -61,6 +79,7 @@
}
_startPoint = startPoint;
_endPoint = endPoint;
//[_gradientLayer removeFromSuperlayer];
if (!_gradientLayer) {
@ -79,6 +98,9 @@
_gradientLayer.startPoint = _startPoint;
if (!CGPointEqualToPoint(_endPoint, CGPointZero)) {
_gradientLayer.endPoint = _endPoint;
}
if (!CGRectEqualToRect(_gradientLayer.frame, CGRectZero)) {
[self.layer insertSublayer:_gradientLayer atIndex:0];
@ -108,10 +130,15 @@
});
}
/*
NSString* const kRZDBChangeKeyObject = @"RZDBChangeObject";
NSString* const kRZDBChangeKeyOld = @"RZDBChangeOld";
NSString* const kRZDBChangeKeyNew = @"RZDBChangeNew";
NSString* const kRZDBChangeKeyKeyPath = @"RZDBChangeKeyPath";
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == self && [keyPath isEqualToString:@"bounds"]) {
- (void) valueChanged:(NSDictionary *) value {
if ([value objectForKey:kRZDBChangeKeyNew]) {
if(_gradientLayer) {
dispatch_async(dispatch_get_main_queue(), ^{
[self setBackgroundLayerWithColors:_colors startPoint:_startPoint];

View File

@ -1 +0,0 @@
Example/Pods/Pods.xcodeproj

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = '963AFB80CB68E667D89A4C7B'
BlueprintIdentifier = 'A51FCBC4CED1FD3D48C2F214'
BlueprintName = 'PNGradientView'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'PNGradientView.framework'>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = '12B177DDA082A50726C424AC'
BlueprintName = 'Expecta+Snapshots'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'Expecta_Snapshots.framework'>
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = '3D237569497EB53811AC3351'
BlueprintName = 'Expecta'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'Expecta.framework'>
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = 'CE117A2658BA964DD6ECF18A'
BlueprintName = 'FBSnapshotTestCase'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'FBSnapshotTestCase.framework'>
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = '5DD4745A32CC2E1C7B4A0AE6'
BlueprintName = 'PNGradientView-PNGradientView'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'PNGradientView.bundle'>
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = 'AA31E0A16A34ACEE49D65959'
BlueprintName = 'Pods-PNGradientView_Example'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'Pods_PNGradientView_Example.framework'>
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = '4B4F9E8B9EA5B9EC44A8E05E'
BlueprintName = 'Pods-PNGradientView_Tests'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'Pods_PNGradientView_Tests.framework'>
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = '2EB0DD10E957F9682D746AD1'
BlueprintName = 'RZDataBinding'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'RZDataBinding.framework'>
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = '74A8631BB9DBFC985A95AE74'
BlueprintName = 'Specta'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'Specta.framework'>
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Expecta+Snapshots.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>Expecta.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>FBSnapshotTestCase.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>PNGradientView-PNGradientView.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>PNGradientView.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>Pods-PNGradientView_Example.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>Pods-PNGradientView_Tests.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>RZDataBinding.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
<key>Specta.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>00ADDBCA9E516B429C7877EB5AED2DCE</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>0B99C8D5A873496DE4086F03AF29071A</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>0D888F29E05E498D0CD91A51D28599A5</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>2E6C956A3E83E4661FC7B7C27E7D27E1</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>619F0D28240534293108906FED04836F</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>7AEFD28FABDA2DF637B4BC792B5B63A3</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>7D5097010DB523C6693F927502589212</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>7E1ACFE19093575ABADD24863D9D660B</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>868F42091F6340DFB84C0E018FC63F3B</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>888163BF32D32B8DC7BDBC80245BB66C</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>9F398D93E5F20E1A9631212C886EDD61</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>A411EE74B7748799DE9E84BF880AF647</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>B5522B4C786061CA9DFC8A4198028552</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>CE5271B3FF815EE2585901865CD41778</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>D07447EE3CEBB0BCB8D61DFCB8EA915F</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>E1E99BE24B4D5E362ECFB6580A5D5A8D</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>