- Upgrade to 1.0
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 188 B |
|
Before Width: | Height: | Size: 353 B |
|
Before Width: | Height: | Size: 523 B |
|
Before Width: | Height: | Size: 523 B |
|
Before Width: | Height: | Size: 958 B |
|
Before Width: | Height: | Size: 317 B |
|
Before Width: | Height: | Size: 722 B |
|
Before Width: | Height: | Size: 341 B |
|
Before Width: | Height: | Size: 782 B |
|
Before Width: | Height: | Size: 899 B |
|
Before Width: | Height: | Size: 245 B |
|
Before Width: | Height: | Size: 458 B |
|
Before Width: | Height: | Size: 170 B |
|
Before Width: | Height: | Size: 263 B |
|
Before Width: | Height: | Size: 387 B |
|
Before Width: | Height: | Size: 261 B |
|
Before Width: | Height: | Size: 505 B |
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 58 KiB |
27
Example/PNObject/Main.storyboard
Normal 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>
|
||||
15
Example/PNObject/PNObjAppDelegate.h
Normal 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
|
||||
@ -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 {
|
||||
13
Example/PNObject/PNObjViewController.h
Normal 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
|
||||
@ -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];
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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]));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
46
Example/Pods/DDDKeychainWrapper/Pod/Classes/DDDKeychainWrapper.h
generated
Normal 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
|
||||
373
Example/Pods/DDDKeychainWrapper/Pod/Classes/DDDKeychainWrapper.m
generated
Normal 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
|
||||
47
Example/Pods/DDDKeychainWrapper/README.md
generated
Normal file
@ -0,0 +1,47 @@
|
||||
# DDDKeychainWrapper
|
||||
|
||||
[](https://travis-ci.org/axldyb/DDDKeychainWrapper)
|
||||
[](http://cocoadocs.org/docsets/DDDKeychainWrapper)
|
||||
[](http://cocoadocs.org/docsets/DDDKeychainWrapper)
|
||||
[](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.
|
||||
|
||||
@ -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));
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
22
Example/Pods/Expecta+Snapshots/LICENSE.md
generated
@ -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.
|
||||
87
Example/Pods/Expecta+Snapshots/README.md
generated
@ -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).
|
||||
|
||||
[](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)
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
29
Example/Pods/FBSnapshotTestCase/LICENSE
generated
@ -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.
|
||||
97
Example/Pods/FBSnapshotTestCase/README.md
generated
@ -1,97 +0,0 @@
|
||||
FBSnapshotTestCase
|
||||
======================
|
||||
|
||||
[](https://travis-ci.org/facebook/ios-snapshot-test-case) [](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`|
|
||||
|
||||
|
||||

|
||||
|
||||
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`.
|
||||
17
Example/Pods/Local Podspecs/PNObject.podspec.json
generated
@ -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": [
|
||||
|
||||
46
Example/Pods/Manifest.lock
generated
@ -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
@ -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.
|
||||
19
Example/Pods/NAChloride/NAChloride/NAAEAD.h
generated
Normal 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
|
||||
98
Example/Pods/NAChloride/NAChloride/NAAEAD.m
generated
Normal 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
|
||||
24
Example/Pods/NAChloride/NAChloride/NAAuth.h
generated
Normal 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
|
||||
50
Example/Pods/NAChloride/NAChloride/NAAuth.m
generated
Normal 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
|
||||
|
||||
21
Example/Pods/NAChloride/NAChloride/NABox.h
generated
Normal 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
|
||||
78
Example/Pods/NAChloride/NAChloride/NABox.m
generated
Normal 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
|
||||
22
Example/Pods/NAChloride/NAChloride/NABoxKeypair.h
generated
Normal 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
|
||||
57
Example/Pods/NAChloride/NAChloride/NABoxKeypair.m
generated
Normal 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
|
||||
30
Example/Pods/NAChloride/NAChloride/NAChloride.h
generated
Normal 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>
|
||||
64
Example/Pods/NAChloride/NAChloride/NAInterface.h
generated
Normal 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);
|
||||
60
Example/Pods/NAChloride/NAChloride/NAInterface.m
generated
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
24
Example/Pods/NAChloride/NAChloride/NAOneTimeAuth.h
generated
Normal 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
|
||||
49
Example/Pods/NAChloride/NAChloride/NAOneTimeAuth.m
generated
Normal 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
|
||||
25
Example/Pods/NAChloride/NAChloride/NARandom.h
generated
Normal 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
|
||||
32
Example/Pods/NAChloride/NAChloride/NARandom.m
generated
Normal 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
|
||||
29
Example/Pods/NAChloride/NAChloride/NAScrypt.h
generated
Normal 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
|
||||
51
Example/Pods/NAChloride/NAChloride/NAScrypt.m
generated
Normal 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
|
||||
@ -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;
|
||||
|
||||
84
Example/Pods/NAChloride/NAChloride/NASecretBox.m
generated
Normal 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
|
||||
52
Example/Pods/NAChloride/NAChloride/NASecureData.h
generated
Normal 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
|
||||
100
Example/Pods/NAChloride/NAChloride/NASecureData.m
generated
Normal 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
|
||||
15
Example/Pods/NAChloride/NAChloride/NAStream.h
generated
Normal 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
|
||||
41
Example/Pods/NAChloride/NAChloride/NAStream.m
generated
Normal 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
|
||||