- Upgrade to 1.0

This commit is contained in:
Giuseppe Nucifora 2016-12-28 18:49:02 +01:00
parent 4e66c377fd
commit 0c2b3f1c25
452 changed files with 33888 additions and 16806 deletions

View File

@ -7,21 +7,22 @@
objects = {
/* Begin PBXBuildFile section */
3F3C53CB2D851F471BE8A604 /* Pods_PNObject_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 89BBA9195EECA9F4625BB1DC /* Pods_PNObject_Example.framework */; };
2EC5979E315D48625574D725 /* Pods_PNObject_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 808511432137F18DDFE44210 /* Pods_PNObject_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 */; };
6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; };
6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; };
6003F59E195388D20070C39A /* PNObjectAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* PNObjectAppDelegate.m */; };
6003F5A7195388D20070C39A /* PNObjectViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* PNObjectViewController.m */; };
6003F59E195388D20070C39A /* PNObjAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* PNObjAppDelegate.m */; };
6003F5A7195388D20070C39A /* PNObjViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* PNObjViewController.m */; };
6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; };
6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; };
6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; };
6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; };
60D833DF200A0D946F2E8C4D /* Pods_PNObject_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CAD2FCF55030EAE61415B480 /* Pods_PNObject_Tests.framework */; };
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; };
FD9FF8837F786E50B88531C0 /* Pods_PNObject_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F74BC1D549B9C619883DB7E0 /* Pods_PNObject_Example.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -35,8 +36,9 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
2DBDB9BA06776F1FFBD6EDC3 /* Pods-PNObject_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNObject_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PNObject_Tests/Pods-PNObject_Tests.release.xcconfig"; sourceTree = "<group>"; };
4082E2E9CCB7F56C1A30AF72 /* Pods-PNObject_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNObject_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example.release.xcconfig"; sourceTree = "<group>"; };
116AF818F8117B91EF290C9A /* Pods-PNObject_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNObject_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example.release.xcconfig"; sourceTree = "<group>"; };
2661CEE1F223361CD1DACC25 /* PNObject.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = PNObject.podspec; path = ../PNObject.podspec; sourceTree = "<group>"; };
3E392C278361604C1602CAF0 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
6003F58A195388D20070C39A /* PNObject_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PNObject_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; };
@ -45,10 +47,10 @@
6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
6003F59B195388D20070C39A /* PNObject-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PNObject-Prefix.pch"; sourceTree = "<group>"; };
6003F59C195388D20070C39A /* PNObjectAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PNObjectAppDelegate.h; sourceTree = "<group>"; };
6003F59D195388D20070C39A /* PNObjectAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNObjectAppDelegate.m; sourceTree = "<group>"; };
6003F5A5195388D20070C39A /* PNObjectViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PNObjectViewController.h; sourceTree = "<group>"; };
6003F5A6195388D20070C39A /* PNObjectViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNObjectViewController.m; sourceTree = "<group>"; };
6003F59C195388D20070C39A /* PNObjAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PNObjAppDelegate.h; sourceTree = "<group>"; };
6003F59D195388D20070C39A /* PNObjAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNObjAppDelegate.m; sourceTree = "<group>"; };
6003F5A5195388D20070C39A /* PNObjViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PNObjViewController.h; sourceTree = "<group>"; };
6003F5A6195388D20070C39A /* PNObjViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNObjViewController.m; sourceTree = "<group>"; };
6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
6003F5AE195388D20070C39A /* PNObject_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PNObject_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
@ -56,15 +58,13 @@
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>"; };
6805F44B1DF6DCC600C79B18 /* PNObject_Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PNObject_Example.entitlements; sourceTree = "<group>"; };
75FCB4EFD17838CCA4C93E4A /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
89BBA9195EECA9F4625BB1DC /* Pods_PNObject_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PNObject_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8C017F9292D546DE8DBC84F8 /* PNObject.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = PNObject.podspec; path = ../PNObject.podspec; sourceTree = "<group>"; };
9898F9E5E65D3B402429FD73 /* Pods-PNObject_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNObject_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example.debug.xcconfig"; sourceTree = "<group>"; };
9F4B24EF58CA8BC69BFC569B /* Pods-PNObject_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNObject_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PNObject_Tests/Pods-PNObject_Tests.debug.xcconfig"; sourceTree = "<group>"; };
BA17B1571B43FAFAA8B45B6D /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
CAD2FCF55030EAE61415B480 /* Pods_PNObject_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PNObject_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DE7587A588720F0590F50755 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
808511432137F18DDFE44210 /* Pods_PNObject_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PNObject_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
A90E0E5930707E3F83662328 /* Pods-PNObject_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNObject_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PNObject_Tests/Pods-PNObject_Tests.debug.xcconfig"; sourceTree = "<group>"; };
C0F786F6B0C2234E3B3A5F30 /* Pods-PNObject_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNObject_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PNObject_Tests/Pods-PNObject_Tests.release.xcconfig"; sourceTree = "<group>"; };
C6D958A9E3E6FFB7818A72C7 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
E20844B85780704FB196FA2A /* Pods-PNObject_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PNObject_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example.debug.xcconfig"; sourceTree = "<group>"; };
F74BC1D549B9C619883DB7E0 /* Pods_PNObject_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PNObject_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -75,7 +75,7 @@
6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */,
6003F592195388D20070C39A /* UIKit.framework in Frameworks */,
6003F58E195388D20070C39A /* Foundation.framework in Frameworks */,
3F3C53CB2D851F471BE8A604 /* Pods_PNObject_Example.framework in Frameworks */,
FD9FF8837F786E50B88531C0 /* Pods_PNObject_Example.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -86,34 +86,22 @@
6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */,
6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */,
6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */,
60D833DF200A0D946F2E8C4D /* Pods_PNObject_Tests.framework in Frameworks */,
2EC5979E315D48625574D725 /* Pods_PNObject_Tests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2DE160677BF4961C3D433201 /* Pods */ = {
isa = PBXGroup;
children = (
9898F9E5E65D3B402429FD73 /* Pods-PNObject_Example.debug.xcconfig */,
4082E2E9CCB7F56C1A30AF72 /* Pods-PNObject_Example.release.xcconfig */,
9F4B24EF58CA8BC69BFC569B /* Pods-PNObject_Tests.debug.xcconfig */,
2DBDB9BA06776F1FFBD6EDC3 /* Pods-PNObject_Tests.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
6003F581195388D10070C39A = {
isa = PBXGroup;
children = (
6805F44B1DF6DCC600C79B18 /* PNObject_Example.entitlements */,
60FF7A9C1954A5C5007DD14C /* Podspec Metadata */,
6003F593195388D20070C39A /* Example for PNObject */,
6003F5B5195388D20070C39A /* Tests */,
6003F58C195388D20070C39A /* Frameworks */,
6003F58B195388D20070C39A /* Products */,
2DE160677BF4961C3D433201 /* Pods */,
977F1D998CB9116027C75157 /* Pods */,
);
sourceTree = "<group>";
};
@ -133,9 +121,8 @@
6003F58F195388D20070C39A /* CoreGraphics.framework */,
6003F591195388D20070C39A /* UIKit.framework */,
6003F5AF195388D20070C39A /* XCTest.framework */,
DE7587A588720F0590F50755 /* Pods.framework */,
89BBA9195EECA9F4625BB1DC /* Pods_PNObject_Example.framework */,
CAD2FCF55030EAE61415B480 /* Pods_PNObject_Tests.framework */,
F74BC1D549B9C619883DB7E0 /* Pods_PNObject_Example.framework */,
808511432137F18DDFE44210 /* Pods_PNObject_Tests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -143,10 +130,11 @@
6003F593195388D20070C39A /* Example for PNObject */ = {
isa = PBXGroup;
children = (
6003F59C195388D20070C39A /* PNObjectAppDelegate.h */,
6003F59D195388D20070C39A /* PNObjectAppDelegate.m */,
6003F5A5195388D20070C39A /* PNObjectViewController.h */,
6003F5A6195388D20070C39A /* PNObjectViewController.m */,
6003F59C195388D20070C39A /* PNObjAppDelegate.h */,
6003F59D195388D20070C39A /* PNObjAppDelegate.m */,
873B8AEA1B1F5CCA007FD442 /* Main.storyboard */,
6003F5A5195388D20070C39A /* PNObjViewController.h */,
6003F5A6195388D20070C39A /* PNObjViewController.m */,
6003F5A8195388D20070C39A /* Images.xcassets */,
6003F594195388D20070C39A /* Supporting Files */,
);
@ -187,13 +175,24 @@
60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = {
isa = PBXGroup;
children = (
8C017F9292D546DE8DBC84F8 /* PNObject.podspec */,
BA17B1571B43FAFAA8B45B6D /* README.md */,
75FCB4EFD17838CCA4C93E4A /* LICENSE */,
2661CEE1F223361CD1DACC25 /* PNObject.podspec */,
3E392C278361604C1602CAF0 /* README.md */,
C6D958A9E3E6FFB7818A72C7 /* LICENSE */,
);
name = "Podspec Metadata";
sourceTree = "<group>";
};
977F1D998CB9116027C75157 /* Pods */ = {
isa = PBXGroup;
children = (
E20844B85780704FB196FA2A /* Pods-PNObject_Example.debug.xcconfig */,
116AF818F8117B91EF290C9A /* Pods-PNObject_Example.release.xcconfig */,
A90E0E5930707E3F83662328 /* Pods-PNObject_Tests.debug.xcconfig */,
C0F786F6B0C2234E3B3A5F30 /* Pods-PNObject_Tests.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -201,12 +200,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "PNObject_Example" */;
buildPhases = (
387C1DBC7D3E0673105ED546 /* [CP] Check Pods Manifest.lock */,
E4CE079F616C61EA34D08B8B /* [CP] Check Pods Manifest.lock */,
6003F586195388D20070C39A /* Sources */,
6003F587195388D20070C39A /* Frameworks */,
6003F588195388D20070C39A /* Resources */,
3695565DCFF7D29C95488878 /* [CP] Embed Pods Frameworks */,
B84699CAD21D4AE39D658EC6 /* [CP] Copy Pods Resources */,
C40CC8D7B1996A567BE82827 /* [CP] Embed Pods Frameworks */,
614EA73ED5E0ED0F8684468C /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -221,12 +220,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "PNObject_Tests" */;
buildPhases = (
F5E57EFDD9ADE887CAAAC837 /* [CP] Check Pods Manifest.lock */,
3FF38AA7E586B22171D1A118 /* [CP] Check Pods Manifest.lock */,
6003F5AA195388D20070C39A /* Sources */,
6003F5AB195388D20070C39A /* Frameworks */,
6003F5AC195388D20070C39A /* Resources */,
31A4834C1FAEFDFFFAEC6297 /* [CP] Embed Pods Frameworks */,
C2D74CFB72423672ACF097D4 /* [CP] Copy Pods Resources */,
746A765B48FD98342716D81F /* [CP] Embed Pods Frameworks */,
21411FBE0FE21EC75B6AB1E3 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -244,17 +243,12 @@
6003F582195388D10070C39A /* Project object */ = {
isa = PBXProject;
attributes = {
CLASSPREFIX = PNObject;
LastUpgradeCheck = 0820;
CLASSPREFIX = PNObj;
LastUpgradeCheck = 0720;
ORGANIZATIONNAME = "Giuseppe Nucifora";
TargetAttributes = {
6003F589195388D20070C39A = {
DevelopmentTeam = 825G85A28E;
SystemCapabilities = {
com.apple.Push = {
enabled = 1;
};
};
};
6003F5AD195388D20070C39A = {
TestTargetID = 6003F589195388D20070C39A;
@ -285,6 +279,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */,
6003F5A9195388D20070C39A /* Images.xcassets in Resources */,
6003F598195388D20070C39A /* InfoPlist.strings in Resources */,
);
@ -301,67 +296,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
31A4834C1FAEFDFFFAEC6297 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Tests/Pods-PNObject_Tests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3695565DCFF7D29C95488878 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
387C1DBC7D3E0673105ED546 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../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";
showEnvVarsInLog = 0;
};
B84699CAD21D4AE39D658EC6 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
C2D74CFB72423672ACF097D4 /* [CP] Copy Pods Resources */ = {
21411FBE0FE21EC75B6AB1E3 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -376,7 +311,67 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Tests/Pods-PNObject_Tests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
F5E57EFDD9ADE887CAAAC837 /* [CP] Check Pods Manifest.lock */ = {
3FF38AA7E586B22171D1A118 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../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";
showEnvVarsInLog = 0;
};
614EA73ED5E0ED0F8684468C /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
746A765B48FD98342716D81F /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Tests/Pods-PNObject_Tests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
C40CC8D7B1996A567BE82827 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PNObject_Example/Pods-PNObject_Example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
E4CE079F616C61EA34D08B8B /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -398,8 +393,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6003F59E195388D20070C39A /* PNObjectAppDelegate.m in Sources */,
6003F5A7195388D20070C39A /* PNObjectViewController.m in Sources */,
6003F59E195388D20070C39A /* PNObjAppDelegate.m in Sources */,
6003F5A7195388D20070C39A /* PNObjViewController.m in Sources */,
6003F59A195388D20070C39A /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -455,19 +450,14 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@ -480,8 +470,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
ONLY_ACTIVE_ARCH = NO;
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -500,25 +490,20 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
@ -527,15 +512,14 @@
};
6003F5C0195388D20070C39A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9898F9E5E65D3B402429FD73 /* Pods-PNObject_Example.debug.xcconfig */;
baseConfigurationReference = E20844B85780704FB196FA2A /* Pods-PNObject_Example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_ENTITLEMENTS = PNObject_Example.entitlements;
DEVELOPMENT_TEAM = 825G85A28E;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "PNObject/PNObject-Prefix.pch";
INFOPLIST_FILE = "PNObject/PNObject-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -545,15 +529,14 @@
};
6003F5C1195388D20070C39A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4082E2E9CCB7F56C1A30AF72 /* Pods-PNObject_Example.release.xcconfig */;
baseConfigurationReference = 116AF818F8117B91EF290C9A /* Pods-PNObject_Example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_ENTITLEMENTS = PNObject_Example.entitlements;
DEVELOPMENT_TEAM = 825G85A28E;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "PNObject/PNObject-Prefix.pch";
INFOPLIST_FILE = "PNObject/PNObject-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -563,9 +546,8 @@
};
6003F5C3195388D20070C39A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9F4B24EF58CA8BC69BFC569B /* Pods-PNObject_Tests.debug.xcconfig */;
baseConfigurationReference = A90E0E5930707E3F83662328 /* Pods-PNObject_Tests.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
@ -588,9 +570,8 @@
};
6003F5C4195388D20070C39A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 2DBDB9BA06776F1FFBD6EDC3 /* Pods-PNObject_Tests.release.xcconfig */;
baseConfigurationReference = C0F786F6B0C2234E3B3A5F30 /* Pods-PNObject_Tests.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -71,29 +71,7 @@
ReferencedContainer = "container:PNObject.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "NSAutoreleaseFreedObjectCheckEnabled"
value = "YES"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "NSDebugEnabled"
value = "YES"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</LaunchAction>
<ProfileAction

View File

@ -1,123 +1,48 @@
{
"images" : [
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-Small.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-Small@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-Small@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-40@3x.png",
"scale" : "3x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Icon.png",
"scale" : "1x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Icon@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@3x.png",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-Small.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-Small@2x.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-40.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-40@2x.png",
"scale" : "2x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "Icon-Small-50.png",
"scale" : "1x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "Icon-Small-50@2x.png",
"scale" : "2x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "Icon-72.png",
"scale" : "1x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "Icon-72@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-76.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-83.5@2x.png",
"scale" : "2x"
}
],
@ -125,4 +50,4 @@
"version" : 1,
"author" : "xcode"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 899 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 505 B

View File

@ -1,157 +1,46 @@
{
"images" : [
{
"extent" : "full-screen",
"idiom" : "iphone",
"filename" : "Default.png",
"orientation" : "portrait",
"scale" : "1x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"filename" : "Default@2x.png",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "full-screen",
"orientation" : "portrait",
"idiom" : "iphone",
"subtype" : "retina4",
"filename" : "Default-568h@2x.png",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"subtype" : "retina4",
"filename" : "Default-568h@2x.png",
"minimum-system-version" : "7.0",
"orientation" : "portrait",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"idiom" : "iphone",
"filename" : "Default@2x.png",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "to-status-bar",
"idiom" : "ipad",
"filename" : "Default~ipad.png",
"orientation" : "portrait",
"scale" : "1x"
},
{
"extent" : "to-status-bar",
"idiom" : "ipad",
"filename" : "Default~ipad@2x.png",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "to-status-bar",
"idiom" : "ipad",
"filename" : "Default~ipad~landscape.png",
"orientation" : "landscape",
"scale" : "1x"
},
{
"extent" : "to-status-bar",
"idiom" : "ipad",
"filename" : "Default~ipad~landscape@2x.png",
"orientation" : "landscape",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"filename" : "Default~ipad~nostatusbar.png",
"minimum-system-version" : "7.0",
"orientation" : "portrait",
"scale" : "1x"
},
{
"extent" : "full-screen",
"orientation" : "landscape",
"idiom" : "ipad",
"filename" : "Default~ipad~nostatusbar.png",
"orientation" : "portrait",
"scale" : "1x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"filename" : "Default~ipad~nostatusbar@2x.png",
"minimum-system-version" : "7.0",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"filename" : "Default~ipad~nostatusbar@2x.png",
"orientation" : "portrait",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"filename" : "Default~ipad~landscape~nostatusbar.png",
"minimum-system-version" : "7.0",
"orientation" : "landscape",
"scale" : "1x"
},
{
"extent" : "full-screen",
"orientation" : "portrait",
"idiom" : "ipad",
"filename" : "Default~ipad~landscape~nostatusbar.png",
"orientation" : "landscape",
"scale" : "1x"
},
{
"extent" : "full-screen",
"idiom" : "ipad",
"filename" : "Default~ipad~landscape~nostatusbar@2x.png",
"minimum-system-version" : "7.0",
"orientation" : "landscape",
"scale" : "2x"
},
{
"extent" : "full-screen",
"orientation" : "landscape",
"idiom" : "ipad",
"filename" : "Default~ipad~landscape~nostatusbar@2x.png",
"orientation" : "landscape",
"scale" : "2x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"subtype" : "736h",
"filename" : "Default-Portrait-736h@3x.png",
"minimum-system-version" : "8.0",
"orientation" : "portrait",
"scale" : "3x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"subtype" : "736h",
"filename" : "Default-Landscape-736h@3x.png",
"minimum-system-version" : "8.0",
"orientation" : "landscape",
"scale" : "3x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"subtype" : "667h",
"filename" : "Default-667h@2x.png",
"minimum-system-version" : "8.0",
"orientation" : "portrait",
"minimum-system-version" : "7.0",
"scale" : "2x"
}
],
@ -159,4 +48,4 @@
"version" : 1,
"author" : "xcode"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="whP-gf-Uak">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="wQg-tq-qST">
<objects>
<viewController id="whP-gf-Uak" customClass="PNObjViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="uEw-UM-LJ8"/>
<viewControllerLayoutGuide type="bottom" id="Mvr-aV-6Um"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="TpU-gO-2f1">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="tc2-Qw-aMS" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="305" y="433"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,15 @@
//
// PNObjAppDelegate.h
// PNObject
//
// Created by Giuseppe Nucifora on 12/28/2016.
// Copyright (c) 2016 Giuseppe Nucifora. All rights reserved.
//
@import UIKit;
@interface PNObjAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@ -1,36 +1,34 @@
//
// PNObjectAppDelegate.m
// PNObjAppDelegate.m
// PNObject
//
// Created by Giuseppe Nucifora on 12/28/2015.
// Copyright (c) 2015 Giuseppe Nucifora. All rights reserved.
// Created by Giuseppe Nucifora on 12/28/2016.
// Copyright (c) 2016 Giuseppe Nucifora. All rights reserved.
//
#import "PNObjectAppDelegate.h"
#import "PNObjectViewController.h"
#import "PNObjAppDelegate.h"
#import "PNObject.h"
#import "PNUser.h"
#import "PNAddress.h"
#import "PNObject+PNObjectConnection.h"
#import <PNObject/PNUser.h>
#import <PNObject/PNAddress.h>
#import <PNObject/PNObject+PNObjectConnection.h>
#import "PNObjViewController.h"
#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKLoginKit/FBSDKLoginKit.h>
#import <FBSDKShareKit/FBSDKShareKit.h>
#import "UIDevice-Hardware.h"
#import "PNInstallation.h"
#import <PNObject/PNInstallation.h>
@implementation PNObjectAppDelegate
@implementation PNObjAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[FBSDKSettings setAppID:@"213761522305123"];
[[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
@ -47,7 +45,7 @@
PNObjectViewController *viewController = [[PNObjectViewController alloc] init];
PNObjViewController *viewController = [[PNObjViewController alloc] init];
switch ([[UIDevice currentDevice] deviceFamily]) {
case UIDeviceFamilyiPhone:
@ -65,17 +63,17 @@
break;
}
}
_window.rootViewController = viewController;
_window.backgroundColor = [UIColor whiteColor];
[_window makeKeyAndVisible];
/*[PNUser loginCurrentUserWithEmail:@"socials2@giuseppenucifora.com" password:@"asdasdasd" withBlockSuccess:^(PNUser * _Nullable responseObject) {
} failure:^(NSError * _Nonnull error) {
}];*/
} failure:^(NSError * _Nonnull error) {
}];*/
if ([PNUser currentUser] && [[PNUser currentUser] isAuthenticated]) {
NSLogDebug(@"Login in corso...");
@ -87,6 +85,7 @@
}];
}
return YES;
}
@ -107,8 +106,9 @@
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[FBSDKAppEvents activateApp];
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
@ -116,17 +116,6 @@
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [[FBSDKApplicationDelegate sharedInstance] application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
#pragma mark - Remote Notification
- (void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

View File

@ -0,0 +1,13 @@
//
// PNObjViewController.h
// PNObject
//
// Created by Giuseppe Nucifora on 12/28/2016.
// Copyright (c) 2016 Giuseppe Nucifora. All rights reserved.
//
@import UIKit;
@interface PNObjViewController : UIViewController
@end

View File

@ -1,19 +1,19 @@
//
// PNObjectViewController.m
// PNObjViewController.m
// PNObject
//
// Created by Giuseppe Nucifora on 12/28/2015.
// Copyright (c) 2015 Giuseppe Nucifora. All rights reserved.
// Created by Giuseppe Nucifora on 12/28/2016.
// Copyright (c) 2016 Giuseppe Nucifora. All rights reserved.
//
#import "PNObjectViewController.h"
#import "PNObjViewController.h"
#import <PNObject/PNObject.h>
#import <PNObject/PNUser.h>
#import <PNObject/PNAddress.h>
#import <PNObject/PNObject+PNObjectConnection.h>
#import <PureLayout/PureLayout.h>
#import "PNObject.h"
#import "PNUser.h"
#import "PNAddress.h"
#import "PNObject+PNObjectConnection.h"
@interface PNObjectViewController ()
@interface PNObjViewController ()
@property (nonatomic) BOOL didSetupConstraints;
@ -23,15 +23,15 @@
@property (nonatomic, strong) UIButton *cancelToken;
@end
@implementation PNObjectViewController
@implementation PNObjViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_refreshToken = [UIButton newAutoLayoutView];
[_refreshToken addTarget:self action:@selector(refreshTokenAction) forControlEvents:UIControlEventTouchUpInside];
[_refreshToken setTitle:@"Refresh Token" forState:UIControlStateNormal];
@ -39,9 +39,9 @@
[_refreshToken.layer setBorderColor:[UIColor blackColor].CGColor];
[_refreshToken.layer setCornerRadius:4];
[_refreshToken.layer setBorderWidth:2];
[self.view addSubview:_refreshToken];
_apiCall = [UIButton newAutoLayoutView];
[_apiCall addTarget:self action:@selector(apiCallAction) forControlEvents:UIControlEventTouchUpInside];
[_apiCall setTitle:@"API Call" forState:UIControlStateNormal];
@ -49,9 +49,9 @@
[_apiCall.layer setBorderColor:[UIColor blackColor].CGColor];
[_apiCall.layer setCornerRadius:4];
[_apiCall.layer setBorderWidth:2];
[self.view addSubview:_apiCall];
_cancelToken = [UIButton newAutoLayoutView];
[_cancelToken addTarget:self action:@selector(cancelTokenAction) forControlEvents:UIControlEventTouchUpInside];
[_cancelToken setTitle:@"Reset Token" forState:UIControlStateNormal];
@ -59,31 +59,31 @@
[_cancelToken.layer setBorderColor:[UIColor blackColor].CGColor];
[_cancelToken.layer setCornerRadius:4];
[_cancelToken.layer setBorderWidth:2];
[self.view addSubview:_cancelToken];
//User * user = [User currentUser];
[self.view setNeedsUpdateConstraints];
}
- (void) updateViewConstraints {
if (!_didSetupConstraints) {
_didSetupConstraints = YES;
[self.view autoPinEdgesToSuperviewEdges];
[_refreshToken autoAlignAxisToSuperviewAxis:ALAxisVertical];
[_refreshToken autoAlignAxisToSuperviewAxis:ALAxisHorizontal];
[_refreshToken autoSetDimension:ALDimensionWidth toSize:140];
[_refreshToken autoSetDimension:ALDimensionHeight toSize:44];
[_apiCall autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:_refreshToken withOffset:-35];
[_apiCall autoAlignAxisToSuperviewAxis:ALAxisVertical];
[_apiCall autoSetDimension:ALDimensionWidth toSize:140];
[_apiCall autoSetDimension:ALDimensionHeight toSize:44];
[_cancelToken autoAlignAxisToSuperviewAxis:ALAxisVertical];
[_cancelToken autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:_refreshToken withOffset:35];
[_cancelToken autoSetDimension:ALDimensionWidth toSize:140];
@ -101,67 +101,68 @@
}
- (void) apiCallAction {
/*PNObjcPassword *password = [PNObjcPassword new];
[password setPassword:@"asdasdasd"];
[password setConfirmPassword:@"asdasdasd"];
User *user = [User new];
[user setFirstName:@"Giuseppe"];
[user setLastName:@"Nuficora"];
[user setEmail:@"packman5@giuseppenucifora.com"];
[user setPassword:password];
[user setHasAcceptedNewsletter:YES];
[user setHasAcceptedPrivacy:YES];
*/
[password setPassword:@"asdasdasd"];
[password setConfirmPassword:@"asdasdasd"];
User *user = [User new];
[user setFirstName:@"Giuseppe"];
[user setLastName:@"Nuficora"];
[user setEmail:@"packman5@giuseppenucifora.com"];
[user setPassword:password];
[user setHasAcceptedNewsletter:YES];
[user setHasAcceptedPrivacy:YES];
*/
//[user saveLocally];
NSLog(@"%@",[[PNUser currentUser] JSONFormObject]);
//NSLog(@"%@",[user JSONObjectMap]);
/*[user registerWithBlockSuccess:^(PNUser * _Nullable responseObject) {
} failure:^(NSError * _Nonnull error) {
}];*/
/*[[User currentUser] socialLoginWithBlockSuccessFromViewController:self
blockSuccess:^(PNUser * _Nullable responseObject) {
} failure:^(NSError * _Nonnull error) {
}];*/
//User * user = [User currentUser];
//if ([user isAuthenticated]) {
/*[User loginCurrentUserWithEmail:@"demo@packman.example" password:@"demo@packman.example" withBlockSuccess:^(PNUser * _Nullable responseObject) {
NSLog(@"response : %@",responseObject);
NSLog(@"%@",[User currentUser]);
} failure:^(NSError * _Nonnull error) {
NSLog(@"response : %@",error);
}];*/
NSLog(@"response : %@",responseObject);
NSLog(@"%@",[User currentUser]);
} failure:^(NSError * _Nonnull error) {
NSLog(@"response : %@",error);
}];*/
[PNUser socialLoginWithBlockSuccess:^(PNUser * _Nullable responseObject) {
} failure:^(NSError * _Nonnull error) {
}];
/*[User resetPasswordForEmail:@"packman@giuseppenucifora.com" Progress:^(NSProgress * _Nonnull uploadProgress) {
} Success:^(NSDictionary * _Nullable responseObject) {
} failure:^(NSError * _Nonnull error) {
}];*/
} Success:^(NSDictionary * _Nullable responseObject) {
} failure:^(NSError * _Nonnull error) {
}];*/
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];

View File

@ -24,6 +24,25 @@
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
@ -64,26 +83,6 @@
</dict>
</dict>
</dict>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIRequiresFullScreen</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>

View File

@ -1,15 +0,0 @@
//
// PNObjectAppDelegate.h
// PNObject
//
// Created by Giuseppe Nucifora on 12/28/2015.
// Copyright (c) 2015 Giuseppe Nucifora. All rights reserved.
//
@import UIKit;
@interface PNObjectAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@ -1,13 +0,0 @@
//
// PNObjectViewController.h
// PNObject
//
// Created by Giuseppe Nucifora on 12/28/2015.
// Copyright (c) 2015 Giuseppe Nucifora. All rights reserved.
//
@import UIKit;
@interface PNObjectViewController : UIViewController
@end

View File

@ -2,16 +2,16 @@
// main.m
// PNObject
//
// Created by Giuseppe Nucifora on 12/28/2015.
// Copyright (c) 2015 Giuseppe Nucifora. All rights reserved.
// Created by Giuseppe Nucifora on 12/28/2016.
// Copyright (c) 2016 Giuseppe Nucifora. All rights reserved.
//
@import UIKit;
#import "PNObjectAppDelegate.h"
#import "PNObjAppDelegate.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([PNObjectAppDelegate class]));
return UIApplicationMain(argc, argv, nil, NSStringFromClass([PNObjAppDelegate class]));
}
}

View File

@ -1,8 +0,0 @@
<?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>aps-environment</key>
<string>development</string>
</dict>
</plist>

View File

@ -1,27 +1,13 @@
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
target 'PNObject_Example' do
pod 'PNObject', :path => '../'
pod 'PEAR-FileManager-iOS'
pod 'NSDate_Utils'
pod 'UIDevice-Utils'
pod 'AFNetworking'
pod 'nv-ios-http-status'
pod 'NSString-Helper'
pod 'CodFis-Helper'
pod 'StrongestPasswordValidator'
pod 'PureLayout'
pod 'FBSDKCoreKit'
pod 'FBSDKShareKit'
pod 'FBSDKLoginKit'
pod 'PNObject', :path => '../'
pod 'PureLayout'
end
target 'PNObject_Tests' do
inherit! :search_paths
target 'PNObject_Tests' do
pod 'PNObject', :path => '../'
pod 'Specta'
pod 'Expecta'
pod 'FBSnapshotTestCase'
pod 'Expecta+Snapshots'
pod 'Expecta'
end
end

View File

@ -22,40 +22,35 @@ PODS:
- Bolts/Tasks (1.8.4)
- CocoaSecurity (1.2.4)
- CodFis-Helper (0.1.3)
- DDDKeychainWrapper (1.0.0)
- DJLocalization (1.2.2):
- DJLocalization/Core (= 1.2.2)
- DJLocalization/Core (1.2.2)
- Expecta (1.0.5)
- Expecta+Snapshots (3.0.0):
- Expecta (~> 1.0)
- FBSnapshotTestCase/Core (~> 2.0)
- Specta (~> 1.0)
- FBSDKCoreKit (4.18.0):
- Bolts (~> 1.7)
- FBSDKLoginKit (4.18.0):
- FBSDKCoreKit
- FBSDKShareKit (4.18.0):
- FBSDKCoreKit
- FBSnapshotTestCase (2.1.4):
- FBSnapshotTestCase/SwiftSupport (= 2.1.4)
- FBSnapshotTestCase/Core (2.1.4)
- FBSnapshotTestCase/SwiftSupport (2.1.4):
- FBSnapshotTestCase/Core
- NACrypto (1.0.6)
- NSDate_Utils (0.1.3)
- libsodium (1.0.11)
- NAChloride (2.2.1):
- libsodium
- NSDate_Utils (1.0.0)
- NSString-Helper (1.0.5)
- NSUserDefaults-AESEncryptor (0.0.4):
- CocoaSecurity (~> 1.2.2)
- nv-ios-http-status (0.0.1)
- PEAR-FileManager-iOS (1.3.1)
- PNObject (0.9.0):
- PNObject (1.0.0):
- AFNetworking
- CodFis-Helper
- DDDKeychainWrapper
- DJLocalization
- FBSDKCoreKit
- FBSDKLoginKit
- FBSDKShareKit
- NACrypto
- NAChloride
- NSDate_Utils
- NSString-Helper
- NSUserDefaults-AESEncryptor
@ -71,23 +66,10 @@ PODS:
- UIDevice-Utils (0.1.6)
DEPENDENCIES:
- AFNetworking
- CodFis-Helper
- Expecta
- Expecta+Snapshots
- FBSDKCoreKit
- FBSDKLoginKit
- FBSDKShareKit
- FBSnapshotTestCase
- NSDate_Utils
- NSString-Helper
- nv-ios-http-status
- PEAR-FileManager-iOS
- PNObject (from `../`)
- PureLayout
- Specta
- StrongestPasswordValidator
- UIDevice-Utils
EXTERNAL SOURCES:
PNObject:
@ -98,26 +80,26 @@ SPEC CHECKSUMS:
Bolts: 8a7995239dbe724f9cba2248b766d48b7ebdd322
CocoaSecurity: d288a6f87e0f363823d2cb83e753814a6944f71a
CodFis-Helper: 28be4c74d7202542459d72354f59b1215871de87
DDDKeychainWrapper: e681a4daba6448786fa83b4941f58102a33b1897
DJLocalization: 0c84029af375647d4104a42ae36be87194c46c47
Expecta: e1c022fcd33910b6be89c291d2775b3fe27a89fe
Expecta+Snapshots: c343f410c7a6392f3e22e78f94c44b6c0749a516
FBSDKCoreKit: 15fef8804a4629f98c6f4e55e81a76c9d725d85e
FBSDKLoginKit: 6773073e970b2b15fb12e451ce7f11da0532b880
FBSDKShareKit: 0b8d6cc3f103c75297eb3c62caec284a2ccf1b9e
FBSnapshotTestCase: '094f9f314decbabe373b87cc339bea235a63e07a'
NACrypto: ce3900f1775f1b0cc27ce7c4953b94c598a74149
NSDate_Utils: 68669d2c81f310ee13026c791f4f0ed227b94c65
libsodium: 9aba161d2ee096977ecbdcce1ada69ffe511970c
NAChloride: 8f3d4f9a20df6b68840789a22a70aa4fcc437b0c
NSDate_Utils: 45d47afab329001ccafe056308d0cc05460e5298
NSString-Helper: 459e1b6a62b3bf7db10f01b0d102548608e945c4
NSUserDefaults-AESEncryptor: da02cfef056f1e18ebe2748767915f08b274c9c5
nv-ios-http-status: b6c2b5fc8656cc19e0d3000dadce2080b99d0e2f
PEAR-FileManager-iOS: 3bc403f68a53483f5629aa822f4649e40275c4d3
PNObject: f8d8116d0f72252552bddcbc3703d6d1e4719354
PNObject: 34839792d6fcc437b3123d0770b4d45e845bf2c9
PureLayout: 4d550abe49a94f24c2808b9b95db9131685fe4cd
RZDataBinding: 6981e90ddaae2f5e02028323b1043f8c31013109
Specta: ac94d110b865115fe60ff2c6d7281053c6f8e8a2
StrongestPasswordValidator: 921e42615bdf353513c6f925bffd4fc29865dbd7
UIDevice-Utils: 11c10b18d3c6489b45a97436e5ae6064a3622820
PODFILE CHECKSUM: 7c7096ec3fa75c9e7a1d2b538e5719592b9f5e60
PODFILE CHECKSUM: 57090c7ea88a91b49ca8dd73d9e7c6b866e772ba
COCOAPODS: 1.2.0.beta.1

View File

@ -1,6 +1,4 @@
The MIT License (MIT)
Copyright (c) 2015 Gabriel Handford
Copyright (c) 2014 axldyb <aksel.dybdal@shortcut.no>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -9,14 +7,13 @@ 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 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.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,46 @@
//
// DDDKeychainWrapper.h
// Aksel Dybdal
//
// Created by Aksel Dybdal on 02.04.14.
// Copyright (c) 2014 Aksel Dybdal. All rights reserved.
//
/**
A small wrapper for Keychain access.
Inspired by:
http://useyourloaf.com/blog/2010/03/29/simple-iphone-keychain-access.html
*/
#import <Foundation/Foundation.h>
@interface DDDKeychainWrapper : NSObject
+ (void)setString:(NSString *)string forKey:(NSString *)key;
+ (NSString *)stringForKey:(NSString *)key;
+ (void)setDate:(NSDate *)date forKey:(NSString *)key;
+ (NSDate *)dateForKey:(NSString *)key;
+ (void)setData:(NSData *)data forKey:(NSString *)key;
+ (NSData *)dataForKey:(NSString *)key;
+ (void)setArray:(NSArray *)array forKey:(NSString *)key;
+ (NSArray *)arrayForKey:(NSString *)key;
+ (void)setDictionary:(NSDictionary *)dictionary forKey:(NSString *)key;
+ (NSDictionary *)dictionaryForKey:(NSString *)key;
+ (void)setNumber:(NSNumber *)number forKey:(NSString *)key;
+ (NSNumber *)numberForKey:(NSString *)key;
+ (void)setBoolean:(BOOL)boolean forKey:(NSString *)key;
+ (BOOL)booleanForKey:(NSString *)key;
+ (void)setObject:(id)object forKey:(NSString *)key;
+ (id)objectForKey:(NSString *)key;
+ (void)wipeKeychain;
@end

View File

@ -0,0 +1,373 @@
//
// DDDKeychainWrapper.m
// Aksel Dybdal
//
// Created by Aksel Dybdal on 02.04.14.
// Copyright (c) 2014 Aksel Dybdal. All rights reserved.
//
#import "DDDKeychainWrapper.h"
#import <Security/Security.h>
typedef NS_ENUM(NSUInteger, DDDKeychainWrapperErrorCode) {
DDDKeychainWrapperErrorCreatingKeychainValue = 1,
DDDKeychainWrapperErrorUpdatingKeychainValue,
DDDKeychainWrapperErrorDeletingKeychainValue,
DDDKeychainWrapperErrorSearchingKeychainValue
};
NSString *const kDDDKeychainWrapperServiceName = @"com.dddkeychainwrapper.keychainService";
NSString *const kDDDKeychainWrapperErrorDomain = @"DDDKeychainWrapperErrorDomain";
#ifdef DEBUG
# warning "Including NSLog"
# define DDDLOG(s, ...) NSLog(s, ## __VA_ARGS__)
#else
# define DDDLOG(s, ...) while(0){}
#endif
@implementation DDDKeychainWrapper
#pragma mark - String
+ (void)setString:(NSString *)string forKey:(NSString *)key
{
NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding];
[self setData:stringData forIdentifier:key];
}
+ (NSString *)stringForKey:(NSString *)key
{
NSData *stringData = [self dataForIdentifier:key];
return [[NSString alloc] initWithData:stringData encoding:NSUTF8StringEncoding];
}
#pragma mark - Date
+ (void)setDate:(NSDate *)date forKey:(NSString *)key
{
NSData *dateData = [NSKeyedArchiver archivedDataWithRootObject:date];
[self setData:dateData forIdentifier:key];
}
+ (NSDate *)dateForKey:(NSString *)key
{
NSData *dateData = [self dataForIdentifier:key];
return (NSDate *)[NSKeyedUnarchiver unarchiveObjectWithData:dateData];
}
#pragma mark - Data
+ (void)setData:(NSData *)data forKey:(NSString *)key
{
[self setData:data forIdentifier:key];
}
+ (NSData *)dataForKey:(NSString *)key
{
return [self dataForIdentifier:key];
}
#pragma mark - Array
+ (void)setArray:(NSArray *)array forKey:(NSString *)key
{
for (id obj in array) {
NSAssert([obj conformsToProtocol:@protocol(NSCoding)], @"Objects must confirm to NSCoding protocol in order to be stored in keychain");
}
NSData *arrayData = [NSKeyedArchiver archivedDataWithRootObject:array];
[self setData:arrayData forIdentifier:key];
}
+ (NSArray *)arrayForKey:(NSString *)key
{
NSData *arrayData = [self dataForIdentifier:key];
return (NSArray *)[NSKeyedUnarchiver unarchiveObjectWithData:arrayData];
}
#pragma mark - Dictionary
+ (void)setDictionary:(NSDictionary *)dictionary forKey:(NSString *)key
{
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSAssert([key conformsToProtocol:@protocol(NSCoding)], @"Keys must confirm to NSCoding protocol in order to be stored in keychain");
NSAssert([obj conformsToProtocol:@protocol(NSCoding)], @"Objects must confirm to NSCoding protocol in order to be stored in keychain");
}];
NSData *dictionaryData = [NSKeyedArchiver archivedDataWithRootObject:dictionary];
[self setData:dictionaryData forIdentifier:key];
}
+ (NSDictionary *)dictionaryForKey:(NSString *)key
{
NSData *dictionaryData = [self dataForIdentifier:key];
return (NSDictionary *)[NSKeyedUnarchiver unarchiveObjectWithData:dictionaryData];
}
#pragma mark - Number
+ (void)setNumber:(NSNumber *)number forKey:(NSString *)key
{
NSData *numberData = [NSKeyedArchiver archivedDataWithRootObject:number];
[self setData:numberData forIdentifier:key];
}
+ (NSNumber *)numberForKey:(NSString *)key
{
NSData *numberData = [self dataForIdentifier:key];
return (NSNumber *)[NSKeyedUnarchiver unarchiveObjectWithData:numberData];
}
#pragma mark - BOOL
+ (void)setBoolean:(BOOL)boolean forKey:(NSString *)key
{
NSNumber *boolNumber = [NSNumber numberWithBool:boolean];
[self setNumber:boolNumber forKey:key];
}
+ (BOOL)booleanForKey:(NSString *)key
{
NSNumber *boolNumber = [self numberForKey:key];
return [boolNumber boolValue];
}
#pragma mark - Object
+ (void)setObject:(id)object forKey:(NSString *)key
{
NSAssert([object conformsToProtocol:@protocol(NSCoding)], @"Object must confirm to NSCoding protocol in order to be stored in keychain");
NSData *objectData = [NSKeyedArchiver archivedDataWithRootObject:object];
[self setData:objectData forIdentifier:key];
}
+ (id)objectForKey:(NSString *)key
{
NSData *objectData = [self dataForIdentifier:key];
return (id)[NSKeyedUnarchiver unarchiveObjectWithData:objectData];
}
#pragma mark - Clear Keychain
+ (void)wipeKeychain
{
NSArray *secItemClasses = @[(__bridge id)kSecClassGenericPassword,
(__bridge id)kSecClassInternetPassword,
(__bridge id)kSecClassCertificate,
(__bridge id)kSecClassKey,
(__bridge id)kSecClassIdentity];
for (id secItemClass in secItemClasses) {
NSDictionary *spec = @{(__bridge id)kSecClass: (id)secItemClass};
SecItemDelete((__bridge CFDictionaryRef)spec);
}
}
#pragma mark - Private
+ (void)setData:(NSData *)data forIdentifier:(NSString *)identifier
{
NSError *error = nil;
// If no data provided we assume we want to delete the value
if (nil == data || NO == [data bytes]) {
[self deleteKeychainValueForIdentifier:identifier error:&error];
if (error) {
DDDLOG(@"Error deleting keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
}
return;
}
// We first look up the key in order to see if we need to update or create the value
if ([self searchKeychainCopyMatching:identifier error:&error]) {
if (error) {
DDDLOG(@"Error finding keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
return;
}
if (![self updateKeychainValue:data forIdentifier:identifier error:&error]) {
DDDLOG(@"Error updating keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
}
} else {
if (![self createKeychainValue:data forIdentifier:identifier error:&error]) {
DDDLOG(@"Error creating keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
}
}
}
+ (NSData *)dataForIdentifier:(NSString *)identifier
{
NSError *error = nil;
NSData *stringData = [self searchKeychainCopyMatching:identifier error:&error];
if (error) {
DDDLOG(@"Error finding keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
return nil;
}
return stringData;
}
+ (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier
{
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
[searchDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrGeneric];
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrAccount];
[searchDictionary setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge id)kSecAttrAccessible];
[searchDictionary setObject:kDDDKeychainWrapperServiceName forKey:(__bridge id)kSecAttrService];
return searchDictionary;
}
+ (NSData *)searchKeychainCopyMatching:(NSString *)identifier error:(NSError **)error
{
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
[searchDictionary setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
[searchDictionary setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
CFDataRef result;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, (CFTypeRef *)&result);
if (status != errSecSuccess) {
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorSearchingKeychainValue];
return nil;
}
NSData *data = (__bridge NSData *)result;
return data;
}
+ (BOOL)createKeychainValue:(NSData *)data forIdentifier:(NSString *)identifier error:(NSError **)error
{
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
[dictionary setObject:data forKey:(__bridge id)kSecValueData];
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dictionary, NULL);
if (status == errSecSuccess) {
return YES;
}
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorCreatingKeychainValue];
return NO;
}
+ (BOOL)updateKeychainValue:(NSData *)data forIdentifier:(NSString *)identifier error:(NSError **)error
{
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
[updateDictionary setObject:data forKey:(__bridge id)kSecValueData];
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)searchDictionary,
(__bridge CFDictionaryRef)updateDictionary);
if (status == errSecSuccess) {
return YES;
}
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorUpdatingKeychainValue];
return NO;
}
+ (BOOL)deleteKeychainValueForIdentifier:(NSString *)identifier error:(NSError **)error
{
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)searchDictionary);
if (status == errSecSuccess) {
return YES;
}
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorDeletingKeychainValue];
return NO;
}
#pragma mark - Error
+ (void)keychainError:(NSError **)error forStatus:(OSStatus)status domain:(NSUInteger)domain
{
NSString *errorString = @"";
switch (status) {
case errSecUnimplemented:
errorString = @"Function or operation not implemented.";
break;
case errSecIO:
errorString = @"I/O error (bummers)";
break;
case errSecOpWr:
errorString = @"File already open with write permission";
break;
case errSecParam:
errorString = @"One or more parameters passed to a function where not valid.";
break;
case errSecAllocate:
errorString = @"Failed to allocate memory.";
break;
case errSecUserCanceled:
errorString = @"User canceled the operation.";
break;
case errSecBadReq:
errorString = @"Bad parameter or invalid state for operation.";
break;
case errSecInternalComponent:
errorString = @"errSecInternalComponent";
break;
case errSecNotAvailable:
errorString = @"No keychain is available. You may need to restart your computer.";
break;
case errSecDuplicateItem:
errorString = @"The specified item already exists in the keychain.";
break;
case errSecItemNotFound:
errorString = @"The specified item could not be found in the keychain.";
break;
case errSecInteractionNotAllowed:
errorString = @"User interaction is not allowed.";
break;
case errSecDecode:
errorString = @"Unable to decode the provided data.";
break;
case errSecAuthFailed:
errorString = @"The user name or passphrase you entered is not correct.";
break;
default:
errorString = @"Unknown error";
break;
}
*error = [NSError errorWithDomain:kDDDKeychainWrapperErrorDomain
code:domain
userInfo:@{NSLocalizedDescriptionKey: errorString}];
}
@end

View File

@ -0,0 +1,47 @@
# DDDKeychainWrapper
[![CI Status](http://img.shields.io/travis/axldyb/DDDKeychainWrapper.svg?style=flat)](https://travis-ci.org/axldyb/DDDKeychainWrapper)
[![Version](https://img.shields.io/cocoapods/v/DDDKeychainWrapper.svg?style=flat)](http://cocoadocs.org/docsets/DDDKeychainWrapper)
[![License](https://img.shields.io/cocoapods/l/DDDKeychainWrapper.svg?style=flat)](http://cocoadocs.org/docsets/DDDKeychainWrapper)
[![Platform](https://img.shields.io/cocoapods/p/DDDKeychainWrapper.svg?style=flat)](http://cocoadocs.org/docsets/DDDKeychainWrapper)
## Usage
Storing your sensitive data in the keychain can take up a lot of time and effort. It should be easy to just drop something in there and retrive it with a few simple lines of code. DDDKeychainWrapper offers this simplicity and here is how we do it:
```objective-c
// Writing to the Keychain
[DDDKeychainWrapper setString:@"Secret string" forKey:@"my_key"];
// Reading from the Keychain
NSString *secretString = [DDDKeychainWrapper stringForKey:@"my_key"];
```
DDDKeychainWrapper has support for the following types
* NSString
* NSDate
* NSData
* NSArray
* NSDictionary
* NSNumber
* BOOL
* Objects (Must confirm to NSCoding)
A method for wiping the keychain data inserted via the wrapper exists as well.
## Installation
DDDKeychainWrapper is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
pod "DDDKeychainWrapper"
## Author
axldyb, aksel.dybdal@shortcut.no
## License
DDDKeychainWrapper is available under the MIT license. See the LICENSE file for more info.

View File

@ -1,14 +0,0 @@
#import <Expecta/Expecta.h>
#import "ExpectaObject+FBSnapshotTest.h"
@interface EXPExpectFBSnapshotTest : NSObject
@end
/// Set the default folder for image tests to run in
extern void setGlobalReferenceImageDir(char *reference);
EXPMatcherInterface(haveValidSnapshot, (void));
EXPMatcherInterface(recordSnapshot, (void));
EXPMatcherInterface(haveValidSnapshotNamed, (NSString *snapshot));
EXPMatcherInterface(recordSnapshotNamed, (NSString *snapshot));

View File

@ -1,284 +0,0 @@
#import "EXPMatchers+FBSnapshotTest.h"
#import <Expecta/EXPMatcherHelpers.h>
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
@interface EXPExpectFBSnapshotTest()
@property (nonatomic, strong) NSString *referenceImagesDirectory;
@end
@implementation EXPExpectFBSnapshotTest
+ (id)instance
{
static EXPExpectFBSnapshotTest *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
+ (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer snapshot:(NSString *)snapshot testCase:(id)testCase record:(BOOL)record referenceDirectory:(NSString *)referenceDirectory error:(NSError **)error
{
FBSnapshotTestController *snapshotController = [[FBSnapshotTestController alloc] initWithTestClass:[testCase class]];
snapshotController.recordMode = record;
snapshotController.referenceImagesDirectory = referenceDirectory;
snapshotController.usesDrawViewHierarchyInRect = [Expecta usesDrawViewHierarchyInRect];
if (! snapshotController.referenceImagesDirectory) {
[NSException raise:@"Missing value for referenceImagesDirectory" format:@"Call [[EXPExpectFBSnapshotTest instance] setReferenceImagesDirectory"];
}
return [snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
selector:NSSelectorFromString(snapshot)
identifier:nil
tolerance:0
error:error];
}
+ (NSString *)combinedError:(NSString *)message test:(NSString *)test error:(NSError *)error
{
NSAssert(message, @"missing message");
NSAssert(test, @"missing test name");
NSMutableArray *ary = [NSMutableArray array];
[ary addObject:[NSString stringWithFormat:@"%@ %@", message, test]];
for(NSString *key in error.userInfo.keyEnumerator) {
[ary addObject:[NSString stringWithFormat:@" %@: %@", key, [error.userInfo valueForKey:key]]];
}
return [ary componentsJoinedByString:@"\n"];
}
@end
void setGlobalReferenceImageDir(char *reference) {
NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%s", reference];
[[EXPExpectFBSnapshotTest instance] setReferenceImagesDirectory:referenceImagesDirectory];
};
@interface EXPExpect(ReferenceDirExtension)
- (NSString *)_getDefaultReferenceDirectory;
@end
@implementation EXPExpect(ReferenceDirExtension)
- (NSString *)_getDefaultReferenceDirectory
{
NSString *globalReference = [[EXPExpectFBSnapshotTest instance] referenceImagesDirectory];
if (globalReference) {
return globalReference;
}
// Search the test file's path to find the first folder with the substring "tests"
// then append "/ReferenceImages" and use that
NSString *testFileName = [NSString stringWithCString:self.fileName encoding:NSUTF8StringEncoding];
NSArray *pathComponents = [testFileName pathComponents];
for (NSString *folder in pathComponents) {
if ([folder.lowercaseString rangeOfString:@"tests"].location != NSNotFound) {
NSArray *folderPathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents indexOfObject:folder] + 1)];
return [NSString stringWithFormat:@"%@/ReferenceImages", [folderPathComponents componentsJoinedByString:@"/"]];
}
}
[NSException raise:@"Could not infer reference image folder" format:@"You should provide a reference dir using setGlobalReferenceImageDir(FB_REFERENCE_IMAGE_DIR);"];
return nil;
}
@end
#import <Specta/Specta.h>
#import <Specta/SpectaUtility.h>
#import <Specta/SPTExample.h>
NSString *sanitizedTestPath();
NSString *sanitizedTestPath(){
id compiledExample = [[NSThread currentThread] threadDictionary][@"SPTCurrentSpec"]; // SPTSpec
NSString *name;
if ([compiledExample respondsToSelector:@selector(name)]) {
// Specta 0.3 syntax
name = [compiledExample performSelector:@selector(name)];
} else if ([compiledExample respondsToSelector:@selector(fileName)]) {
// Specta 0.2 syntax
name = [compiledExample performSelector:@selector(fileName)];
}
name = [[[[name componentsSeparatedByString:@" test_"] lastObject] stringByReplacingOccurrencesOfString:@"__" withString:@"_"] stringByReplacingOccurrencesOfString:@"]" withString:@""];
return name;
}
EXPMatcherImplementationBegin(haveValidSnapshot, (void)){
__block NSError *error = nil;
prerequisite(^BOOL{
return actual;
});
match(^BOOL{
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
NSString *name = sanitizedTestPath();
if ([actual isKindOfClass:UIViewController.class]) {
[actual beginAppearanceTransition:YES animated:NO];
[actual endAppearanceTransition];
actual = [actual view];
}
return [EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:name testCase:[self testCase] record:NO referenceDirectory:referenceImageDir 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];
});
failureMessageForNotTo(^NSString *{
return [EXPExpectFBSnapshotTest combinedError:@"expected to not have a matching snapshot in" test:sanitizedTestPath() error:error];
});
}
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 actual && 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];
[actual endAppearanceTransition];
actual = [actual view];
}
[EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:sanitizedTestPath() testCase:[self testCase] record:YES referenceDirectory:referenceImageDir 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];
}
if (error) {
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a snapshot in" test:sanitizedTestPath() error:error];
} else {
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", sanitizedTestPath()];
}
});
failureMessageForNotTo(^NSString *{
if (error) {
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a snapshot in" test:sanitizedTestPath() error:error];
} else {
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", sanitizedTestPath()];
}
});
}
EXPMatcherImplementationEnd
EXPMatcherImplementationBegin(haveValidSnapshotNamed, (NSString *snapshot)){
BOOL snapshotIsNil = (snapshot == nil);
__block NSError *error = nil;
prerequisite(^BOOL{
return actual && !(snapshotIsNil);
});
match(^BOOL{
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
if ([actual isKindOfClass:UIViewController.class]) {
[actual beginAppearanceTransition:YES animated:NO];
[actual endAppearanceTransition];
actual = [actual view];
}
return [EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:snapshot testCase:[self testCase] record:NO referenceDirectory:referenceImageDir 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];
});
failureMessageForNotTo(^NSString *{
return [EXPExpectFBSnapshotTest combinedError:@"expected not to have a matching snapshot named" test:snapshot error:error];
});
}
EXPMatcherImplementationEnd
EXPMatcherImplementationBegin(recordSnapshotNamed, (NSString *snapshot)) {
BOOL snapshotExists = (snapshot != nil);
BOOL actualIsViewLayerOrViewController = ([actual isKindOfClass:UIView.class] || [actual isKindOfClass:CALayer.class] || [actual isKindOfClass:UIViewController.class]);
__block NSError *error = nil;
id actualRef = actual;
prerequisite(^BOOL{
return actualRef && 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];
[actual endAppearanceTransition];
actual = [actual view];
}
[EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:snapshot testCase:[self testCase] record:YES referenceDirectory:referenceImageDir 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];
}
if (error) {
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a matching snapshot named" test:snapshot error:error];
} else {
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", snapshot];
}
});
failureMessageForNotTo(^NSString *{
if (!actualIsViewLayerOrViewController) {
return [EXPExpectFBSnapshotTest combinedError:@"Expected a View, Layer or View Controller." test:snapshot error:nil];
}
if (error) {
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a matching snapshot named" test:snapshot error:error];
} else {
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", snapshot];
}
});
}
EXPMatcherImplementationEnd

View File

@ -1,17 +0,0 @@
//
// ExpectaObject+FBSnapshotTest.h
// Expecta+Snapshots
//
// Created by John Boiles on 8/3/15.
// Copyright (c) 2015 Expecta+Snapshots All rights reserved.
//
#import <Expecta/ExpectaObject.h>
@interface Expecta (FBSnapshotTest)
+ (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect;
+ (BOOL)usesDrawViewHierarchyInRect;
@end

View File

@ -1,25 +0,0 @@
//
// ExpectaObject+FBSnapshotTest.m
// Expecta+Snapshots
//
// Created by John Boiles on 8/3/15.
// Copyright (c) 2015 Expecta+Snapshots All rights reserved.
//
#import "ExpectaObject+FBSnapshotTest.h"
#import <objc/runtime.h>
static NSString const *kUsesDrawViewHierarchyInRectKey = @"ExpectaObject+FBSnapshotTest.usesDrawViewHierarchyInRect";
@implementation Expecta (FBSnapshotTest)
+ (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect {
objc_setAssociatedObject(self, (__bridge const void *)(kUsesDrawViewHierarchyInRectKey), @(usesDrawViewHierarchyInRect), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (BOOL)usesDrawViewHierarchyInRect {
NSNumber *usesDrawViewHierarchyInRect = objc_getAssociatedObject(self, (__bridge const void *)(kUsesDrawViewHierarchyInRectKey));
return usesDrawViewHierarchyInRect.boolValue;
}
@end

View File

@ -1,22 +0,0 @@
MIT License
Copyright (c) 2014 Daniel Doubrovkine, Artsy Inc.
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.

View File

@ -1,87 +0,0 @@
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)
### Usage
Add `Expecta+Snapshots` to your Podfile, the latest `FBSnapshotTestCase` will come in as a dependency.
``` ruby
pod 'Expecta+Snapshots'
```
### App setup
Use `expect(view).to.recordSnapshotNamed(@"unique snapshot name")` to record a snapshot and `expect(view).to.haveValidSnapshotNamed(@"unique snapshot name")` to check it.
If you project was compiled with Specta included, you have two extra methods that use the spec hierarchy to generate the snapshot name for you: `recordSnapshot()` and `haveValidSnapshot()`. You should only call these once per `it()` block.
If you need the `usesDrawViewHierarchyInRect` property in order to correctly render UIVisualEffect, UIAppearance and Size Classes, call `[Expecta setUsesDrawViewHierarchyInRect:NO];` inside `beforeAll`.
``` Objective-C
#define EXP_SHORTHAND
#include <Specta/Specta.h>
#include <Expecta/Expecta.h>
#include <Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h>
#include "FBExampleView.h"
SpecBegin(FBExampleView)
describe(@"manual matching", ^{
it(@"matches view", ^{
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
expect(view).to.recordSnapshotNamed(@"FBExampleView");
expect(view).to.haveValidSnapshotNamed(@"FBExampleView");
});
it(@"doesn't match a view", ^{
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
expect(view).toNot.haveValidSnapshotNamed(@"FBExampleViewDoesNotExist");
});
});
describe(@"test name derived matching", ^{
it(@"matches view", ^{
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
expect(view).to.recordSnapshot();
expect(view).to.haveValidSnapshot();
});
it(@"doesn't match a view", ^{
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
expect(view).toNot.haveValidSnapshot();
});
});
SpecEnd
```
### 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.
```
beforeAll(^{
setGlobalReferenceImageDir(FB_REFERENCE_IMAGE_DIR);
});
```
### Example
A complete project can be found in [FBSnapshotTestCaseDemo](FBSnapshotTestCaseDemo).
Notably, take a look at [FBSnapshotTestCaseDemoSpecs.m](FBSnapshotTestCaseDemo/FBSnapshotTestCaseDemoTests/FBSnapshotTestCaseDemoSpecs.m) for a complete example, which is an expanded Specta version version of [FBSnapshotTestCaseDemoTests.m](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCaseDemo/FBSnapshotTestCaseDemoTests/FBSnapshotTestCaseDemoTests.m).
Finally you can consult the tests for [ARTiledImageView](https://github.com/dblock/ARTiledImageView/tree/master/IntegrationTests) or [NAMapKit](https://github.com/neilang/NAMapKit/tree/master/Demo/DemoTests).
### License
MIT, see [LICENSE](LICENSE.md)

View File

@ -1,20 +0,0 @@
/*
* 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

@ -1,27 +0,0 @@
/*
* 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

@ -1,37 +0,0 @@
//
// Created by Gabriel Handford on 3/1/09.
// Copyright 2009-2013. All rights reserved.
// Created by John Boiles on 10/20/11.
// Copyright (c) 2011. All rights reserved
// Modified by Felix Schulze on 2/11/13.
// Copyright 2013. All rights reserved.
//
// 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 <UIKit/UIKit.h>
@interface UIImage (Compare)
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance;
@end

View File

@ -1,134 +0,0 @@
//
// Created by Gabriel Handford on 3/1/09.
// Copyright 2009-2013. All rights reserved.
// Created by John Boiles on 10/20/11.
// Copyright (c) 2011. All rights reserved
// Modified by Felix Schulze on 2/11/13.
// Copyright 2013. All rights reserved.
//
// 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 <FBSnapshotTestCase/UIImage+Compare.h>
// This makes debugging much more fun
typedef union {
uint32_t raw;
unsigned char bytes[4];
struct {
char red;
char green;
char blue;
char alpha;
} __attribute__ ((packed)) pixels;
} FBComparePixel;
@implementation UIImage (Compare)
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
{
NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size.");
CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage));
CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
// The images have the equal size, so we could use the smallest amount of bytes because of byte padding
size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage));
size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow;
void *referenceImagePixels = calloc(1, referenceImageSizeBytes);
void *imagePixels = calloc(1, referenceImageSizeBytes);
if (!referenceImagePixels || !imagePixels) {
free(referenceImagePixels);
free(imagePixels);
return NO;
}
CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels,
referenceImageSize.width,
referenceImageSize.height,
CGImageGetBitsPerComponent(self.CGImage),
minBytesPerRow,
CGImageGetColorSpace(self.CGImage),
(CGBitmapInfo)kCGImageAlphaPremultipliedLast
);
CGContextRef imageContext = CGBitmapContextCreate(imagePixels,
imageSize.width,
imageSize.height,
CGImageGetBitsPerComponent(image.CGImage),
minBytesPerRow,
CGImageGetColorSpace(image.CGImage),
(CGBitmapInfo)kCGImageAlphaPremultipliedLast
);
if (!referenceImageContext || !imageContext) {
CGContextRelease(referenceImageContext);
CGContextRelease(imageContext);
free(referenceImagePixels);
free(imagePixels);
return NO;
}
CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage);
CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage);
CGContextRelease(referenceImageContext);
CGContextRelease(imageContext);
BOOL imageEqual = YES;
// Do a fast compare if we can
if (tolerance == 0) {
imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0);
} else {
// Go through each pixel in turn and see if it is different
const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height;
FBComparePixel *p1 = referenceImagePixels;
FBComparePixel *p2 = imagePixels;
NSInteger numDiffPixels = 0;
for (int n = 0; n < pixelCount; ++n) {
// If this pixel is different, increment the pixel diff count and see
// if we have hit our limit.
if (p1->raw != p2->raw) {
numDiffPixels ++;
CGFloat percent = (CGFloat)numDiffPixels / pixelCount;
if (percent > tolerance) {
imageEqual = NO;
break;
}
}
p1++;
p2++;
}
}
free(referenceImagePixels);
free(imagePixels);
return imageEqual;
}
@end

View File

@ -1,37 +0,0 @@
//
// Created by Gabriel Handford on 3/1/09.
// Copyright 2009-2013. All rights reserved.
// Created by John Boiles on 10/20/11.
// Copyright (c) 2011. All rights reserved
// Modified by Felix Schulze on 2/11/13.
// Copyright 2013. All rights reserved.
//
// 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 <UIKit/UIKit.h>
@interface UIImage (Diff)
- (UIImage *)fb_diffWithImage:(UIImage *)image;
@end

View File

@ -1,56 +0,0 @@
//
// Created by Gabriel Handford on 3/1/09.
// Copyright 2009-2013. All rights reserved.
// Created by John Boiles on 10/20/11.
// Copyright (c) 2011. All rights reserved
// Modified by Felix Schulze on 2/11/13.
// Copyright 2013. All rights reserved.
//
// 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 <FBSnapshotTestCase/UIImage+Diff.h>
@implementation UIImage (Diff)
- (UIImage *)fb_diffWithImage:(UIImage *)image
{
if (!image) {
return nil;
}
CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height));
UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
CGContextSetAlpha(context, 0.5);
CGContextBeginTransparencyLayer(context, NULL);
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
CGContextSetBlendMode(context, kCGBlendModeDifference);
CGContextSetFillColorWithColor(context,[UIColor whiteColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height));
CGContextEndTransparencyLayer(context);
UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return returnImage;
}
@end

View File

@ -1,24 +0,0 @@
/*
* 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 UIImage (Snapshot)
/// Uses renderInContext: to get a snapshot of the layer.
+ (UIImage *)fb_imageForLayer:(CALayer *)layer;
/// Uses renderInContext: to get a snapshot of the view layer.
+ (UIImage *)fb_imageForViewLayer:(UIView *)view;
/// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed.
+ (UIImage *)fb_imageForView:(UIView *)view;
@end

View File

@ -1,73 +0,0 @@
/*
* 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/UIImage+Snapshot.h>
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
@implementation UIImage (Snapshot)
+ (UIImage *)fb_imageForLayer:(CALayer *)layer
{
CGRect bounds = layer.bounds;
NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer);
NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer);
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
NSAssert1(context, @"Could not generate context for layer %@", layer);
CGContextSaveGState(context);
[layer layoutIfNeeded];
[layer renderInContext:context];
CGContextRestoreGState(context);
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshot;
}
+ (UIImage *)fb_imageForViewLayer:(UIView *)view
{
[view layoutIfNeeded];
return [self fb_imageForLayer:view.layer];
}
+ (UIImage *)fb_imageForView:(UIView *)view
{
CGRect bounds = view.bounds;
NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view);
NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view);
// 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];
removeFromSuperview = YES;
}
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
[view layoutIfNeeded];
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (removeFromSuperview) {
[view removeFromSuperview];
}
return snapshot;
}
@end

View File

@ -1,180 +0,0 @@
/*
* 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/FBSnapshotTestCasePlatform.h>
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
/*
There are three ways of setting reference image directories.
1. Set the preprocessor macro FB_REFERENCE_IMAGE_DIR to a double quoted
c-string with the path.
2. Set an environment variable named FB_REFERENCE_IMAGE_DIR with the path. This
takes precedence over the preprocessor macro to allow for run-time override.
3. Keep everything unset, which will cause the reference images to be looked up
inside the bundle holding the current test, in the
Resources/ReferenceImages_* directories.
*/
#ifndef FB_REFERENCE_IMAGE_DIR
#define FB_REFERENCE_IMAGE_DIR ""
#endif
/**
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
@param view The view 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 of pixels that can differ and still count as an 'identical' view
*/
#define FBSnapshotVerifyViewWithOptions(view__, identifier__, suffixes__, tolerance__) \
FBSnapshotVerifyViewOrLayerWithOptions(View, view__, identifier__, suffixes__, tolerance__)
#define FBSnapshotVerifyView(view__, identifier__) \
FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
/**
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 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
*/
#define FBSnapshotVerifyLayerWithOptions(layer__, identifier__, suffixes__, tolerance__) \
FBSnapshotVerifyViewOrLayerWithOptions(Layer, layer__, identifier__, suffixes__, tolerance__)
#define FBSnapshotVerifyLayer(layer__, identifier__) \
FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
#define FBSnapshotVerifyViewOrLayerWithOptions(what__, viewOrLayer__, identifier__, suffixes__, tolerance__) \
{ \
NSString *errorDescription = [self snapshotVerifyViewOrLayer:viewOrLayer__ identifier:identifier__ suffixes:suffixes__ tolerance:tolerance__]; \
BOOL noErrors = (errorDescription == nil); \
XCTAssertTrue(noErrors, @"%@", errorDescription); \
}
/**
The base class of view snapshotting tests. If you have small UI component, it's often easier to configure it in a test
and compare an image of the view to a reference image that write lots of complex layout-code tests.
In order to flip the tests in your subclass to record the reference images set @c recordMode to @c YES.
@attention When recording, the reference image directory should be explicitly
set, otherwise the images may be written to somewhere inside the
simulator directory.
For example:
@code
- (void)setUp
{
[super setUp];
self.recordMode = YES;
}
@endcode
*/
@interface FBSnapshotTestCase : XCTestCase
/**
When YES, the test macros will save reference images, rather than performing an actual test.
*/
@property (readwrite, nonatomic, assign) BOOL recordMode;
/**
When @c YES appends the name of the device model and OS to the snapshot file name.
The default value is @c NO.
*/
@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
/**
When YES, renders a snapshot of the complete view hierarchy as visible onscreen.
There are several things that do not work if renderInContext: is used.
- UIVisualEffect #70
- UIAppearance #91
- Size Classes #92
@attention If the view does't belong to a UIWindow, it will create one and add the view as a subview.
*/
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
- (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
@param referenceImagesDirectory The directory in which reference images are stored.
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if the comparison (or saving of the reference image) succeeded.
*/
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
referenceImagesDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr;
/**
Performs the comparison or records a snapshot of the view if recordMode is YES.
@param view The view to snapshot
@param referenceImagesDirectory The directory in which reference images are stored.
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if the comparison (or saving of the reference image) succeeded.
*/
- (BOOL)compareSnapshotOfView:(UIView *)view
referenceImagesDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr;
/**
Checks if reference image with identifier based name exists in the reference images directory.
@param referenceImagesDirectory The directory in which reference images are stored.
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if reference image exists.
*/
- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
error:(NSError **)errorPtr;
/**
Returns the reference image directory.
Helper function used to implement the assert macros.
@param dir directory to use if environment variable not specified. Ignored if null or empty.
*/
- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir;
@end

View File

@ -1,192 +0,0 @@
/*
* 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/FBSnapshotTestCase.h>
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
@implementation FBSnapshotTestCase
{
FBSnapshotTestController *_snapshotController;
}
#pragma mark - Overrides
- (void)setUp
{
[super setUp];
_snapshotController = [[FBSnapshotTestController alloc] initWithTestName:NSStringFromClass([self class])];
}
- (void)tearDown
{
_snapshotController = nil;
[super tearDown];
}
- (BOOL)recordMode
{
return _snapshotController.recordMode;
}
- (void)setRecordMode:(BOOL)recordMode
{
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
_snapshotController.recordMode = recordMode;
}
- (BOOL)isDeviceAgnostic
{
return _snapshotController.deviceAgnostic;
}
- (void)setDeviceAgnostic:(BOOL)deviceAgnostic
{
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
_snapshotController.deviceAgnostic = deviceAgnostic;
}
- (BOOL)usesDrawViewHierarchyInRect
{
return _snapshotController.usesDrawViewHierarchyInRect;
}
- (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect
{
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
_snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect;
}
#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
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
return [self _compareSnapshotOfViewOrLayer:layer
referenceImagesDirectory:referenceImagesDirectory
identifier:identifier
tolerance:tolerance
error:errorPtr];
}
- (BOOL)compareSnapshotOfView:(UIView *)view
referenceImagesDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
return [self _compareSnapshotOfViewOrLayer:view
referenceImagesDirectory:referenceImagesDirectory
identifier:identifier
tolerance:tolerance
error:errorPtr];
}
- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
_snapshotController.referenceImagesDirectory = referenceImagesDirectory;
UIImage *referenceImage = [_snapshotController referenceImageForSelector:self.invocation.selector
identifier:identifier
error:errorPtr];
return (referenceImage != nil);
}
- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir
{
NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"];
if (envReferenceImageDirectory) {
return envReferenceImageDirectory;
}
if (dir && dir.length > 0) {
return dir;
}
return [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:@"ReferenceImages"];
}
#pragma mark - Private API
- (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer
referenceImagesDirectory:(NSString *)referenceImagesDirectory
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
_snapshotController.referenceImagesDirectory = referenceImagesDirectory;
return [_snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
selector:self.invocation.selector
identifier:identifier
tolerance:tolerance
error:errorPtr];
}
@end

View File

@ -1,44 +0,0 @@
/*
* 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 <Foundation/Foundation.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Returns a Boolean value that indicates whether the snapshot test is running in 64Bit.
This method is a convenience for creating the suffixes set based on the architecture
that the test is running.
@returns @c YES if the test is running in 64bit, otherwise @c NO.
*/
BOOL FBSnapshotTestCaseIs64Bit(void);
/**
Returns a default set of strings that is used to append a suffix based on the architectures.
@warning Do not modify this function, you can create your own and use it with @c FBSnapshotVerifyViewWithOptions()
@returns An @c NSOrderedSet object containing strings that are appended to the reference images directory.
*/
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void);
/**
Returns a fully «normalized» file name.
Strips punctuation and spaces and replaces them with @c _. Also appends the device model, running OS and screen size to the file name.
@returns An @c NSString object containing the passed @c fileName with the device model, OS and screen size appended at the end.
*/
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName);
#ifdef __cplusplus
}
#endif

View File

@ -1,51 +0,0 @@
/*
* 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/FBSnapshotTestCasePlatform.h>
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
#import <UIKit/UIKit.h>
BOOL FBSnapshotTestCaseIs64Bit(void)
{
#if __LP64__
return YES;
#else
return NO;
#endif
}
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void)
{
NSMutableOrderedSet *suffixesSet = [[NSMutableOrderedSet alloc] init];
[suffixesSet addObject:@"_32"];
[suffixesSet addObject:@"_64"];
if (FBSnapshotTestCaseIs64Bit()) {
return [suffixesSet reversedOrderedSet];
}
return [suffixesSet copy];
}
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName)
{
UIDevice *device = [UIDevice currentDevice];
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];
NSMutableCharacterSet *invalidCharacters = [NSMutableCharacterSet new];
[invalidCharacters formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
[invalidCharacters formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
NSArray *validComponents = [fileName componentsSeparatedByCharactersInSet:invalidCharacters];
fileName = [validComponents componentsJoinedByString:@"_"];
return fileName;
}

View File

@ -1,166 +0,0 @@
/*
* 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 <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) {
FBSnapshotTestControllerErrorCodeUnknown,
FBSnapshotTestControllerErrorCodeNeedsRecord,
FBSnapshotTestControllerErrorCodePNGCreationFailed,
FBSnapshotTestControllerErrorCodeImagesDifferentSizes,
FBSnapshotTestControllerErrorCodeImagesDifferent,
};
/**
Errors returned by the methods of FBSnapshotTestController use this domain.
*/
extern NSString *const FBSnapshotTestControllerErrorDomain;
/**
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
*/
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.
Instances are initialized with the test class, and directories to read and write to.
*/
@interface FBSnapshotTestController : NSObject
/**
Record snapshots.
*/
@property (readwrite, nonatomic, assign) BOOL recordMode;
/**
When @c YES appends the name of the device model and OS to the snapshot file name.
The default value is @c NO.
*/
@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
/**
Uses drawViewHierarchyInRect:afterScreenUpdates: to draw the image instead of renderInContext:
*/
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
/**
The directory in which referfence images are stored.
*/
@property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory;
/**
@param testClass The subclass of FBSnapshotTestCase that is using this controller.
@returns An instance of FBSnapshotTestController.
*/
- (instancetype)initWithTestClass:(Class)testClass;
/**
Designated initializer.
@param testName The name of the tests.
@returns An instance of FBSnapshotTestController.
*/
- (instancetype)initWithTestName:(NSString *)testName;
/**
Performs the comparison of the layer.
@param layer The Layer to snapshot.
@param selector The test method being run.
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if the comparison (or saving of the reference image) succeeded.
*/
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr;
/**
Performs the comparison of the view.
@param view The view to snapshot.
@param selector The test method being run.
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if the comparison (or saving of the reference image) succeeded.
*/
- (BOOL)compareSnapshotOfView:(UIView *)view
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr;
/**
Performs the comparison of a view or layer.
@param view The view or layer to snapshot.
@param selector The test method being run.
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
@param tolerance The percentage of pixels that can differ and still be considered 'identical'
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
@returns YES if the comparison (or saving of the reference image) succeeded.
*/
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
selector:(SEL)selector
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr;
/**
Loads a reference image.
@param selector The test method being run.
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
@param errorPtr An error, if this methods returns nil, the error will be something useful.
@returns An image.
*/
- (UIImage *)referenceImageForSelector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr;
/**
Performs a pixel-by-pixel comparison of the two images with an allowable margin of error.
@param referenceImage The reference (correct) image.
@param image The image to test against the reference.
@param tolerance The percentage of pixels that can differ and still be considered 'identical'
@param errorPtr An error that indicates why the comparison failed if it does.
@returns YES if the comparison succeeded and the images are the same(ish).
*/
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
toImage:(UIImage *)image
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr;
/**
Saves the reference image and the test image to `failedOutputDirectory`.
@param referenceImage The reference (correct) image.
@param testImage The image to test against the reference.
@param selector The test method being run.
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
@param errorPtr An error that indicates why the comparison failed if it does.
@returns YES if the save succeeded.
*/
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
testImage:(UIImage *)testImage
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr;
@end

View File

@ -1,358 +0,0 @@
/*
* 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/FBSnapshotTestController.h>
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
#import <FBSnapshotTestCase/UIImage+Compare.h>
#import <FBSnapshotTestCase/UIImage+Diff.h>
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
#import <UIKit/UIKit.h>
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,
FBTestSnapshotFileNameTypeFailedReference,
FBTestSnapshotFileNameTypeFailedTest,
FBTestSnapshotFileNameTypeFailedTestDiff,
};
@implementation FBSnapshotTestController
{
NSString *_testName;
NSFileManager *_fileManager;
}
#pragma mark - Initializers
- (instancetype)initWithTestClass:(Class)testClass;
{
return [self initWithTestName:NSStringFromClass(testClass)];
}
- (instancetype)initWithTestName:(NSString *)testName
{
if (self = [super init]) {
_testName = [testName copy];
_deviceAgnostic = NO;
_fileManager = [[NSFileManager alloc] init];
}
return self;
}
#pragma mark - Overrides
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory];
}
#pragma mark - Public API
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
return [self compareSnapshotOfViewOrLayer:layer
selector:selector
identifier:identifier
tolerance:0
error:errorPtr];
}
- (BOOL)compareSnapshotOfView:(UIView *)view
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
return [self compareSnapshotOfViewOrLayer:view
selector:selector
identifier:identifier
tolerance:0
error:errorPtr];
}
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
selector:(SEL)selector
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
if (self.recordMode) {
return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr];
} else {
return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier tolerance:tolerance error:errorPtr];
}
}
- (UIImage *)referenceImageForSelector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
if (nil == image && NULL != errorPtr) {
BOOL exists = [_fileManager fileExistsAtPath:filePath];
if (!exists) {
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
code:FBSnapshotTestControllerErrorCodeNeedsRecord
userInfo:@{
FBReferenceImageFilePathKey: filePath,
NSLocalizedDescriptionKey: @"Unable to load reference image.",
NSLocalizedFailureReasonErrorKey: @"Reference image not found. You need to run the test in record mode",
}];
} else {
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
code:FBSnapshotTestControllerErrorCodeUnknown
userInfo:nil];
}
}
return image;
}
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
toImage:(UIImage *)image
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
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:errorCode
userInfo:@{
NSLocalizedDescriptionKey: errorDescription,
NSLocalizedFailureReasonErrorKey: errorReason,
FBReferenceImageKey: referenceImage,
FBCapturedImageKey: image,
FBDiffedImageKey: [referenceImage fb_diffWithImage:image],
}];
}
return NO;
}
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
testImage:(UIImage *)testImage
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
NSData *referencePNGData = UIImagePNGRepresentation(referenceImage);
NSData *testPNGData = UIImagePNGRepresentation(testImage);
NSString *referencePath = [self _failedFilePathForSelector:selector
identifier:identifier
fileNameType:FBTestSnapshotFileNameTypeFailedReference];
NSError *creationError = nil;
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[referencePath stringByDeletingLastPathComponent]
withIntermediateDirectories:YES
attributes:nil
error:&creationError];
if (!didCreateDir) {
if (NULL != errorPtr) {
*errorPtr = creationError;
}
return NO;
}
if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) {
return NO;
}
NSString *testPath = [self _failedFilePathForSelector:selector
identifier:identifier
fileNameType:FBTestSnapshotFileNameTypeFailedTest];
if (![testPNGData writeToFile:testPath options:NSDataWritingAtomic error:errorPtr]) {
return NO;
}
NSString *diffPath = [self _failedFilePathForSelector:selector
identifier:identifier
fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff];
UIImage *diffImage = [referenceImage fb_diffWithImage:testImage];
NSData *diffImageData = UIImagePNGRepresentation(diffImage);
if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) {
return NO;
}
NSLog(@"If you have Kaleidoscope installed you can run this command to see an image diff:\n"
@"ksdiff \"%@\" \"%@\"", referencePath, testPath);
return YES;
}
#pragma mark - Private API
- (NSString *)_fileNameForSelector:(SEL)selector
identifier:(NSString *)identifier
fileNameType:(FBTestSnapshotFileNameType)fileNameType
{
NSString *fileName = nil;
switch (fileNameType) {
case FBTestSnapshotFileNameTypeFailedReference:
fileName = @"reference_";
break;
case FBTestSnapshotFileNameTypeFailedTest:
fileName = @"failed_";
break;
case FBTestSnapshotFileNameTypeFailedTestDiff:
fileName = @"diff_";
break;
default:
fileName = @"";
break;
}
fileName = [fileName stringByAppendingString:NSStringFromSelector(selector)];
if (0 < identifier.length) {
fileName = [fileName stringByAppendingFormat:@"_%@", identifier];
}
if (self.isDeviceAgnostic) {
fileName = FBDeviceAgnosticNormalizedFileName(fileName);
}
if ([[UIScreen mainScreen] scale] > 1) {
fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]];
}
fileName = [fileName stringByAppendingPathExtension:@"png"];
return fileName;
}
- (NSString *)_referenceFilePathForSelector:(SEL)selector
identifier:(NSString *)identifier
{
NSString *fileName = [self _fileNameForSelector:selector
identifier:identifier
fileNameType:FBTestSnapshotFileNameTypeReference];
NSString *filePath = [_referenceImagesDirectory stringByAppendingPathComponent:_testName];
filePath = [filePath stringByAppendingPathComponent:fileName];
return filePath;
}
- (NSString *)_failedFilePathForSelector:(SEL)selector
identifier:(NSString *)identifier
fileNameType:(FBTestSnapshotFileNameType)fileNameType
{
NSString *fileName = [self _fileNameForSelector:selector
identifier:identifier
fileNameType:fileNameType];
NSString *folderPath = NSTemporaryDirectory();
if (getenv("IMAGE_DIFF_DIR")) {
folderPath = @(getenv("IMAGE_DIFF_DIR"));
}
NSString *filePath = [folderPath stringByAppendingPathComponent:_testName];
filePath = [filePath stringByAppendingPathComponent:fileName];
return filePath;
}
- (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer
selector:(SEL)selector
identifier:(NSString *)identifier
tolerance:(CGFloat)tolerance
error:(NSError **)errorPtr
{
UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr];
if (nil != referenceImage) {
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr];
if (!imagesSame) {
NSError *saveError = nil;
if ([self saveFailedReferenceImage:referenceImage testImage:snapshot selector:selector identifier:identifier error:&saveError] == NO) {
NSLog(@"Error saving test images: %@", saveError);
}
}
return imagesSame;
}
return NO;
}
- (BOOL)_recordSnapshotOfViewOrLayer:(id)viewOrLayer
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
return [self _saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr];
}
- (BOOL)_saveReferenceImage:(UIImage *)image
selector:(SEL)selector
identifier:(NSString *)identifier
error:(NSError **)errorPtr
{
BOOL didWrite = NO;
if (nil != image) {
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
NSData *pngData = UIImagePNGRepresentation(image);
if (nil != pngData) {
NSError *creationError = nil;
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
withIntermediateDirectories:YES
attributes:nil
error:&creationError];
if (!didCreateDir) {
if (NULL != errorPtr) {
*errorPtr = creationError;
}
return NO;
}
didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr];
if (didWrite) {
NSLog(@"Reference image save at: %@", filePath);
}
} else {
if (nil != errorPtr) {
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
code:FBSnapshotTestControllerErrorCodePNGCreationFailed
userInfo:@{
FBReferenceImageFilePathKey: filePath,
}];
}
}
}
return didWrite;
}
- (UIImage *)_imageForViewOrLayer:(id)viewOrLayer
{
if ([viewOrLayer isKindOfClass:[UIView class]]) {
if (_usesDrawViewHierarchyInRect) {
return [UIImage fb_imageForView:viewOrLayer];
} else {
return [UIImage fb_imageForViewLayer:viewOrLayer];
}
} else if ([viewOrLayer isKindOfClass:[CALayer class]]) {
return [UIImage fb_imageForLayer:viewOrLayer];
} else {
[NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer];
}
return nil;
}
@end

View File

@ -1,125 +0,0 @@
/*
* 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.
*
*/
#if swift(>=3)
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.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.isKind(of: UIView.self) {
do {
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.isKind(of: CALayer.self) {
do {
try compareSnapshot(of: 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)
}
}
}
#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,29 +0,0 @@
BSD License
For the FBSnapshotTestCase software
Copyright (c) 2013, Facebook, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
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.

View File

@ -1,97 +0,0 @@
FBSnapshotTestCase
======================
[![Build Status](https://travis-ci.org/facebook/ios-snapshot-test-case.svg)](https://travis-ci.org/facebook/ios-snapshot-test-case) [![Cocoa Pod Version](https://cocoapod-badges.herokuapp.com/v/FBSnapshotTestCase/badge.svg)](http://cocoadocs.org/docsets/FBSnapshotTestCase/)
What it does
------------
A "snapshot test case" takes a configured `UIView` or `CALayer` and uses the
`renderInContext:` method to get an image snapshot of its contents. It
compares this snapshot to a "reference image" stored in your source code
repository and fails the test if the two images don't match.
Why?
----
At Facebook we write a lot of UI code. As you might imagine, each type of
feed story is rendered using a subclass of `UIView`. There are a lot of edge
cases that we want to handle correctly:
- What if there is more text than can fit in the space available?
- What if an image doesn't match the size of an image view?
- What should the highlighted state look like?
It's straightforward to test logic code, but less obvious how you should test
views. You can do a lot of rectangle asserts, but these are hard to understand
or visualize. Looking at an image diff shows you exactly what changed and how
it will look to users.
We developed `FBSnapshotTestCase` to make snapshot tests easy.
Installation with CocoaPods
---------------------------
1. Add the following lines to your Podfile:
```
target "Tests" do
pod 'FBSnapshotTestCase'
end
```
If you support iOS 7 use `FBSnapshotTestCase/Core` instead, which doesn't contain Swift support.
Replace "Tests" with the name of your test project.
2. There are [three ways](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCase/FBSnapshotTestCase.h#L19-L29) of setting reference image directories, the recommended one is to define `FB_REFERENCE_IMAGE_DIR` in your scheme. This should point to the directory where you want reference images to be stored. At Facebook, we normally use this:
|Name|Value|
|:---|:----|
|`FB_REFERENCE_IMAGE_DIR`|`$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages`|
![](FBSnapshotTestCaseDemo/Scheme_FB_REFERENCE_IMAGE_DIR.png)
Creating a snapshot test
------------------------
1. Subclass `FBSnapshotTestCase` instead of `XCTestCase`.
2. From within your test, use `FBSnapshotVerifyView`.
3. Run the test once with `self.recordMode = YES;` in the test's `-setUp`
method. (This creates the reference images on disk.)
4. Remove the line enabling record mode and run the test.
Features
--------
- Automatically names reference images on disk according to test class and
selector.
- Prints a descriptive error message to the console on failure. (Bonus:
failure message includes a one-line command to see an image diff if
you have [Kaleidoscope](http://www.kaleidoscopeapp.com) installed.)
- Supply an optional "identifier" if you want to perform multiple snapshots
in a single test method.
- Support for `CALayer` via `FBSnapshotVerifyLayer`.
- `usesDrawViewHierarchyInRect` to handle cases like `UIVisualEffect`, `UIAppearance` and Size Classes.
- `isDeviceAgnostic` to allow appending the device model (`iPhone`, `iPad`, `iPod Touch`, etc), OS version and screen size to the images (allowing to have multiple tests for the same «snapshot» for different `OS`s and devices).
Notes
-----
Your unit test must be an "application test", not a "logic test." (That is, it
must be run within the Simulator so that it has access to UIKit.) In Xcode 5
and later new projects only offer application tests, but older projects will
have separate targets for the two types.
Authors
-------
`FBSnapshotTestCase` was written at Facebook by
[Jonathan Dann](https://facebook.com/j.p.dann) with significant contributions by
[Todd Krabach](https://facebook.com/toddkrabach).
License
-------
`FBSnapshotTestCase` is BSD-licensed. See `LICENSE`.

View File

@ -1,21 +1,23 @@
{
"name": "PNObject",
"version": "0.9.0",
"version": "1.0.0",
"summary": "PNObject is a simple replica of the more complex ParseObject",
"homepage": "https://git.giuseppenucifora.com/giuseppenucifora/PNObject",
"license": "MIT",
"license": {
"type": "MIT",
"file": "LICENSE"
},
"authors": {
"Giuseppe Nucifora": "me@giuseppenucifora.com"
},
"source": {
"git": "https://git.giuseppenucifora.com/giuseppenucifora/PNObject.git",
"tag": "0.9.0"
"tag": "1.0.0"
},
"platforms": {
"ios": "8.0"
},
"requires_arc": true,
"source_files": "Pod/Classes/**/*",
"source_files": "PNObject/Classes/**/*",
"dependencies": {
"AFNetworking": [
@ -50,7 +52,10 @@
"FBSDKLoginKit": [
],
"NACrypto": [
"NAChloride": [
],
"DDDKeychainWrapper": [
],
"NSUserDefaults-AESEncryptor": [

View File

@ -22,40 +22,35 @@ PODS:
- Bolts/Tasks (1.8.4)
- CocoaSecurity (1.2.4)
- CodFis-Helper (0.1.3)
- DDDKeychainWrapper (1.0.0)
- DJLocalization (1.2.2):
- DJLocalization/Core (= 1.2.2)
- DJLocalization/Core (1.2.2)
- Expecta (1.0.5)
- Expecta+Snapshots (3.0.0):
- Expecta (~> 1.0)
- FBSnapshotTestCase/Core (~> 2.0)
- Specta (~> 1.0)
- FBSDKCoreKit (4.18.0):
- Bolts (~> 1.7)
- FBSDKLoginKit (4.18.0):
- FBSDKCoreKit
- FBSDKShareKit (4.18.0):
- FBSDKCoreKit
- FBSnapshotTestCase (2.1.4):
- FBSnapshotTestCase/SwiftSupport (= 2.1.4)
- FBSnapshotTestCase/Core (2.1.4)
- FBSnapshotTestCase/SwiftSupport (2.1.4):
- FBSnapshotTestCase/Core
- NACrypto (1.0.6)
- NSDate_Utils (0.1.3)
- libsodium (1.0.11)
- NAChloride (2.2.1):
- libsodium
- NSDate_Utils (1.0.0)
- NSString-Helper (1.0.5)
- NSUserDefaults-AESEncryptor (0.0.4):
- CocoaSecurity (~> 1.2.2)
- nv-ios-http-status (0.0.1)
- PEAR-FileManager-iOS (1.3.1)
- PNObject (0.9.0):
- PNObject (1.0.0):
- AFNetworking
- CodFis-Helper
- DDDKeychainWrapper
- DJLocalization
- FBSDKCoreKit
- FBSDKLoginKit
- FBSDKShareKit
- NACrypto
- NAChloride
- NSDate_Utils
- NSString-Helper
- NSUserDefaults-AESEncryptor
@ -71,23 +66,10 @@ PODS:
- UIDevice-Utils (0.1.6)
DEPENDENCIES:
- AFNetworking
- CodFis-Helper
- Expecta
- Expecta+Snapshots
- FBSDKCoreKit
- FBSDKLoginKit
- FBSDKShareKit
- FBSnapshotTestCase
- NSDate_Utils
- NSString-Helper
- nv-ios-http-status
- PEAR-FileManager-iOS
- PNObject (from `../`)
- PureLayout
- Specta
- StrongestPasswordValidator
- UIDevice-Utils
EXTERNAL SOURCES:
PNObject:
@ -98,26 +80,26 @@ SPEC CHECKSUMS:
Bolts: 8a7995239dbe724f9cba2248b766d48b7ebdd322
CocoaSecurity: d288a6f87e0f363823d2cb83e753814a6944f71a
CodFis-Helper: 28be4c74d7202542459d72354f59b1215871de87
DDDKeychainWrapper: e681a4daba6448786fa83b4941f58102a33b1897
DJLocalization: 0c84029af375647d4104a42ae36be87194c46c47
Expecta: e1c022fcd33910b6be89c291d2775b3fe27a89fe
Expecta+Snapshots: c343f410c7a6392f3e22e78f94c44b6c0749a516
FBSDKCoreKit: 15fef8804a4629f98c6f4e55e81a76c9d725d85e
FBSDKLoginKit: 6773073e970b2b15fb12e451ce7f11da0532b880
FBSDKShareKit: 0b8d6cc3f103c75297eb3c62caec284a2ccf1b9e
FBSnapshotTestCase: '094f9f314decbabe373b87cc339bea235a63e07a'
NACrypto: ce3900f1775f1b0cc27ce7c4953b94c598a74149
NSDate_Utils: 68669d2c81f310ee13026c791f4f0ed227b94c65
libsodium: 9aba161d2ee096977ecbdcce1ada69ffe511970c
NAChloride: 8f3d4f9a20df6b68840789a22a70aa4fcc437b0c
NSDate_Utils: 45d47afab329001ccafe056308d0cc05460e5298
NSString-Helper: 459e1b6a62b3bf7db10f01b0d102548608e945c4
NSUserDefaults-AESEncryptor: da02cfef056f1e18ebe2748767915f08b274c9c5
nv-ios-http-status: b6c2b5fc8656cc19e0d3000dadce2080b99d0e2f
PEAR-FileManager-iOS: 3bc403f68a53483f5629aa822f4649e40275c4d3
PNObject: f8d8116d0f72252552bddcbc3703d6d1e4719354
PNObject: 34839792d6fcc437b3123d0770b4d45e845bf2c9
PureLayout: 4d550abe49a94f24c2808b9b95db9131685fe4cd
RZDataBinding: 6981e90ddaae2f5e02028323b1043f8c31013109
Specta: ac94d110b865115fe60ff2c6d7281053c6f8e8a2
StrongestPasswordValidator: 921e42615bdf353513c6f925bffd4fc29865dbd7
UIDevice-Utils: 11c10b18d3c6489b45a97436e5ae6064a3622820
PODFILE CHECKSUM: 7c7096ec3fa75c9e7a1d2b538e5719592b9f5e60
PODFILE CHECKSUM: 57090c7ea88a91b49ca8dd73d9e7c6b866e772ba
COCOAPODS: 1.2.0.beta.1

20
Example/Pods/NAChloride/LICENSE generated Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 Gabriel Handford
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.

View File

@ -0,0 +1,19 @@
//
// NAAEAD.h
// NAChloride
//
// Created by Gabriel on 6/18/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NAAEAD : NSObject
@property (getter=isSecureDataEnabled) BOOL secureDataEnabled;
- (NSData *)encryptChaCha20Poly1305:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key additionalData:(NSData *)additionalData error:(NSError **)error;
- (NSData *)decryptChaCha20Poly1305:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key additionalData:(NSData *)additionalData error:(NSError **)error;
@end

View File

@ -0,0 +1,98 @@
//
// NAAEAD.m
// NAChloride
//
// Created by Gabriel on 6/18/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import "NAAEAD.h"
#import "NAInterface.h"
#import "NASecureData.h"
#import "sodium.h"
@implementation NAAEAD
+ (void)initialize { NAChlorideInit(); }
- (NSData *)encryptChaCha20Poly1305:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key additionalData:(NSData *)additionalData error:(NSError **)error {
if (!nonce || [nonce length] != NAAEADNonceSize) {
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
return nil;
}
if (!data) {
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
return nil;
}
if (!additionalData) {
if (error) *error = NAError(NAErrorCodeInvalidAdditionalData, @"Invalid additional data");
return nil;
}
if (!key || [key length] != NAAEADKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
return nil;
}
NSMutableData *outData = [NSMutableData dataWithLength:[data length] + NAAEADASize];
unsigned long long outLength;
int retval = crypto_aead_chacha20poly1305_encrypt([outData mutableBytes], &outLength,
[data bytes], [data length],
[additionalData bytes], [additionalData length],
NULL,
[nonce bytes],
[key bytes]);
if (retval != 0) {
if (error) *error = NAError(NAErrorCodeFailure, @"AEAD encrypt failed");
return nil;
}
return outData;
}
- (NSData *)decryptChaCha20Poly1305:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key additionalData:(NSData *)additionalData error:(NSError **)error {
if (!nonce || [nonce length] != NAAEADNonceSize) {
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
return nil;
}
if (!data) {
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
return nil;
}
if (!additionalData) {
if (error) *error = NAError(NAErrorCodeInvalidAdditionalData, @"Invalid additional data");
return nil;
}
if (!key || [key length] != NAAEADKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
return nil;
}
__block unsigned long long outLength;
__block int retval = -1;
NSMutableData *outData = NAData(self.secureDataEnabled, data.length, ^(void *bytes, NSUInteger length) {
retval = crypto_aead_chacha20poly1305_decrypt(bytes, &outLength,
NULL,
[data bytes], [data length],
[additionalData bytes], [additionalData length],
[nonce bytes],
[key bytes]);
});
if (retval != 0) {
if (error) *error = NAError(NAErrorCodeVerificationFailed, @"Verification failed");
return nil;
}
return [outData na_truncate:outData.length - (NSUInteger)outLength];
}
@end

View File

@ -0,0 +1,24 @@
//
// NAAuth.h
// NAChloride
//
// Created by Gabriel on 6/16/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
/*!
Computes an authentication tag for a message and a secret key, and provides a way to verify that a given tag is valid for a given message and a key.
*/
@interface NAAuth : NSObject
- (NSData *)auth:(NSData *)data key:(NSData *)key error:(NSError **)error;
/*!
Returns YES if verifies OK.
*/
- (BOOL)verify:(NSData *)auth data:(NSData *)data key:(NSData *)key error:(NSError **)error;
@end

View File

@ -0,0 +1,50 @@
//
// NAAuth.m
// NAChloride
//
// Created by Gabriel on 6/16/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import "NAAuth.h"
#import "NAInterface.h"
#import "sodium.h"
@implementation NAAuth
+ (void)initialize { NAChlorideInit(); }
- (NSData *)auth:(NSData *)data key:(NSData *)key error:(NSError **)error {
if (!key || [key length] != NAAuthKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
return nil;
}
NSMutableData *outData = [NSMutableData dataWithLength:NAAuthSize];
crypto_auth([outData mutableBytes], [data bytes], [data length], [key bytes]);
return outData;
}
- (BOOL)verify:(NSData *)auth data:(NSData *)data key:(NSData *)key error:(NSError **)error {
if (!key || [key length] != NAAuthKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
return NO;
}
if (!auth || [auth length] != NAAuthSize) {
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
return NO;
}
if (crypto_auth_verify([auth bytes], [data bytes], [data length], [key bytes]) != 0) {
if (error) *error = NAError(NAErrorCodeVerificationFailed, @"Verification failed");
return NO; // Message forged!
}
return YES;
}
@end

View File

@ -0,0 +1,21 @@
//
// NABox.h
// NAChloride
//
// Created by Gabriel on 6/18/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NABoxKeypair.h"
@interface NABox : NSObject
@property (getter=isSecureDataEnabled) BOOL secureDataEnabled;
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce keypair:(NABoxKeypair *)keypair error:(NSError **)error;
- (NSData *)decrypt:(NSData *)data nonce:(NSData *)nonce keypair:(NABoxKeypair *)keypair error:(NSError **)error;
@end

View File

@ -0,0 +1,78 @@
//
// NABox.m
// NAChloride
//
// Created by Gabriel on 6/18/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import "NABox.h"
#import "NAInterface.h"
#import "sodium.h"
@implementation NABox
+ (void)initialize { NAChlorideInit(); }
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce keypair:(NABoxKeypair *)keypair error:(NSError **)error {
if (!nonce || [nonce length] != NABoxNonceSize) {
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
return nil;
}
if (!data) {
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
return nil;
}
if (!keypair) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid keypair");
return nil;
}
NSMutableData *outData = [NSMutableData dataWithLength:[data length] + NABoxMACSize];
int retval = crypto_box_easy([outData mutableBytes],
[data bytes], [data length],
[nonce bytes],
[keypair.publicKey bytes],
[keypair.secretKey bytes]);
if (retval != 0) {
if (error) *error = NAError(NAErrorCodeFailure, @"Encrypt (box) failed");
return nil;
}
return outData;
}
- (NSData *)decrypt:(NSData *)data nonce:(NSData *)nonce keypair:(NABoxKeypair *)keypair error:(NSError **)error {
if (!nonce || [nonce length] != NABoxNonceSize) {
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
return nil;
}
if (!data) {
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
return nil;
}
__block int retval = -1;
NSMutableData *outData = NAData(self.secureDataEnabled, data.length, ^(void *bytes, NSUInteger length) {
retval = crypto_box_open_easy(bytes,
[data bytes], [data length],
[nonce bytes],
[keypair.publicKey bytes],
[keypair.secretKey bytes]);
});
if (retval != 0) {
if (error) *error = NAError(NAErrorCodeVerificationFailed, @"Verification failed");
return nil;
}
return [outData na_truncate:NABoxMACSize];
}
@end

View File

@ -0,0 +1,22 @@
//
// NABoxKeypair.h
// NAChloride
//
// Created by Gabriel on 6/18/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NASecureData.h"
@interface NABoxKeypair : NSObject
@property (readonly) NSData *publicKey;
@property (readonly) NASecureData *secretKey;
- (instancetype)initWithPublicKey:(NSData *)publicKey secretKey:(NASecureData *)secretKey error:(NSError **)error;
+ (instancetype)generate:(NSError **)error;
@end

View File

@ -0,0 +1,57 @@
//
// NABoxKeypair.m
// NAChloride
//
// Created by Gabriel on 6/18/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import "NABoxKeypair.h"
#import "NAInterface.h"
#import "NASecureData.h"
#import "sodium.h"
@interface NABoxKeypair ()
@property NSData *publicKey;
@property NASecureData *secretKey;
@end
@implementation NABoxKeypair
+ (void)initialize { NAChlorideInit(); }
- (instancetype)initWithPublicKey:(NSData *)publicKey secretKey:(NASecureData *)secretKey error:(NSError **)error {
if ((self = [super init])) {
if (!publicKey || [publicKey length] != NABoxPublicKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid public key");
return nil;
}
if (!secretKey || [secretKey length] != NABoxPublicKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid secret key");
return nil;
}
_publicKey = publicKey;
_secretKey = secretKey;
}
return self;
}
+ (instancetype)generate:(NSError **)error {
NSMutableData *publicKey = [NSMutableData dataWithLength:NABoxPublicKeySize];
__block int retval = -1;
NASecureData *secretKey = [NASecureData secureReadOnlyDataWithLength:NABoxSecretKeySize completion:^(void *bytes, NSUInteger length) {
retval = crypto_box_keypair([publicKey mutableBytes], bytes);
}];
if (retval != 0) {
if (error) *error = NAError(NAErrorCodeFailure, @"Keypair generate failed");
return nil;
}
return [[NABoxKeypair alloc] initWithPublicKey:publicKey secretKey:secretKey error:error];
}
@end

View File

@ -0,0 +1,30 @@
//
// NAChloride.h
// NAChloride
//
// Created by Gabriel Handford on 1/16/14.
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
//! Project version number for NAChloride.
FOUNDATION_EXPORT double NAChlorideVersionNumber;
//! Project version string for NAChloride.
FOUNDATION_EXPORT const unsigned char NAChlorideVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <NAChloride/PublicHeader.h>
#import <NAChloride/NAInterface.h>
#import <NAChloride/NASecretBox.h>
#import <NAChloride/NABox.h>
#import <NAChloride/NABoxKeypair.h>
#import <NAChloride/NAAuth.h>
#import <NAChloride/NAAEAD.h>
#import <NAChloride/NAOneTimeAuth.h>
#import <NAChloride/NAScrypt.h>
#import <NAChloride/NAStream.h>
#import <NAChloride/NARandom.h>
#import <NAChloride/NASecureData.h>

View File

@ -0,0 +1,64 @@
//
// NAInterface.h
// NAChloride
//
// Created by Gabriel on 6/25/14.
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM (NSInteger, NAErrorCode) {
NAErrorCodeFailure = 1, // Generic failure
NAErrorCodeInvalidNonce = 100,
NAErrorCodeInvalidKey = 101,
NAErrorCodeInvalidData = 102,
NAErrorCodeInvalidSalt = 103,
NAErrorCodeInvalidAdditionalData = 104, // For AEAD
NAErrorCodeVerificationFailed = 205, // Verification failed
};
extern const size_t NASecretBoxKeySize;
extern const size_t NASecretBoxNonceSize;
extern const size_t NASecretBoxMACSize;
extern const size_t NABoxPublicKeySize;
extern const size_t NABoxSecretKeySize;
extern const size_t NABoxNonceSize;
extern const size_t NABoxMACSize;
extern const size_t NAAuthKeySize;
extern const size_t NAAuthSize;
extern const size_t NAOneTimeAuthKeySize;
extern const size_t NAOneTimeAuthSize;
extern const size_t NAScryptSaltSize;
extern const size_t NAStreamKeySize;
extern const size_t NAStreamNonceSize;
extern const size_t NAXSalsaKeySize;
extern const size_t NAXSalsaNonceSize;
extern const size_t NAAEADKeySize;
extern const size_t NAAEADNonceSize;
extern const size_t NAAEADASize;
// Thread safe libsodium init
void NAChlorideInit(void);
// Don't call this directly (use NAChlorideInit). This is made accessible for testing.
int NASodiumInit(void);
typedef id (^NAWork)(NSError **error);
typedef void (^NACompletion)(NSError *error, id output);
void NADispatch(dispatch_queue_t queue, NAWork work, NACompletion completion);
#define NAError(CODE, DESC) [NSError errorWithDomain:@"NAChloride" code:CODE userInfo:@{NSLocalizedDescriptionKey: DESC}];
typedef void (^NADataCompletion)(void *bytes, NSUInteger length);

View File

@ -0,0 +1,60 @@
//
// NAInterface.m
// NACL
//
// Created by Gabriel Handford on 1/16/14.
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
//
#import "NAInterface.h"
#import "sodium.h"
const size_t NASecretBoxKeySize = crypto_secretbox_KEYBYTES;
const size_t NASecretBoxNonceSize = crypto_secretbox_NONCEBYTES;
const size_t NASecretBoxMACSize = crypto_secretbox_MACBYTES;
const size_t NABoxPublicKeySize = crypto_box_PUBLICKEYBYTES;
const size_t NABoxSecretKeySize = crypto_box_SECRETKEYBYTES;
const size_t NABoxNonceSize = crypto_box_NONCEBYTES;
const size_t NABoxMACSize = crypto_box_MACBYTES;
const size_t NAAuthKeySize = crypto_auth_KEYBYTES;
const size_t NAAuthSize = crypto_auth_BYTES;
const size_t NAOneTimeAuthKeySize = crypto_onetimeauth_KEYBYTES;
const size_t NAOneTimeAuthSize = crypto_onetimeauth_BYTES;
const size_t NAScryptSaltSize = crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
const size_t NAStreamKeySize = crypto_stream_KEYBYTES;
const size_t NAStreamNonceSize = crypto_stream_NONCEBYTES;
const size_t NAXSalsaKeySize = crypto_stream_xsalsa20_KEYBYTES;
const size_t NAXSalsaNonceSize = crypto_stream_xsalsa20_NONCEBYTES;
const size_t NAAEADKeySize = crypto_aead_chacha20poly1305_KEYBYTES;
const size_t NAAEADNonceSize = crypto_aead_chacha20poly1305_NPUBBYTES;
const size_t NAAEADASize = crypto_aead_chacha20poly1305_ABYTES;
void NAChlorideInit(void) {
static dispatch_once_t sodiumInit;
dispatch_once(&sodiumInit, ^{ NASodiumInit(); });
}
int NASodiumInit(void) {
return sodium_init();
}
void NADispatch(dispatch_queue_t queue, NAWork work, NACompletion completion) {
dispatch_async(queue, ^{
NSError *error = nil;
id output = work(&error);
dispatch_async(dispatch_get_main_queue(), ^{
completion(error, output);
});
});
}

View File

@ -0,0 +1,24 @@
//
// NAOneTimeAuth.h
// NAChloride
//
// Created by Gabriel on 9/24/14.
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
/*!
Generates a MAC for a given message and shared key using Poly1305 algorithm
(key may NOT be reused across messages).
*/
@interface NAOneTimeAuth : NSObject
- (NSData *)auth:(NSData *)data key:(NSData *)key error:(NSError **)error;
/*!
Returns YES if verifies OK.
*/
- (BOOL)verify:(NSData *)auth data:(NSData *)data key:(NSData *)key error:(NSError **)error;
@end

View File

@ -0,0 +1,49 @@
//
// NAOneTimeAuth.m
// NAChloride
//
// Created by Gabriel on 9/24/14.
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
//
#import "NAOneTimeAuth.h"
#import "NAInterface.h"
#import "sodium.h"
@implementation NAOneTimeAuth
+ (void)initialize { NAChlorideInit(); }
- (NSData *)auth:(NSData *)data key:(NSData *)key error:(NSError **)error {
if (!key || [key length] != NAOneTimeAuthKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
return nil;
}
NSMutableData *outData = [NSMutableData dataWithLength:NAOneTimeAuthSize];
crypto_onetimeauth([outData mutableBytes], [data bytes], [data length], [key bytes]);
return outData;
}
- (BOOL)verify:(NSData *)auth data:(NSData *)data key:(NSData *)key error:(NSError **)error {
if (!key || [key length] != NAOneTimeAuthKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
return NO;
}
if (!auth || [auth length] != NAOneTimeAuthSize) {
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
return NO;
}
if (crypto_onetimeauth_verify([auth bytes], [data bytes], [data length], [key bytes]) != 0) {
if (error) *error = NAError(NAErrorCodeVerificationFailed, @"Verification failed");
return NO; // Message forged!
}
return YES;
}
@end

View File

@ -0,0 +1,25 @@
//
// NARandom.h
// NAChloride
//
// Created by Gabriel on 6/16/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NASecureData.h"
@interface NARandom : NSObject
/*!
Random data of length bytes.
*/
+ (NSData *)randomData:(NSUInteger)length;
/*!
Random & secure data of length bytes.
*/
+ (NASecureData *)randomSecureReadOnlyData:(NSUInteger)length;
@end

View File

@ -0,0 +1,32 @@
//
// NARandom.m
// NAChloride
//
// Created by Gabriel on 6/16/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import "NARandom.h"
#import "NAInterface.h"
#import "sodium.h"
@implementation NARandom
+ (void)initialize { NAChlorideInit(); }
+ (NSData *)randomData:(NSUInteger)length {
NSMutableData *outData = [NSMutableData dataWithLength:length];
randombytes_buf([outData mutableBytes], length);
return outData;
}
+ (NASecureData *)randomSecureReadOnlyData:(NSUInteger)length {
NASecureData *secureData = [NASecureData secureReadOnlyDataWithLength:length completion:^(void *bytes, NSUInteger length) {
randombytes_buf(bytes, length);
}];
return secureData;
}
@end

View File

@ -0,0 +1,29 @@
//
// NAScrypt.h
// NAChloride
//
// Created by Gabriel on 6/19/14.
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NAScrypt : NSObject
/*!
Key derivation.
@param password Password
@param salt Must be NAScryptSaltSize
Default opslimit is crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE.
Default memlimit is crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE.
*/
+ (NSData *)scrypt:(NSData *)password salt:(NSData *)salt error:(NSError **)error;
/*!
Use the default scrypt. This is for advanced use only.
*/
+ (NSData *)scrypt:(NSData *)password salt:(NSData *)salt N:(uint64_t)N r:(uint32_t)r p:(uint32_t)p length:(size_t)length error:(NSError **)error;
@end

View File

@ -0,0 +1,51 @@
//
// NAScrypt.m
// NAChloride
//
// Created by Gabriel on 6/19/14.
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
//
#import "NAScrypt.h"
#import "NAInterface.h"
#import "sodium.h"
@implementation NAScrypt
+ (void)initialize { NAChlorideInit(); }
+ (NSData *)scrypt:(NSData *)password salt:(NSData *)salt error:(NSError **)error {
if (!salt || [salt length] != NAScryptSaltSize) {
if (error) *error = NAError(NAErrorCodeInvalidSalt, @"Invalid salt")
return nil;
}
NSMutableData *key = [NSMutableData dataWithLength:crypto_box_SEEDBYTES];
int retval = crypto_pwhash_scryptsalsa208sha256([key mutableBytes], key.length, password.bytes, password.length, salt.bytes, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE);
if (retval != 0) {
if (error) *error = NAError(NAErrorCodeFailure, @"Scrypt failed");
return nil;
}
return key;
}
+ (NSData *)scrypt:(NSData *)password salt:(NSData *)salt N:(uint64_t)N r:(uint32_t)r p:(uint32_t)p length:(size_t)length error:(NSError **)error {
NSMutableData *outData = [NSMutableData dataWithLength:length];
int retval = crypto_pwhash_scryptsalsa208sha256_ll((uint8_t *)password.bytes, password.length, (uint8_t *)salt.bytes, salt.length, N, r, p, [outData mutableBytes], outData.length);
if (retval != 0) {
if (error) *error = NAError(NAErrorCodeFailure, @"Scrypt failed");
return nil;
}
NSAssert([outData length] == length, @"Mismatched output length");
return outData;
}
@end

View File

@ -1,14 +1,19 @@
//
// NATwoFish.h
// NACrypto
// NASecretBox.h
// NACL
//
// Created by Gabriel on 6/20/14.
// Created by Gabriel Handford on 1/16/14.
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NATwoFish : NSObject
/*!
Encrypts and authenticates a message using a shared key and nonce.
*/
@interface NASecretBox : NSObject
@property (getter=isSecureDataEnabled) BOOL secureDataEnabled;
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error;

View File

@ -0,0 +1,84 @@
//
// NASecretBox.m
// NACL
//
// Created by Gabriel Handford on 1/16/14.
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
//
#import "NASecretBox.h"
#import "NAInterface.h"
#import "NASecureData.h"
#import "sodium.h"
@implementation NASecretBox
+ (void)initialize { NAChlorideInit(); }
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error {
if (!nonce || [nonce length] != NASecretBoxNonceSize) {
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
return nil;
}
if (!data) {
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
return nil;
}
if (!key || [key length] != NASecretBoxKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
return nil;
}
// Add space for authentication tag of size MACBYTES
NSMutableData *outData = [NSMutableData dataWithLength:[data length] + NASecretBoxMACSize];
int retval = crypto_secretbox_easy([outData mutableBytes],
[data bytes], [data length],
[nonce bytes],
[key bytes]);
if (retval != 0) {
if (error) *error = NAError(NAErrorCodeFailure, @"Encrypt (secret box) failed");
return nil;
}
return outData;
}
- (NSData *)decrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error {
if (!nonce || [nonce length] != NASecretBoxNonceSize) {
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
return nil;
}
if (!data) {
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
return nil;
}
if (!key || [key length] != NASecretBoxKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
return nil;
}
__block int retval = -1;
NSMutableData *outData = NAData(self.secureDataEnabled, data.length, ^(void *bytes, NSUInteger length) {
retval = crypto_secretbox_open_easy(bytes,
[data bytes], [data length],
[nonce bytes], [key bytes]);
});
if (retval != 0) {
if (error) *error = NAError(NAErrorCodeVerificationFailed, @"Verification failed");
return nil;
}
// Remove MAC bytes from data
return [outData na_truncate:NASecretBoxMACSize];
}
@end

View File

@ -0,0 +1,52 @@
//
// NASecureData.h
// NAChloride
//
// Created by Gabriel on 6/19/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NAInterface.h"
typedef NS_ENUM (NSInteger, NASecureDataProtection) {
NASecureDataProtectionReadWrite = 0, // Default no protection
NASecureDataProtectionReadOnly,
NASecureDataProtectionNoAccess,
};
/*!
Secure memory using libsodium.
*/
@interface NASecureData : NSMutableData // Subclassing for convienience
@property (nonatomic) NASecureDataProtection protection;
/*!
Secure and read only data.
*/
+ (instancetype)secureReadOnlyDataWithLength:(NSUInteger)length completion:(NADataCompletion)completion;
/*!
Secure data is has read/write protection in this block.
*/
- (void)readWrite:(void (^)(NASecureData *secureData))completion;
/*!
Truncate.
*/
- (NASecureData *)truncate:(NSUInteger)length;
@end
// Optional building of secure NSData
NSMutableData *NAData(BOOL secure, NSUInteger length, NADataCompletion completion);
@interface NSMutableData (NASecureData)
- (NSData *)na_truncate:(NSUInteger)length;
@end

View File

@ -0,0 +1,100 @@
//
// NASecureData.m
// NAChloride
//
// Created by Gabriel on 6/19/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import "NASecureData.h"
#import "NAInterface.h"
#import "sodium.h"
@interface NASecureData ()
@property void *secureBytes;
@property NSUInteger secureLength;
@end
@implementation NASecureData
+ (void)initialize { NAChlorideInit(); }
- (instancetype)initWithLength:(NSUInteger)length {
if ((self = [super init])) {
NAChlorideInit(); // It's already init'ed, but just to be safe
_secureLength = length;
_secureBytes = sodium_malloc(length);
}
return self;
}
+ (instancetype)secureReadOnlyDataWithLength:(NSUInteger)length completion:(NADataCompletion)completion {
NASecureData *secureData = [[NASecureData alloc] initWithLength:length];
completion(secureData.secureBytes, secureData.length);
secureData.protection = NASecureDataProtectionReadOnly;
return secureData;
}
- (void)dealloc {
sodium_free(_secureBytes);
}
- (void)setProtection:(NASecureDataProtection)protection {
switch (protection) {
// Keep these case statements order from most secure to least secure in case some jerk removes a break;
case NASecureDataProtectionReadWrite: sodium_mprotect_readwrite(_secureBytes); break;
case NASecureDataProtectionReadOnly: sodium_mprotect_readonly(_secureBytes); break;
case NASecureDataProtectionNoAccess: sodium_mprotect_noaccess(_secureBytes); break;
}
}
- (NSUInteger)length {
return _secureLength;
}
- (const void *)bytes {
return _secureBytes;
}
- (void *)mutableBytes {
return _secureBytes;
}
- (void)readWrite:(void (^)(NASecureData *secureData))completion {
NASecureDataProtection protection = self.protection;
self.protection = NASecureDataProtectionReadWrite;
completion(self);
self.protection = protection;
}
- (NASecureData *)truncate:(NSUInteger)length {
if (length == 0) return self;
return [NASecureData secureReadOnlyDataWithLength:(self.length - length) completion:^(void *bytes, NSUInteger length) {
memcpy(bytes, self.bytes, length);
}];
}
- (NSData *)na_truncate:(NSUInteger)length { return [self truncate:length]; }
@end
NSMutableData *NAData(BOOL secure, NSUInteger length, NADataCompletion completion) {
if (!secure) {
NSMutableData *data = [NSMutableData dataWithLength:length];
completion([data mutableBytes], length);
return data;
} else {
return [NASecureData secureReadOnlyDataWithLength:length completion:completion];
}
}
@implementation NSMutableData (NASecureData)
- (NSData *)na_truncate:(NSUInteger)length {
if (length == 0) return self;
return [NSData dataWithBytes:self.bytes length:self.length - length];
}
@end

View File

@ -0,0 +1,15 @@
//
// NAStream.h
// NAChloride
//
// Created by Gabriel on 6/18/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NAStream : NSObject
- (NSData *)xor:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error;
@end

View File

@ -0,0 +1,41 @@
//
// NAStream.m
// NAChloride
//
// Created by Gabriel on 6/18/15.
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
//
#import "NAStream.h"
#import "NAInterface.h"
#import "sodium.h"
@implementation NAStream
+ (void)initialize { NAChlorideInit(); }
- (NSData *)xor:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error {
if (!nonce || [nonce length] < NAStreamNonceSize) {
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid stream nonce");
return nil;
}
if (!key || [key length] != NAStreamKeySize) {
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid stream key");
return nil;
}
NSMutableData *outData = [NSMutableData dataWithLength:[data length]];
int retval = crypto_stream_xor([outData mutableBytes], [data bytes], [data length], [nonce bytes], [key bytes]);
if (retval != 0) {
if (error) *error = NAError(NAErrorCodeFailure, @"Stream failed");
return nil;
}
return outData;
}
@end

Some files were not shown because too many files have changed in this diff Show More