diff --git a/.travis.yml b/.travis.yml
index 4e0b64b..1592c4e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,5 @@
 language: objective-c
 osx_image: xcode7.2
-script: xctool -project Advance.xcodeproj -scheme 'Advance' -configuration Debug -sdk iphonesimulator -arch i386 test
+script:
+	xctool -project Advance.xcodeproj -scheme 'Advance-iOS' -configuration Debug -sdk iphonesimulator -arch i386 test
+	xctool -project Advance.xcodeproj -scheme 'Advance-OSX' -configuration Debug test
diff --git a/Advance-OSX/Info.plist b/Advance-OSX/Info.plist
new file mode 100644
index 0000000..0571631
--- /dev/null
+++ b/Advance-OSX/Info.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>0.9</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2016 Storehouse Media Inc. All rights reserved.</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/Advance-iOS/Info.plist b/Advance-iOS/Info.plist
new file mode 100644
index 0000000..0571631
--- /dev/null
+++ b/Advance-iOS/Info.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>0.9</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2016 Storehouse Media Inc. All rights reserved.</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/Advance.xcodeproj/project.pbxproj b/Advance.xcodeproj/project.pbxproj
index d4e2143..e7d5cc6 100644
--- a/Advance.xcodeproj/project.pbxproj
+++ b/Advance.xcodeproj/project.pbxproj
@@ -30,7 +30,50 @@
 		22F74AB81C76769C004C6E4C /* GesturesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22F74AB71C76769C004C6E4C /* GesturesViewController.swift */; };
 		22F74ABB1C767757004C6E4C /* GestureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22F74ABA1C767757004C6E4C /* GestureView.swift */; };
 		22F74ABC1C76778E004C6E4C /* Advance.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D39CEE11C6E983100DFCF86 /* Advance.framework */; };
+		5D28A2D81C93757200E5A1E9 /* SpringView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D28A2D71C93757200E5A1E9 /* SpringView.swift */; };
+		5D28A2DA1C9378F000E5A1E9 /* SpringContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D28A2D91C9378F000E5A1E9 /* SpringContainerView.swift */; };
 		5D39CEEC1C6E983100DFCF86 /* Advance.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D39CEE11C6E983100DFCF86 /* Advance.framework */; };
+		5D3A2F391C920C710071F08F /* Animatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4631C876AD1000CF9EE /* Animatable.swift */; };
+		5D3A2F3A1C920C7C0071F08F /* BasicAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4691C876AD1000CF9EE /* BasicAnimation.swift */; };
+		5D3A2F3B1C920C7C0071F08F /* TimingFunctionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A46A1C876AD1000CF9EE /* TimingFunctionType.swift */; };
+		5D3A2F3C1C920C7C0071F08F /* UnitBezier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A46B1C876AD1000CF9EE /* UnitBezier.swift */; };
+		5D3A2F3D1C920C810071F08F /* DecayAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A46D1C876AD1000CF9EE /* DecayAnimation.swift */; };
+		5D3A2F3E1C920C860071F08F /* SpringAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A46F1C876AD1000CF9EE /* SpringAnimation.swift */; };
+		5D3A2F3F1C920C8C0071F08F /* AnimationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4661C876AD1000CF9EE /* AnimationType.swift */; };
+		5D3A2F401C920C8C0071F08F /* AnyValueAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4671C876AD1000CF9EE /* AnyValueAnimation.swift */; };
+		5D3A2F411C920C8C0071F08F /* ValueAnimationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4701C876AD1000CF9EE /* ValueAnimationType.swift */; };
+		5D3A2F421C920C8C0071F08F /* Advanceable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4651C876AD1000CF9EE /* Advanceable.swift */; };
+		5D3A2F431C920C920071F08F /* Animator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4721C876AD1000CF9EE /* Animator.swift */; };
+		5D3A2F441C920C920071F08F /* AnimatorContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4731C876AD1000CF9EE /* AnimatorContext.swift */; };
+		5D3A2F451C920C970071F08F /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4751C876AD1000CF9EE /* Event.swift */; };
+		5D3A2F461C920C9C0071F08F /* CAMediaTimingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4771C876AD1000CF9EE /* CAMediaTimingFunction.swift */; };
+		5D3A2F471C920CB10071F08F /* Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A47A1C876AD1000CF9EE /* Loop.swift */; };
+		5D3A2F481C920CBC0071F08F /* DecayFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A47C1C876AD1000CF9EE /* DecayFunction.swift */; };
+		5D3A2F491C920CBC0071F08F /* DynamicFunctionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A47D1C876AD1000CF9EE /* DynamicFunctionType.swift */; };
+		5D3A2F4A1C920CBC0071F08F /* DynamicSolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A47E1C876AD1000CF9EE /* DynamicSolver.swift */; };
+		5D3A2F4B1C920CBC0071F08F /* Spring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A47F1C876AD1000CF9EE /* Spring.swift */; };
+		5D3A2F4C1C920CBC0071F08F /* SpringFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4801C876AD1000CF9EE /* SpringFunction.swift */; };
+		5D3A2F4D1C920CC20071F08F /* VectorConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4881C876AD1000CF9EE /* VectorConvertible.swift */; };
+		5D3A2F4E1C920CC20071F08F /* Double+VectorConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4871C876AD1000CF9EE /* Double+VectorConvertible.swift */; };
+		5D3A2F4F1C920CC20071F08F /* CGFloat+VectorConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4821C876AD1000CF9EE /* CGFloat+VectorConvertible.swift */; };
+		5D3A2F501C920CC20071F08F /* CGPoint+VectorConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4831C876AD1000CF9EE /* CGPoint+VectorConvertible.swift */; };
+		5D3A2F511C920CC20071F08F /* CGRect+VectorConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4841C876AD1000CF9EE /* CGRect+VectorConvertible.swift */; };
+		5D3A2F521C920CC20071F08F /* CGSize+VectorConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4851C876AD1000CF9EE /* CGSize+VectorConvertible.swift */; };
+		5D3A2F531C920CC20071F08F /* CGVector+VectorConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4861C876AD1000CF9EE /* CGVector+VectorConvertible.swift */; };
+		5D3A2F541C920CCA0071F08F /* Vector1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A48B1C876AD1000CF9EE /* Vector1.swift */; };
+		5D3A2F551C920CCA0071F08F /* Vector2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A48C1C876AD1000CF9EE /* Vector2.swift */; };
+		5D3A2F561C920CCA0071F08F /* Vector3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A48D1C876AD1000CF9EE /* Vector3.swift */; };
+		5D3A2F571C920CCA0071F08F /* Vector4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A48E1C876AD1000CF9EE /* Vector4.swift */; };
+		5D3A2F581C920CCA0071F08F /* VectorMathCapable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A48F1C876AD1000CF9EE /* VectorMathCapable.swift */; };
+		5D3A2F591C920CCA0071F08F /* VectorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4901C876AD1000CF9EE /* VectorType.swift */; };
+		5D3A2F5A1C920CCA0071F08F /* Interpolatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A48A1C876AD1000CF9EE /* Interpolatable.swift */; };
+		5D3A2F5F1C922DAA0071F08F /* DisplayLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D3A2F5E1C922DAA0071F08F /* DisplayLink.swift */; };
+		5D3A2F631C923ADB0071F08F /* DisplayLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D3A2F5E1C922DAA0071F08F /* DisplayLink.swift */; };
+		5D3A2F6B1C92559B0071F08F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D3A2F6A1C92559B0071F08F /* AppDelegate.swift */; };
+		5D3A2F6D1C92559B0071F08F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5D3A2F6C1C92559B0071F08F /* Assets.xcassets */; };
+		5D3A2F701C92559B0071F08F /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D3A2F6E1C92559B0071F08F /* MainMenu.xib */; };
+		5D3A2F751C9255B30071F08F /* Advance.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D3A2F311C9209EE0071F08F /* Advance.framework */; };
+		5D3A2F761C9255B30071F08F /* Advance.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5D3A2F311C9209EE0071F08F /* Advance.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		5DA94F481C80DAA00039B9BB /* VectorConvenienceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA94F471C80DAA00039B9BB /* VectorConvenienceTests.swift */; };
 		5DA94F4B1C80E2700039B9BB /* UnitBezierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA94F4A1C80E2700039B9BB /* UnitBezierTests.swift */; };
 		5DA94F4D1C8112810039B9BB /* CAMediaTimingFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA94F4C1C8112810039B9BB /* CAMediaTimingFunctionTests.swift */; };
@@ -69,6 +112,15 @@
 		5DB1A4B11C876AD1000CF9EE /* Vector4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A48E1C876AD1000CF9EE /* Vector4.swift */; };
 		5DB1A4B21C876AD1000CF9EE /* VectorMathCapable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A48F1C876AD1000CF9EE /* VectorMathCapable.swift */; };
 		5DB1A4B31C876AD1000CF9EE /* VectorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1A4901C876AD1000CF9EE /* VectorType.swift */; };
+		5DE959E91C9388B9008487A8 /* DisplayLinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DE959E81C9388B9008487A8 /* DisplayLinkTests.swift */; };
+		5DE959F51C9392E1008487A8 /* Advance.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D3A2F311C9209EE0071F08F /* Advance.framework */; };
+		5DE959FC1C93940E008487A8 /* EventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA94F4E1C8114680039B9BB /* EventTests.swift */; };
+		5DE959FD1C93940E008487A8 /* VectorTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22168B5A1C7A547300BAE519 /* VectorTypeTests.swift */; };
+		5DE959FE1C93940E008487A8 /* VectorConvenienceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA94F471C80DAA00039B9BB /* VectorConvenienceTests.swift */; };
+		5DE959FF1C93940E008487A8 /* BasicAnimationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22168B5C1C7A5E3500BAE519 /* BasicAnimationTests.swift */; };
+		5DE95A001C93940E008487A8 /* UnitBezierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA94F4A1C80E2700039B9BB /* UnitBezierTests.swift */; };
+		5DE95A011C93940E008487A8 /* CAMediaTimingFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA94F4C1C8112810039B9BB /* CAMediaTimingFunctionTests.swift */; };
+		5DE95A021C93940E008487A8 /* DisplayLinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DE959E81C9388B9008487A8 /* DisplayLinkTests.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -86,6 +138,34 @@
 			remoteGlobalIDString = 5D39CEE01C6E983100DFCF86;
 			remoteInfo = Advance;
 		};
+		5D3A2F771C9255B30071F08F /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 5D39CED81C6E983100DFCF86 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 5D3A2F301C9209EE0071F08F;
+			remoteInfo = "Advance-OSX";
+		};
+		5DE959EA1C938C54008487A8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 5D39CED81C6E983100DFCF86 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 22F74AA41C7674BB004C6E4C;
+			remoteInfo = AdvanceSample;
+		};
+		5DE959F61C9392E1008487A8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 5D39CED81C6E983100DFCF86 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 5D3A2F301C9209EE0071F08F;
+			remoteInfo = "Advance-OSX";
+		};
+		5DE95A031C939427008487A8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 5D39CED81C6E983100DFCF86 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 5D3A2F671C92559B0071F08F;
+			remoteInfo = "AdvanceSample-OSX";
+		};
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -99,6 +179,17 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		5D3A2F791C9255B30071F08F /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				5D3A2F761C9255B30071F08F /* Advance.framework in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
@@ -125,10 +216,19 @@
 		22F74AB31C7674BB004C6E4C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		22F74AB71C76769C004C6E4C /* GesturesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GesturesViewController.swift; sourceTree = "<group>"; };
 		22F74ABA1C767757004C6E4C /* GestureView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GestureView.swift; sourceTree = "<group>"; };
+		5D28A2D71C93757200E5A1E9 /* SpringView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringView.swift; sourceTree = "<group>"; };
+		5D28A2D91C9378F000E5A1E9 /* SpringContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpringContainerView.swift; sourceTree = "<group>"; };
 		5D39CEE11C6E983100DFCF86 /* Advance.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Advance.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		5D39CEEB1C6E983100DFCF86 /* Advance.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Advance.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		5D39CEF21C6E983100DFCF86 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		5D39CF101C6E9B1F00DFCF86 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		5D3A2F311C9209EE0071F08F /* Advance.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Advance.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		5D3A2F351C9209EE0071F08F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		5D3A2F5E1C922DAA0071F08F /* DisplayLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayLink.swift; sourceTree = "<group>"; };
+		5D3A2F681C92559B0071F08F /* AdvanceSample-OSX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AdvanceSample-OSX.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		5D3A2F6A1C92559B0071F08F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		5D3A2F6C1C92559B0071F08F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		5D3A2F6F1C92559B0071F08F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
+		5D3A2F711C92559B0071F08F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		5DA94F471C80DAA00039B9BB /* VectorConvenienceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VectorConvenienceTests.swift; sourceTree = "<group>"; };
 		5DA94F491C80DEEB0039B9BB /* Playground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Playground.playground; sourceTree = "<group>"; };
 		5DA94F4A1C80E2700039B9BB /* UnitBezierTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnitBezierTests.swift; sourceTree = "<group>"; };
@@ -168,6 +268,10 @@
 		5DB1A48E1C876AD1000CF9EE /* Vector4.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vector4.swift; sourceTree = "<group>"; };
 		5DB1A48F1C876AD1000CF9EE /* VectorMathCapable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VectorMathCapable.swift; sourceTree = "<group>"; };
 		5DB1A4901C876AD1000CF9EE /* VectorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VectorType.swift; sourceTree = "<group>"; };
+		5DE959E81C9388B9008487A8 /* DisplayLinkTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayLinkTests.swift; sourceTree = "<group>"; };
+		5DE959F01C9392E1008487A8 /* AdvanceTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "AdvanceTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		5DE959F41C9392E1008487A8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		5DE95A061C93963A008487A8 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -194,6 +298,29 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		5D3A2F2D1C9209EE0071F08F /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5D3A2F651C92559B0071F08F /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5D3A2F751C9255B30071F08F /* Advance.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5DE959ED1C9392E1008487A8 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5DE959F51C9392E1008487A8 /* Advance.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -226,7 +353,7 @@
 			name = Springs;
 			sourceTree = "<group>";
 		};
-		22F74AA61C7674BB004C6E4C /* AdvanceSample */ = {
+		22F74AA61C7674BB004C6E4C /* AdvanceSample-iOS */ = {
 			isa = PBXGroup;
 			children = (
 				22142E681C810DAC000C6DEB /* Gravity */,
@@ -242,7 +369,7 @@
 				22F74AB01C7674BB004C6E4C /* LaunchScreen.storyboard */,
 				22F74AB31C7674BB004C6E4C /* Info.plist */,
 			);
-			path = AdvanceSample;
+			path = "AdvanceSample-iOS";
 			sourceTree = "<group>";
 		};
 		22F74AB91C767740004C6E4C /* Gestures */ = {
@@ -262,8 +389,10 @@
 				5DA94F491C80DEEB0039B9BB /* Playground.playground */,
 				5DB1A4611C876AD1000CF9EE /* Advance */,
 				5D39CF281C6E9B2E00DFCF86 /* Resources */,
-				5D39CEEF1C6E983100DFCF86 /* AdvanceTests */,
-				22F74AA61C7674BB004C6E4C /* AdvanceSample */,
+				5D39CEEF1C6E983100DFCF86 /* AdvanceTests-iOS */,
+				5DE959F11C9392E1008487A8 /* AdvanceTests-OSX */,
+				22F74AA61C7674BB004C6E4C /* AdvanceSample-iOS */,
+				5D3A2F691C92559B0071F08F /* AdvanceSample-OSX */,
 				5D39CEE21C6E983100DFCF86 /* Products */,
 			);
 			sourceTree = "<group>";
@@ -274,11 +403,14 @@
 				5D39CEE11C6E983100DFCF86 /* Advance.framework */,
 				5D39CEEB1C6E983100DFCF86 /* Advance.xctest */,
 				22F74AA51C7674BB004C6E4C /* AdvanceSample.app */,
+				5D3A2F311C9209EE0071F08F /* Advance.framework */,
+				5D3A2F681C92559B0071F08F /* AdvanceSample-OSX.app */,
+				5DE959F01C9392E1008487A8 /* AdvanceTests-OSX.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
 		};
-		5D39CEEF1C6E983100DFCF86 /* AdvanceTests */ = {
+		5D39CEEF1C6E983100DFCF86 /* AdvanceTests-iOS */ = {
 			isa = PBXGroup;
 			children = (
 				5DA94F4E1C8114680039B9BB /* EventTests.swift */,
@@ -287,20 +419,43 @@
 				22168B5C1C7A5E3500BAE519 /* BasicAnimationTests.swift */,
 				5DA94F4A1C80E2700039B9BB /* UnitBezierTests.swift */,
 				5DA94F4C1C8112810039B9BB /* CAMediaTimingFunctionTests.swift */,
+				5DE959E81C9388B9008487A8 /* DisplayLinkTests.swift */,
 				5D39CEF21C6E983100DFCF86 /* Info.plist */,
 			);
-			path = AdvanceTests;
+			path = "AdvanceTests-iOS";
 			sourceTree = "<group>";
 		};
 		5D39CF281C6E9B2E00DFCF86 /* Resources */ = {
 			isa = PBXGroup;
 			children = (
-				5D39CF101C6E9B1F00DFCF86 /* Info.plist */,
+				5DE95A051C93963A008487A8 /* Advance-iOS */,
+				5D3A2F321C9209EE0071F08F /* Advance-OSX */,
 			);
 			name = Resources;
 			path = Advance;
 			sourceTree = "<group>";
 		};
+		5D3A2F321C9209EE0071F08F /* Advance-OSX */ = {
+			isa = PBXGroup;
+			children = (
+				5D3A2F351C9209EE0071F08F /* Info.plist */,
+			);
+			path = "Advance-OSX";
+			sourceTree = SOURCE_ROOT;
+		};
+		5D3A2F691C92559B0071F08F /* AdvanceSample-OSX */ = {
+			isa = PBXGroup;
+			children = (
+				5D3A2F6A1C92559B0071F08F /* AppDelegate.swift */,
+				5D28A2D91C9378F000E5A1E9 /* SpringContainerView.swift */,
+				5D28A2D71C93757200E5A1E9 /* SpringView.swift */,
+				5D3A2F6C1C92559B0071F08F /* Assets.xcassets */,
+				5D3A2F6E1C92559B0071F08F /* MainMenu.xib */,
+				5D3A2F711C92559B0071F08F /* Info.plist */,
+			);
+			path = "AdvanceSample-OSX";
+			sourceTree = "<group>";
+		};
 		5DB1A4611C876AD1000CF9EE /* Advance */ = {
 			isa = PBXGroup;
 			children = (
@@ -394,6 +549,7 @@
 			isa = PBXGroup;
 			children = (
 				5DB1A47A1C876AD1000CF9EE /* Loop.swift */,
+				5D3A2F5E1C922DAA0071F08F /* DisplayLink.swift */,
 			);
 			path = Loop;
 			sourceTree = "<group>";
@@ -439,6 +595,22 @@
 			path = Vectors;
 			sourceTree = "<group>";
 		};
+		5DE959F11C9392E1008487A8 /* AdvanceTests-OSX */ = {
+			isa = PBXGroup;
+			children = (
+				5DE959F41C9392E1008487A8 /* Info.plist */,
+			);
+			path = "AdvanceTests-OSX";
+			sourceTree = SOURCE_ROOT;
+		};
+		5DE95A051C93963A008487A8 /* Advance-iOS */ = {
+			isa = PBXGroup;
+			children = (
+				5DE95A061C93963A008487A8 /* Info.plist */,
+			);
+			path = "Advance-iOS";
+			sourceTree = SOURCE_ROOT;
+		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -449,12 +621,19 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		5D3A2F2E1C9209EE0071F08F /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXHeadersBuildPhase section */
 
 /* Begin PBXNativeTarget section */
-		22F74AA41C7674BB004C6E4C /* AdvanceSample */ = {
+		22F74AA41C7674BB004C6E4C /* AdvanceSample-iOS */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 22F74AB61C7674BB004C6E4C /* Build configuration list for PBXNativeTarget "AdvanceSample" */;
+			buildConfigurationList = 22F74AB61C7674BB004C6E4C /* Build configuration list for PBXNativeTarget "AdvanceSample-iOS" */;
 			buildPhases = (
 				22F74AA11C7674BB004C6E4C /* Sources */,
 				22F74AA21C7674BB004C6E4C /* Frameworks */,
@@ -466,14 +645,14 @@
 			dependencies = (
 				22F74ABE1C767797004C6E4C /* PBXTargetDependency */,
 			);
-			name = AdvanceSample;
+			name = "AdvanceSample-iOS";
 			productName = AnimationSample;
 			productReference = 22F74AA51C7674BB004C6E4C /* AdvanceSample.app */;
 			productType = "com.apple.product-type.application";
 		};
-		5D39CEE01C6E983100DFCF86 /* Advance */ = {
+		5D39CEE01C6E983100DFCF86 /* Advance-iOS */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 5D39CEF51C6E983100DFCF86 /* Build configuration list for PBXNativeTarget "Advance" */;
+			buildConfigurationList = 5D39CEF51C6E983100DFCF86 /* Build configuration list for PBXNativeTarget "Advance-iOS" */;
 			buildPhases = (
 				5D39CEDC1C6E983100DFCF86 /* Sources */,
 				5D39CEDD1C6E983100DFCF86 /* Frameworks */,
@@ -484,14 +663,14 @@
 			);
 			dependencies = (
 			);
-			name = Advance;
+			name = "Advance-iOS";
 			productName = Advance;
 			productReference = 5D39CEE11C6E983100DFCF86 /* Advance.framework */;
 			productType = "com.apple.product-type.framework";
 		};
-		5D39CEEA1C6E983100DFCF86 /* AdvanceTests */ = {
+		5D39CEEA1C6E983100DFCF86 /* AdvanceTests-iOS */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 5D39CEF81C6E983100DFCF86 /* Build configuration list for PBXNativeTarget "AdvanceTests" */;
+			buildConfigurationList = 5D39CEF81C6E983100DFCF86 /* Build configuration list for PBXNativeTarget "AdvanceTests-iOS" */;
 			buildPhases = (
 				5D39CEE71C6E983100DFCF86 /* Sources */,
 				5D39CEE81C6E983100DFCF86 /* Frameworks */,
@@ -501,12 +680,69 @@
 			);
 			dependencies = (
 				5D39CEEE1C6E983100DFCF86 /* PBXTargetDependency */,
+				5DE959EB1C938C54008487A8 /* PBXTargetDependency */,
 			);
-			name = AdvanceTests;
+			name = "AdvanceTests-iOS";
 			productName = AdvanceTests;
 			productReference = 5D39CEEB1C6E983100DFCF86 /* Advance.xctest */;
 			productType = "com.apple.product-type.bundle.unit-test";
 		};
+		5D3A2F301C9209EE0071F08F /* Advance-OSX */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5D3A2F381C9209EE0071F08F /* Build configuration list for PBXNativeTarget "Advance-OSX" */;
+			buildPhases = (
+				5D3A2F2C1C9209EE0071F08F /* Sources */,
+				5D3A2F2D1C9209EE0071F08F /* Frameworks */,
+				5D3A2F2E1C9209EE0071F08F /* Headers */,
+				5D3A2F2F1C9209EE0071F08F /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "Advance-OSX";
+			productName = "Advance-OSX";
+			productReference = 5D3A2F311C9209EE0071F08F /* Advance.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+		5D3A2F671C92559B0071F08F /* AdvanceSample-OSX */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5D3A2F721C92559B0071F08F /* Build configuration list for PBXNativeTarget "AdvanceSample-OSX" */;
+			buildPhases = (
+				5D3A2F641C92559B0071F08F /* Sources */,
+				5D3A2F651C92559B0071F08F /* Frameworks */,
+				5D3A2F661C92559B0071F08F /* Resources */,
+				5D3A2F791C9255B30071F08F /* Embed Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				5D3A2F781C9255B30071F08F /* PBXTargetDependency */,
+			);
+			name = "AdvanceSample-OSX";
+			productName = "AdvanceSample-OSX";
+			productReference = 5D3A2F681C92559B0071F08F /* AdvanceSample-OSX.app */;
+			productType = "com.apple.product-type.application";
+		};
+		5DE959EF1C9392E1008487A8 /* AdvanceTests-OSX */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5DE959F81C9392E1008487A8 /* Build configuration list for PBXNativeTarget "AdvanceTests-OSX" */;
+			buildPhases = (
+				5DE959EC1C9392E1008487A8 /* Sources */,
+				5DE959ED1C9392E1008487A8 /* Frameworks */,
+				5DE959EE1C9392E1008487A8 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				5DE959F71C9392E1008487A8 /* PBXTargetDependency */,
+				5DE95A041C939427008487A8 /* PBXTargetDependency */,
+			);
+			name = "AdvanceTests-OSX";
+			productName = "AdvanceTests-OSX";
+			productReference = 5DE959F01C9392E1008487A8 /* AdvanceTests-OSX.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -525,6 +761,17 @@
 					};
 					5D39CEEA1C6E983100DFCF86 = {
 						CreatedOnToolsVersion = 7.3;
+						TestTargetID = 22F74AA41C7674BB004C6E4C;
+					};
+					5D3A2F301C9209EE0071F08F = {
+						CreatedOnToolsVersion = 7.2.1;
+					};
+					5D3A2F671C92559B0071F08F = {
+						CreatedOnToolsVersion = 7.2.1;
+					};
+					5DE959EF1C9392E1008487A8 = {
+						CreatedOnToolsVersion = 7.2.1;
+						TestTargetID = 5D3A2F671C92559B0071F08F;
 					};
 				};
 			};
@@ -541,9 +788,12 @@
 			projectDirPath = "";
 			projectRoot = "";
 			targets = (
-				5D39CEE01C6E983100DFCF86 /* Advance */,
-				5D39CEEA1C6E983100DFCF86 /* AdvanceTests */,
-				22F74AA41C7674BB004C6E4C /* AdvanceSample */,
+				5D39CEE01C6E983100DFCF86 /* Advance-iOS */,
+				5D3A2F301C9209EE0071F08F /* Advance-OSX */,
+				5D39CEEA1C6E983100DFCF86 /* AdvanceTests-iOS */,
+				5DE959EF1C9392E1008487A8 /* AdvanceTests-OSX */,
+				22F74AA41C7674BB004C6E4C /* AdvanceSample-iOS */,
+				5D3A2F671C92559B0071F08F /* AdvanceSample-OSX */,
 			);
 		};
 /* End PBXProject section */
@@ -572,6 +822,29 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		5D3A2F2F1C9209EE0071F08F /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5D3A2F661C92559B0071F08F /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5D3A2F6D1C92559B0071F08F /* Assets.xcassets in Resources */,
+				5D3A2F701C92559B0071F08F /* MainMenu.xib in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5DE959EE1C9392E1008487A8 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -613,6 +886,7 @@
 				5DB1A4921C876AD1000CF9EE /* Advanceable.swift in Sources */,
 				5DB1A4A51C876AD1000CF9EE /* SpringFunction.swift in Sources */,
 				5DB1A4911C876AD1000CF9EE /* Animatable.swift in Sources */,
+				5D3A2F5F1C922DAA0071F08F /* DisplayLink.swift in Sources */,
 				5DB1A4951C876AD1000CF9EE /* BasicAnimation.swift in Sources */,
 				5DB1A49A1C876AD1000CF9EE /* ValueAnimationType.swift in Sources */,
 				5DB1A4A91C876AD1000CF9EE /* CGSize+VectorConvertible.swift in Sources */,
@@ -646,6 +920,7 @@
 			files = (
 				5DA94F4D1C8112810039B9BB /* CAMediaTimingFunctionTests.swift in Sources */,
 				5DA94F4B1C80E2700039B9BB /* UnitBezierTests.swift in Sources */,
+				5DE959E91C9388B9008487A8 /* DisplayLinkTests.swift in Sources */,
 				22168B5B1C7A547300BAE519 /* VectorTypeTests.swift in Sources */,
 				22168B5D1C7A5E3500BAE519 /* BasicAnimationTests.swift in Sources */,
 				5DA94F4F1C8114680039B9BB /* EventTests.swift in Sources */,
@@ -653,19 +928,105 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		5D3A2F2C1C9209EE0071F08F /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5D3A2F471C920CB10071F08F /* Loop.swift in Sources */,
+				5D3A2F441C920C920071F08F /* AnimatorContext.swift in Sources */,
+				5D3A2F3C1C920C7C0071F08F /* UnitBezier.swift in Sources */,
+				5D3A2F411C920C8C0071F08F /* ValueAnimationType.swift in Sources */,
+				5D3A2F4C1C920CBC0071F08F /* SpringFunction.swift in Sources */,
+				5D3A2F391C920C710071F08F /* Animatable.swift in Sources */,
+				5D3A2F3A1C920C7C0071F08F /* BasicAnimation.swift in Sources */,
+				5D3A2F591C920CCA0071F08F /* VectorType.swift in Sources */,
+				5D3A2F3F1C920C8C0071F08F /* AnimationType.swift in Sources */,
+				5D3A2F4F1C920CC20071F08F /* CGFloat+VectorConvertible.swift in Sources */,
+				5D3A2F531C920CC20071F08F /* CGVector+VectorConvertible.swift in Sources */,
+				5D3A2F4B1C920CBC0071F08F /* Spring.swift in Sources */,
+				5D3A2F3E1C920C860071F08F /* SpringAnimation.swift in Sources */,
+				5D3A2F541C920CCA0071F08F /* Vector1.swift in Sources */,
+				5D3A2F571C920CCA0071F08F /* Vector4.swift in Sources */,
+				5D3A2F521C920CC20071F08F /* CGSize+VectorConvertible.swift in Sources */,
+				5D3A2F421C920C8C0071F08F /* Advanceable.swift in Sources */,
+				5D3A2F5A1C920CCA0071F08F /* Interpolatable.swift in Sources */,
+				5D3A2F4E1C920CC20071F08F /* Double+VectorConvertible.swift in Sources */,
+				5D3A2F3D1C920C810071F08F /* DecayAnimation.swift in Sources */,
+				5D3A2F481C920CBC0071F08F /* DecayFunction.swift in Sources */,
+				5D3A2F3B1C920C7C0071F08F /* TimingFunctionType.swift in Sources */,
+				5D3A2F511C920CC20071F08F /* CGRect+VectorConvertible.swift in Sources */,
+				5D3A2F491C920CBC0071F08F /* DynamicFunctionType.swift in Sources */,
+				5D3A2F501C920CC20071F08F /* CGPoint+VectorConvertible.swift in Sources */,
+				5D3A2F581C920CCA0071F08F /* VectorMathCapable.swift in Sources */,
+				5D3A2F631C923ADB0071F08F /* DisplayLink.swift in Sources */,
+				5D3A2F4D1C920CC20071F08F /* VectorConvertible.swift in Sources */,
+				5D3A2F431C920C920071F08F /* Animator.swift in Sources */,
+				5D3A2F401C920C8C0071F08F /* AnyValueAnimation.swift in Sources */,
+				5D3A2F461C920C9C0071F08F /* CAMediaTimingFunction.swift in Sources */,
+				5D3A2F4A1C920CBC0071F08F /* DynamicSolver.swift in Sources */,
+				5D3A2F551C920CCA0071F08F /* Vector2.swift in Sources */,
+				5D3A2F561C920CCA0071F08F /* Vector3.swift in Sources */,
+				5D3A2F451C920C970071F08F /* Event.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5D3A2F641C92559B0071F08F /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5D28A2DA1C9378F000E5A1E9 /* SpringContainerView.swift in Sources */,
+				5D3A2F6B1C92559B0071F08F /* AppDelegate.swift in Sources */,
+				5D28A2D81C93757200E5A1E9 /* SpringView.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		5DE959EC1C9392E1008487A8 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5DE959FE1C93940E008487A8 /* VectorConvenienceTests.swift in Sources */,
+				5DE95A001C93940E008487A8 /* UnitBezierTests.swift in Sources */,
+				5DE95A021C93940E008487A8 /* DisplayLinkTests.swift in Sources */,
+				5DE95A011C93940E008487A8 /* CAMediaTimingFunctionTests.swift in Sources */,
+				5DE959FF1C93940E008487A8 /* BasicAnimationTests.swift in Sources */,
+				5DE959FD1C93940E008487A8 /* VectorTypeTests.swift in Sources */,
+				5DE959FC1C93940E008487A8 /* EventTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
 		22F74ABE1C767797004C6E4C /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
-			target = 5D39CEE01C6E983100DFCF86 /* Advance */;
+			target = 5D39CEE01C6E983100DFCF86 /* Advance-iOS */;
 			targetProxy = 22F74ABD1C767797004C6E4C /* PBXContainerItemProxy */;
 		};
 		5D39CEEE1C6E983100DFCF86 /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
-			target = 5D39CEE01C6E983100DFCF86 /* Advance */;
+			target = 5D39CEE01C6E983100DFCF86 /* Advance-iOS */;
 			targetProxy = 5D39CEED1C6E983100DFCF86 /* PBXContainerItemProxy */;
 		};
+		5D3A2F781C9255B30071F08F /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 5D3A2F301C9209EE0071F08F /* Advance-OSX */;
+			targetProxy = 5D3A2F771C9255B30071F08F /* PBXContainerItemProxy */;
+		};
+		5DE959EB1C938C54008487A8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 22F74AA41C7674BB004C6E4C /* AdvanceSample-iOS */;
+			targetProxy = 5DE959EA1C938C54008487A8 /* PBXContainerItemProxy */;
+		};
+		5DE959F71C9392E1008487A8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 5D3A2F301C9209EE0071F08F /* Advance-OSX */;
+			targetProxy = 5DE959F61C9392E1008487A8 /* PBXContainerItemProxy */;
+		};
+		5DE95A041C939427008487A8 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 5D3A2F671C92559B0071F08F /* AdvanceSample-OSX */;
+			targetProxy = 5DE95A031C939427008487A8 /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
 /* Begin PBXVariantGroup section */
@@ -677,6 +1038,14 @@
 			name = LaunchScreen.storyboard;
 			sourceTree = "<group>";
 		};
+		5D3A2F6E1C92559B0071F08F /* MainMenu.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				5D3A2F6F1C92559B0071F08F /* Base */,
+			);
+			name = MainMenu.xib;
+			sourceTree = "<group>";
+		};
 /* End PBXVariantGroup section */
 
 /* Begin XCBuildConfiguration section */
@@ -686,7 +1055,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
-				INFOPLIST_FILE = "$(SRCROOT)/AdvanceSample/Info.plist";
+				INFOPLIST_FILE = "$(SRCROOT)/AdvanceSample-iOS/Info.plist";
 				IPHONEOS_DEPLOYMENT_TARGET = 8.2;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = co.storehouse.AdvanceSample;
@@ -700,7 +1069,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
-				INFOPLIST_FILE = "$(SRCROOT)/AdvanceSample/Info.plist";
+				INFOPLIST_FILE = "$(SRCROOT)/AdvanceSample-iOS/Info.plist";
 				IPHONEOS_DEPLOYMENT_TARGET = 8.2;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = co.storehouse.AdvanceSample;
@@ -810,7 +1179,7 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				INFOPLIST_FILE = "$(SRCROOT)/Advance/Info.plist";
+				INFOPLIST_FILE = "$(SRCROOT)/Advance-iOS/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -831,7 +1200,7 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				INFOPLIST_FILE = "$(SRCROOT)/Advance/Info.plist";
+				INFOPLIST_FILE = "$(SRCROOT)/Advance-iOS/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -845,31 +1214,138 @@
 		5D39CEF91C6E983100DFCF86 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				INFOPLIST_FILE = Advance/Info.plist;
+				INFOPLIST_FILE = "AdvanceTests-iOS/Info.plist";
 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = co.storehouse.Advance;
 				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)Tests";
 				PRODUCT_NAME = Advance;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AdvanceSample.app/AdvanceSample";
 			};
 			name = Debug;
 		};
 		5D39CEFA1C6E983100DFCF86 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				INFOPLIST_FILE = Advance/Info.plist;
+				INFOPLIST_FILE = "AdvanceTests-iOS/Info.plist";
 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = co.storehouse.Advance;
 				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)Tests";
 				PRODUCT_NAME = Advance;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AdvanceSample.app/AdvanceSample";
+			};
+			name = Release;
+		};
+		5D3A2F361C9209EE0071F08F /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_IDENTITY = "-";
+				COMBINE_HIDPI_IMAGES = YES;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FRAMEWORK_VERSION = A;
+				INFOPLIST_FILE = "Advance-OSX/Info.plist";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				PRODUCT_BUNDLE_IDENTIFIER = "co.storehouse.Advance-OSX";
+				PRODUCT_NAME = Advance;
+				SDKROOT = macosx;
+				SKIP_INSTALL = YES;
+			};
+			name = Debug;
+		};
+		5D3A2F371C9209EE0071F08F /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_IDENTITY = "-";
+				COMBINE_HIDPI_IMAGES = YES;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FRAMEWORK_VERSION = A;
+				INFOPLIST_FILE = "Advance-OSX/Info.plist";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				PRODUCT_BUNDLE_IDENTIFIER = "co.storehouse.Advance-OSX";
+				PRODUCT_NAME = Advance;
+				SDKROOT = macosx;
+				SKIP_INSTALL = YES;
+				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+			};
+			name = Release;
+		};
+		5D3A2F731C92559B0071F08F /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_IDENTITY = "-";
+				COMBINE_HIDPI_IMAGES = YES;
+				EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
+				INFOPLIST_FILE = "AdvanceSample-OSX/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				PRODUCT_BUNDLE_IDENTIFIER = "co.storehouse.AdvanceSample-OSX";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+			};
+			name = Debug;
+		};
+		5D3A2F741C92559B0071F08F /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_IDENTITY = "-";
+				COMBINE_HIDPI_IMAGES = YES;
+				EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
+				INFOPLIST_FILE = "AdvanceSample-OSX/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				PRODUCT_BUNDLE_IDENTIFIER = "co.storehouse.AdvanceSample-OSX";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+			};
+			name = Release;
+		};
+		5DE959F91C9392E1008487A8 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_IDENTITY = "-";
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = "AdvanceTests-OSX/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				PRODUCT_BUNDLE_IDENTIFIER = "co.storehouse.AdvanceTests-OSX";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AdvanceSample-OSX.app/Contents/MacOS/AdvanceSample-OSX";
+			};
+			name = Debug;
+		};
+		5DE959FA1C9392E1008487A8 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_IDENTITY = "-";
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = "AdvanceTests-OSX/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				PRODUCT_BUNDLE_IDENTIFIER = "co.storehouse.AdvanceTests-OSX";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AdvanceSample-OSX.app/Contents/MacOS/AdvanceSample-OSX";
 			};
 			name = Release;
 		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
-		22F74AB61C7674BB004C6E4C /* Build configuration list for PBXNativeTarget "AdvanceSample" */ = {
+		22F74AB61C7674BB004C6E4C /* Build configuration list for PBXNativeTarget "AdvanceSample-iOS" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				22F74AB41C7674BB004C6E4C /* Debug */,
@@ -887,7 +1363,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		5D39CEF51C6E983100DFCF86 /* Build configuration list for PBXNativeTarget "Advance" */ = {
+		5D39CEF51C6E983100DFCF86 /* Build configuration list for PBXNativeTarget "Advance-iOS" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				5D39CEF61C6E983100DFCF86 /* Debug */,
@@ -896,7 +1372,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		5D39CEF81C6E983100DFCF86 /* Build configuration list for PBXNativeTarget "AdvanceTests" */ = {
+		5D39CEF81C6E983100DFCF86 /* Build configuration list for PBXNativeTarget "AdvanceTests-iOS" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				5D39CEF91C6E983100DFCF86 /* Debug */,
@@ -905,6 +1381,33 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		5D3A2F381C9209EE0071F08F /* Build configuration list for PBXNativeTarget "Advance-OSX" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5D3A2F361C9209EE0071F08F /* Debug */,
+				5D3A2F371C9209EE0071F08F /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		5D3A2F721C92559B0071F08F /* Build configuration list for PBXNativeTarget "AdvanceSample-OSX" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5D3A2F731C92559B0071F08F /* Debug */,
+				5D3A2F741C92559B0071F08F /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		5DE959F81C9392E1008487A8 /* Build configuration list for PBXNativeTarget "AdvanceTests-OSX" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5DE959F91C9392E1008487A8 /* Debug */,
+				5DE959FA1C9392E1008487A8 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 5D39CED81C6E983100DFCF86 /* Project object */;
diff --git a/Advance.xcodeproj/xcshareddata/xcschemes/Advance-OSX.xcscheme b/Advance.xcodeproj/xcshareddata/xcschemes/Advance-OSX.xcscheme
new file mode 100644
index 0000000..9ed45cb
--- /dev/null
+++ b/Advance.xcodeproj/xcshareddata/xcschemes/Advance-OSX.xcscheme
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0720"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5D3A2F301C9209EE0071F08F"
+               BuildableName = "Advance.framework"
+               BlueprintName = "Advance-OSX"
+               ReferencedContainer = "container:Advance.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5DE959EF1C9392E1008487A8"
+               BuildableName = "AdvanceTests-OSX.xctest"
+               BlueprintName = "AdvanceTests-OSX"
+               ReferencedContainer = "container:Advance.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5D3A2F301C9209EE0071F08F"
+            BuildableName = "Advance.framework"
+            BlueprintName = "Advance-OSX"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5D3A2F301C9209EE0071F08F"
+            BuildableName = "Advance.framework"
+            BlueprintName = "Advance-OSX"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5D3A2F301C9209EE0071F08F"
+            BuildableName = "Advance.framework"
+            BlueprintName = "Advance-OSX"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/Advance.xcodeproj/xcshareddata/xcschemes/Advance-iOS.xcscheme b/Advance.xcodeproj/xcshareddata/xcschemes/Advance-iOS.xcscheme
new file mode 100644
index 0000000..c33de0e
--- /dev/null
+++ b/Advance.xcodeproj/xcshareddata/xcschemes/Advance-iOS.xcscheme
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0720"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5D39CEE01C6E983100DFCF86"
+               BuildableName = "Advance.framework"
+               BlueprintName = "Advance-iOS"
+               ReferencedContainer = "container:Advance.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5D39CEEA1C6E983100DFCF86"
+               BuildableName = "Advance.xctest"
+               BlueprintName = "AdvanceTests-iOS"
+               ReferencedContainer = "container:Advance.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5D39CEE01C6E983100DFCF86"
+            BuildableName = "Advance.framework"
+            BlueprintName = "Advance-iOS"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5D39CEE01C6E983100DFCF86"
+            BuildableName = "Advance.framework"
+            BlueprintName = "Advance-iOS"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5D39CEE01C6E983100DFCF86"
+            BuildableName = "Advance.framework"
+            BlueprintName = "Advance-iOS"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/Advance.xcodeproj/xcshareddata/xcschemes/Advance.xcscheme b/Advance.xcodeproj/xcshareddata/xcschemes/Advance.xcscheme
deleted file mode 100644
index e451e1c..0000000
--- a/Advance.xcodeproj/xcshareddata/xcschemes/Advance.xcscheme
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "0720"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "5D39CEE01C6E983100DFCF86"
-               BuildableName = "Advance.framework"
-               BlueprintName = "Advance"
-               ReferencedContainer = "container:Advance.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "5D39CEEA1C6E983100DFCF86"
-               BuildableName = "Advance.xctest"
-               BlueprintName = "AdvanceTests"
-               ReferencedContainer = "container:Advance.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-      </Testables>
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "5D39CEE01C6E983100DFCF86"
-            BuildableName = "Advance.framework"
-            BlueprintName = "Advance"
-            ReferencedContainer = "container:Advance.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "5D39CEE01C6E983100DFCF86"
-            BuildableName = "Advance.framework"
-            BlueprintName = "Advance"
-            ReferencedContainer = "container:Advance.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "5D39CEE01C6E983100DFCF86"
-            BuildableName = "Advance.framework"
-            BlueprintName = "Advance"
-            ReferencedContainer = "container:Advance.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>
diff --git a/Advance.xcodeproj/xcshareddata/xcschemes/AdvanceSample-OSX.xcscheme b/Advance.xcodeproj/xcshareddata/xcschemes/AdvanceSample-OSX.xcscheme
new file mode 100644
index 0000000..e483178
--- /dev/null
+++ b/Advance.xcodeproj/xcshareddata/xcschemes/AdvanceSample-OSX.xcscheme
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0720"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5D3A2F671C92559B0071F08F"
+               BuildableName = "AdvanceSample-OSX.app"
+               BlueprintName = "AdvanceSample-OSX"
+               ReferencedContainer = "container:Advance.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5D3A2F671C92559B0071F08F"
+            BuildableName = "AdvanceSample-OSX.app"
+            BlueprintName = "AdvanceSample-OSX"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5D3A2F671C92559B0071F08F"
+            BuildableName = "AdvanceSample-OSX.app"
+            BlueprintName = "AdvanceSample-OSX"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "5D3A2F671C92559B0071F08F"
+            BuildableName = "AdvanceSample-OSX.app"
+            BlueprintName = "AdvanceSample-OSX"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/Advance.xcodeproj/xcshareddata/xcschemes/AdvanceSample-iOS.xcscheme b/Advance.xcodeproj/xcshareddata/xcschemes/AdvanceSample-iOS.xcscheme
new file mode 100644
index 0000000..1ed8ba8
--- /dev/null
+++ b/Advance.xcodeproj/xcshareddata/xcschemes/AdvanceSample-iOS.xcscheme
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0720"
+   version = "1.8">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES"
+            hideIssues = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "22F74AA41C7674BB004C6E4C"
+               BuildableName = "AdvanceSample.app"
+               BlueprintName = "AdvanceSample-iOS"
+               ReferencedContainer = "container:Advance.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "22F74AA41C7674BB004C6E4C"
+            BuildableName = "AdvanceSample.app"
+            BlueprintName = "AdvanceSample-iOS"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "22F74AA41C7674BB004C6E4C"
+            BuildableName = "AdvanceSample.app"
+            BlueprintName = "AdvanceSample-iOS"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "22F74AA41C7674BB004C6E4C"
+            BuildableName = "AdvanceSample.app"
+            BlueprintName = "AdvanceSample-iOS"
+            ReferencedContainer = "container:Advance.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/Advance.xcodeproj/xcshareddata/xcschemes/AdvanceSample.xcscheme b/Advance.xcodeproj/xcshareddata/xcschemes/AdvanceSample.xcscheme
deleted file mode 100644
index 7150d97..0000000
--- a/Advance.xcodeproj/xcshareddata/xcschemes/AdvanceSample.xcscheme
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "0720"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES"
-            hideIssues = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "22F74AA41C7674BB004C6E4C"
-               BuildableName = "AdvanceSample.app"
-               BlueprintName = "AdvanceSample"
-               ReferencedContainer = "container:Advance.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-      </Testables>
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "22F74AA41C7674BB004C6E4C"
-            BuildableName = "AdvanceSample.app"
-            BlueprintName = "AdvanceSample"
-            ReferencedContainer = "container:Advance.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "22F74AA41C7674BB004C6E4C"
-            BuildableName = "AdvanceSample.app"
-            BlueprintName = "AdvanceSample"
-            ReferencedContainer = "container:Advance.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "22F74AA41C7674BB004C6E4C"
-            BuildableName = "AdvanceSample.app"
-            BlueprintName = "AdvanceSample"
-            ReferencedContainer = "container:Advance.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>
diff --git a/Advance/Info.plist b/Advance/Info.plist
deleted file mode 100644
index 6010f22..0000000
--- a/Advance/Info.plist
+++ /dev/null
@@ -1,26 +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>CFBundleDevelopmentRegion</key>
-	<string>en</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>FMWK</string>
-	<key>CFBundleShortVersionString</key>
-	<string>0.9</string>
-	<key>CFBundleSignature</key>
-	<string>????</string>
-	<key>CFBundleVersion</key>
-	<string>$(CURRENT_PROJECT_VERSION)</string>
-	<key>NSPrincipalClass</key>
-	<string></string>
-</dict>
-</plist>
diff --git a/Advance/Loop/DisplayLink.swift b/Advance/Loop/DisplayLink.swift
new file mode 100644
index 0000000..f810ff8
--- /dev/null
+++ b/Advance/Loop/DisplayLink.swift
@@ -0,0 +1,169 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import Foundation
+
+#if os(iOS)
+import QuartzCore
+#elseif os(OSX)
+import CoreVideo
+#endif
+
+
+extension DisplayLink {
+    /// Info about a particular frame.
+    struct Frame : Equatable {
+        /// The timestamp that the frame.
+        var timestamp: Double
+        
+        /// The current duration between frames.
+        var duration: Double
+    }
+}
+
+func ==(lhs: DisplayLink.Frame, rhs: DisplayLink.Frame) -> Bool {
+    return lhs.timestamp == rhs.timestamp && lhs.duration == rhs.duration
+}
+
+
+#if os(iOS) // iOS support using CADisplayLink --------------------------------------------------------
+
+/// DisplayLink is used to hook into screen refreshes.
+internal final class DisplayLink {
+    
+    /// The callback to call for each frame.
+    var callback: ((frame: Frame) -> Void)? = nil
+    
+    /// If the display link is paused or not.
+    var paused: Bool {
+        get {
+            return displayLink.paused
+        }
+        set {
+            displayLink.paused = newValue
+        }
+    }
+    
+    /// The CADisplayLink that powers this DisplayLink instance.
+    let displayLink: CADisplayLink
+    
+    /// The target for the CADisplayLink (because CADisplayLink retains its target).
+    let target = DisplayLinkTarget()
+    
+    /// Creates a new paused DisplayLink instance.
+    init() {
+        displayLink = CADisplayLink(target: target, selector: "frame:")
+        displayLink.paused = true
+        displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
+        
+        target.callback = { [unowned self] (frame) in
+            self.callback?(frame: frame)
+        }
+    }
+    
+    deinit {
+        displayLink.invalidate()
+    }
+    
+    /// The target for the CADisplayLink (because CADisplayLink retains its target).
+    internal final class DisplayLinkTarget {
+        
+        /// The callback to call for each frame.
+        var callback: ((frame: DisplayLink.Frame) -> Void)? = nil
+        
+        /// Called for each frame from the CADisplayLink.
+        dynamic func frame(displayLink: CADisplayLink) {
+            callback?(frame: Frame(timestamp: displayLink.timestamp, duration: displayLink.duration))
+        }
+    }
+}
+
+#elseif os(OSX) // OS X support using CVDisplayLink --------------------------------------------------
+
+/// DisplayLink is used to hook into screen refreshes.
+internal final class DisplayLink {
+    
+    /// The callback to call for each frame.
+    var callback: ((frame: Frame) -> Void)? = nil
+    
+    /// If the display link is paused or not.
+    var paused: Bool = true {
+        didSet {
+            guard paused != oldValue else { return }
+            if paused == true {
+                CVDisplayLinkStop(self.displayLink)
+            } else {
+                CVDisplayLinkStart(self.displayLink)
+            }
+        }
+    }
+    
+    /// The CVDisplayLink that powers this DisplayLink instance.
+    var displayLink: CVDisplayLinkRef = {
+        var dl: CVDisplayLinkRef? = nil
+        CVDisplayLinkCreateWithActiveCGDisplays(&dl)
+        return dl!
+    }()
+    
+    var lastFrame = Frame(timestamp: 0, duration: 0)
+    
+    /// Creates a new paused DisplayLink instance.
+    init() {
+        func displayLinkOutputCallback(displayLink: CVDisplayLink, inNow: UnsafePointer<CVTimeStamp>, inOutputTime: UnsafePointer<CVTimeStamp>, flagsIn: CVOptionFlags, flagsOut: UnsafeMutablePointer<CVOptionFlags>, userInfo: UnsafeMutablePointer<Void>) -> CVReturn {
+            let timestamp = Double(inNow.memory.videoTime) / Double(inNow.memory.videoTimeScale) // convert to seconds
+            let dl = unsafeBitCast(userInfo, DisplayLink.self) // cast back to a pointer to self
+            
+            let time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLink)
+            let duration = Double(Double(time.timeValue) / Double(time.timeScale)) // convert to seconds
+            let info = Frame(timestamp: timestamp, duration: duration)
+            
+            // dispatch to main - this is called from an internal CV thread
+            dispatch_async(dispatch_get_main_queue()) { () -> Void in
+                dl.frame(info) // call frame(timestamp:) on self
+            }
+            return kCVReturnSuccess
+        }
+        
+        // hook up the frame output callback
+        CVDisplayLinkSetOutputCallback(self.displayLink, displayLinkOutputCallback, UnsafeMutablePointer<Void>(unsafeAddressOf(self))) // pass in self as the 'userInfo'
+    }
+    
+    deinit {
+        paused = true
+    }
+    
+    /// Called for each CVDisplayLink frame callback.
+    func frame(frame: Frame) {
+        guard paused == false else { return }
+        guard frame.timestamp != lastFrame.timestamp else { return }
+        callback?(frame: frame)
+        lastFrame = frame
+    }
+}
+
+#endif // ------------------------------------------------------------------------------------------
diff --git a/Advance/Loop/Loop.swift b/Advance/Loop/Loop.swift
index ff158dd..bc649dd 100644
--- a/Advance/Loop/Loop.swift
+++ b/Advance/Loop/Loop.swift
@@ -38,10 +38,11 @@ public final class Loop {
     
     private var currentAnimationTime: Double = 0.0
     
-    private lazy var displayLink: CADisplayLink = {
-        let link = CADisplayLink(target: self, selector: "displayLinkDidFire:")
-        link.paused = true
-        link.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
+    private lazy var displayLink: DisplayLink = {
+        let link = DisplayLink()
+        link.callback = { [unowned self] (frame) in
+            self.displayLinkDidFire(frame)
+        }
         return link
     }()
     
@@ -88,12 +89,12 @@ public final class Loop {
         displayLink.paused = true
     }
     
-    dynamic private func displayLinkDidFire(displayLink: CADisplayLink) {
+    private func displayLinkDidFire(frame: DisplayLink.Frame) {
         
-        let timestamp = max(displayLink.timestamp, currentAnimationTime)
+        let timestamp = max(frame.timestamp, currentAnimationTime)
         
         if currentAnimationTime == 0.0 {
-            currentAnimationTime = timestamp - displayLink.duration
+            currentAnimationTime = timestamp - frame.duration
         }
         
         let elapsed = timestamp - currentAnimationTime
diff --git a/AdvanceSample-OSX/AppDelegate.swift b/AdvanceSample-OSX/AppDelegate.swift
new file mode 100644
index 0000000..b6c3cc9
--- /dev/null
+++ b/AdvanceSample-OSX/AppDelegate.swift
@@ -0,0 +1,41 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import Cocoa
+
+
+@NSApplicationMain
+class AppDelegate: NSObject, NSApplicationDelegate {
+
+    @IBOutlet var window: NSWindow!
+    
+    func applicationDidFinishLaunching(aNotification: NSNotification) {
+        
+    }
+}
+
diff --git a/AdvanceSample-OSX/Assets.xcassets/AppIcon.appiconset/Contents.json b/AdvanceSample-OSX/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..2db2b1c
--- /dev/null
+++ b/AdvanceSample-OSX/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,58 @@
+{
+  "images" : [
+    {
+      "idiom" : "mac",
+      "size" : "16x16",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "16x16",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "32x32",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "32x32",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "128x128",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "128x128",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "256x256",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "256x256",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "512x512",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "512x512",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/AdvanceSample-OSX/Base.lproj/MainMenu.xib b/AdvanceSample-OSX/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000..d000235
--- /dev/null
+++ b/AdvanceSample-OSX/Base.lproj/MainMenu.xib
@@ -0,0 +1,756 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9532" systemVersion="15D21" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+    <dependencies>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9532"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+            <connections>
+                <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+            </connections>
+        </customObject>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="AdvanceSample_OSX" customModuleProvider="target">
+            <connections>
+                <outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
+            </connections>
+        </customObject>
+        <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+        <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+            <items>
+                <menuItem title="AdvanceSample-OSX" id="1Xt-HY-uBw">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="AdvanceSample-OSX" systemMenu="apple" id="uQy-DD-JDr">
+                        <items>
+                            <menuItem title="About AdvanceSample-OSX" id="5kV-Vb-QxS">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+                            <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+                            <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+                            <menuItem title="Services" id="NMo-om-nkz">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+                            <menuItem title="Hide AdvanceSample-OSX" keyEquivalent="h" id="Olw-nP-bQN">
+                                <connections>
+                                    <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Show All" id="Kd2-mp-pUS">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+                            <menuItem title="Quit AdvanceSample-OSX" keyEquivalent="q" id="4sb-4s-VLi">
+                                <connections>
+                                    <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="File" id="dMs-cI-mzQ">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="File" id="bib-Uj-vzu">
+                        <items>
+                            <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
+                                <connections>
+                                    <action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
+                                <connections>
+                                    <action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open Recent" id="tXI-mr-wws">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
+                                    <items>
+                                        <menuItem title="Clear Menu" id="vNY-rz-j42">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
+                            <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
+                                <connections>
+                                    <action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
+                                <connections>
+                                    <action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
+                                <connections>
+                                    <action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Revert to Saved" id="KaW-ft-85H">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
+                            <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
+                                <connections>
+                                    <action selector="print:" target="-1" id="qaZ-4w-aoO"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Edit" id="5QF-Oa-p0T">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+                        <items>
+                            <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+                                <connections>
+                                    <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+                                <connections>
+                                    <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+                            <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+                                <connections>
+                                    <action selector="cut:" target="-1" id="YJe-68-I9s"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+                                <connections>
+                                    <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+                                <connections>
+                                    <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Delete" id="pa3-QI-u2k">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+                                <connections>
+                                    <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+                            <menuItem title="Find" id="4EN-yA-p0u">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Find" id="1b7-l0-nxx">
+                                    <items>
+                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+                                            <connections>
+                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+                                    <items>
+                                        <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+                                            <connections>
+                                                <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+                                            <connections>
+                                                <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+                                        <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Substitutions" id="9ic-FL-obx">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+                                    <items>
+                                        <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+                                        <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Links" id="cwL-P1-jid">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Data Detectors" id="tRr-pd-1PS">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Transformations" id="2oI-Rn-ZJC">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+                                    <items>
+                                        <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Speech" id="xrE-MZ-jX0">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+                                    <items>
+                                        <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Format" id="jxT-CU-nIS">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Format" id="GEO-Iw-cKr">
+                        <items>
+                            <menuItem title="Font" id="Gi5-1S-RQB">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
+                                    <items>
+                                        <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
+                                            <connections>
+                                                <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
+                                            <connections>
+                                                <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
+                                            <connections>
+                                                <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
+                                            <connections>
+                                                <action selector="underline:" target="-1" id="FYS-2b-JAY"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
+                                        <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
+                                            <connections>
+                                                <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
+                                            <connections>
+                                                <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
+                                        <menuItem title="Kern" id="jBQ-r6-VK2">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
+                                                <items>
+                                                    <menuItem title="Use Default" id="GUa-eO-cwY">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Use None" id="cDB-IK-hbR">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Tighten" id="46P-cB-AYj">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Loosen" id="ogc-rX-tC1">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Ligatures" id="o6e-r0-MWq">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
+                                                <items>
+                                                    <menuItem title="Use Default" id="agt-UL-0e3">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Use None" id="J7y-lM-qPV">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Use All" id="xQD-1f-W4t">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Baseline" id="OaQ-X3-Vso">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Baseline" id="ijk-EB-dga">
+                                                <items>
+                                                    <menuItem title="Use Default" id="3Om-Ey-2VK">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Superscript" id="Rqc-34-cIF">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Subscript" id="I0S-gh-46l">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Raise" id="2h7-ER-AoG">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Lower" id="1tx-W0-xDw">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
+                                        <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
+                                            <connections>
+                                                <action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
+                                        <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Text" id="Fal-I4-PZk">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Text" id="d9c-me-L2H">
+                                    <items>
+                                        <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
+                                            <connections>
+                                                <action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
+                                            <connections>
+                                                <action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Justify" id="J5U-5w-g23">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
+                                            <connections>
+                                                <action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
+                                        <menuItem title="Writing Direction" id="H1b-Si-o9J">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
+                                                <items>
+                                                    <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                    </menuItem>
+                                                    <menuItem id="YGs-j5-SAR">
+                                                        <string key="title">	Default</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="Lbh-J2-qVU">
+                                                        <string key="title">	Left to Right</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="jFq-tB-4Kx">
+                                                        <string key="title">	Right to Left</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
+                                                    <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                    </menuItem>
+                                                    <menuItem id="Nop-cj-93Q">
+                                                        <string key="title">	Default</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="BgM-ve-c93">
+                                                        <string key="title">	Left to Right</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="RB4-Sm-HuC">
+                                                        <string key="title">	Right to Left</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
+                                        <menuItem title="Show Ruler" id="vLm-3I-IUL">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
+                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
+                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="View" id="H8h-7b-M4v">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="View" id="HyV-fh-RgO">
+                        <items>
+                            <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Window" id="aUF-d1-5bR">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+                        <items>
+                            <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+                                <connections>
+                                    <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Zoom" id="R4o-n2-Eq4">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+                            <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Help" id="wpr-3q-Mcd">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+                        <items>
+                            <menuItem title="AdvanceSample-OSX Help" keyEquivalent="?" id="FKE-Sm-Kum">
+                                <connections>
+                                    <action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+            </items>
+        </menu>
+        <window title="AdvanceSample-OSX" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
+            <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+            <rect key="contentRect" x="335" y="390" width="470" height="628"/>
+            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
+            <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
+                <rect key="frame" x="0.0" y="0.0" width="470" height="628"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <customView translatesAutoresizingMaskIntoConstraints="NO" id="2GC-Eh-228" customClass="SpringContainerView" customModule="AdvanceSample_OSX" customModuleProvider="target">
+                        <rect key="frame" x="0.0" y="0.0" width="470" height="628"/>
+                        <subviews>
+                            <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t5A-3S-zNd">
+                                <rect key="frame" x="91" y="591" width="288" height="17"/>
+                                <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Click anywhere to move the dot using a spring." id="mqy-JN-xV3">
+                                    <font key="font" metaFont="system"/>
+                                    <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                </textFieldCell>
+                            </textField>
+                            <slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wyX-O9-fNI">
+                                <rect key="frame" x="81" y="48" width="371" height="21"/>
+                                <sliderCell key="cell" continuous="YES" state="on" alignment="left" minValue="1" maxValue="500" doubleValue="50" tickMarkPosition="above" sliderType="linear" id="cnN-NF-sqL"/>
+                                <connections>
+                                    <action selector="tensionChanged:" target="2GC-Eh-228" id="kM1-uo-EY2"/>
+                                </connections>
+                            </slider>
+                            <slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="msm-mE-zF5">
+                                <rect key="frame" x="81" y="16" width="371" height="21"/>
+                                <sliderCell key="cell" continuous="YES" state="on" alignment="left" minValue="0.10000000000000001" maxValue="150" doubleValue="50" tickMarkPosition="above" sliderType="linear" id="8hn-8y-v1g"/>
+                                <connections>
+                                    <action selector="dampingSlider:" target="2GC-Eh-228" id="UUZ-c0-UfB"/>
+                                </connections>
+                            </slider>
+                            <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ve5-f8-d4B">
+                                <rect key="frame" x="18" y="51" width="59" height="17"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="55" id="xKs-Ai-bvu"/>
+                                </constraints>
+                                <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Tension" id="sEv-Tu-sum">
+                                    <font key="font" metaFont="system"/>
+                                    <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                </textFieldCell>
+                            </textField>
+                            <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eYd-jG-Gsk">
+                                <rect key="frame" x="18" y="20" width="59" height="17"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="55" id="cAt-85-tBh"/>
+                                </constraints>
+                                <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Damping" id="wlW-hx-4ts">
+                                    <font key="font" metaFont="system"/>
+                                    <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                </textFieldCell>
+                            </textField>
+                        </subviews>
+                        <constraints>
+                            <constraint firstItem="t5A-3S-zNd" firstAttribute="centerX" secondItem="2GC-Eh-228" secondAttribute="centerX" id="4Or-jw-7dG"/>
+                            <constraint firstItem="eYd-jG-Gsk" firstAttribute="top" secondItem="ve5-f8-d4B" secondAttribute="bottom" constant="14" id="JDe-SJ-mJV"/>
+                            <constraint firstItem="msm-mE-zF5" firstAttribute="top" secondItem="wyX-O9-fNI" secondAttribute="bottom" constant="15" id="KNR-MF-Pfc"/>
+                            <constraint firstAttribute="trailing" secondItem="msm-mE-zF5" secondAttribute="trailing" constant="20" id="KZ3-xV-Gaf"/>
+                            <constraint firstItem="wyX-O9-fNI" firstAttribute="leading" secondItem="ve5-f8-d4B" secondAttribute="trailing" constant="8" id="P5o-Pm-rvf"/>
+                            <constraint firstItem="t5A-3S-zNd" firstAttribute="top" secondItem="2GC-Eh-228" secondAttribute="top" constant="20" symbolic="YES" id="T8m-T7-0Ng"/>
+                            <constraint firstAttribute="trailing" secondItem="wyX-O9-fNI" secondAttribute="trailing" constant="20" id="Vtn-6L-3xx"/>
+                            <constraint firstAttribute="bottom" secondItem="eYd-jG-Gsk" secondAttribute="bottom" constant="20" id="VwL-Cf-M2R"/>
+                            <constraint firstAttribute="bottom" secondItem="msm-mE-zF5" secondAttribute="bottom" constant="18" id="W9Z-sT-x5C"/>
+                            <constraint firstItem="eYd-jG-Gsk" firstAttribute="leading" secondItem="2GC-Eh-228" secondAttribute="leading" constant="20" id="as3-37-Sdg"/>
+                            <constraint firstItem="ve5-f8-d4B" firstAttribute="leading" secondItem="2GC-Eh-228" secondAttribute="leading" constant="20" id="dxd-Cp-LTT"/>
+                            <constraint firstItem="msm-mE-zF5" firstAttribute="leading" secondItem="eYd-jG-Gsk" secondAttribute="trailing" constant="8" id="hRj-fK-Q5Z"/>
+                        </constraints>
+                        <connections>
+                            <outlet property="dampingSlider" destination="msm-mE-zF5" id="4zB-Jo-8gM"/>
+                            <outlet property="tensionSlider" destination="wyX-O9-fNI" id="Dei-7q-NHR"/>
+                        </connections>
+                    </customView>
+                </subviews>
+                <constraints>
+                    <constraint firstAttribute="bottom" secondItem="2GC-Eh-228" secondAttribute="bottom" id="2OG-Rf-8kS"/>
+                    <constraint firstItem="2GC-Eh-228" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" id="Dnm-hf-J9Q"/>
+                    <constraint firstItem="2GC-Eh-228" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" id="LlQ-9v-pRI"/>
+                    <constraint firstAttribute="trailing" secondItem="2GC-Eh-228" secondAttribute="trailing" id="No3-SS-91K"/>
+                </constraints>
+            </view>
+            <point key="canvasLocation" x="213" y="489"/>
+        </window>
+    </objects>
+</document>
diff --git a/AdvanceSample-OSX/Info.plist b/AdvanceSample-OSX/Info.plist
new file mode 100644
index 0000000..5f4b77b
--- /dev/null
+++ b/AdvanceSample-OSX/Info.plist
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2016 Storehouse Media Inc. All rights reserved.</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>
diff --git a/AdvanceSample-OSX/SpringContainerView.swift b/AdvanceSample-OSX/SpringContainerView.swift
new file mode 100644
index 0000000..7d12b7c
--- /dev/null
+++ b/AdvanceSample-OSX/SpringContainerView.swift
@@ -0,0 +1,70 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import Cocoa
+import Advance
+
+
+final class SpringContainerView : NSView {
+    
+    @IBOutlet var tensionSlider: NSSlider!
+    @IBOutlet var dampingSlider: NSSlider!
+    
+    var springView = SpringView(frame: CGRect(x: 0.0, y: 0.0, width: 24.0, height: 24.0))
+
+    required init?(coder: NSCoder) {
+        super.init(coder: coder)
+        wantsLayer = true
+        layer?.backgroundColor = NSColor.whiteColor().CGColor
+        layer?.delegate = self
+        
+        addSubview(springView)
+        springView.centerSpring.reset(CGPoint(x: bounds.midX, y: bounds.midY))
+    }
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        tensionSlider.doubleValue = springView.centerSpring.configuration.tension
+        dampingSlider.doubleValue = springView.centerSpring.configuration.damping
+    }
+    
+    override func mouseDown(theEvent: NSEvent) {
+        var point = theEvent.locationInWindow
+        point.x -= (springView.bounds.width / 2.0)
+        point.y -= (springView.bounds.height / 2.0)
+        springView.centerSpring.target = point
+    }
+    
+    @IBAction func tensionChanged(sender: AnyObject) {
+        springView.centerSpring.configuration.tension = tensionSlider.doubleValue
+    }
+    
+    @IBAction func dampingSlider(sender: AnyObject) {
+        springView.centerSpring.configuration.damping = dampingSlider.doubleValue
+    }
+}
diff --git a/AdvanceSample-OSX/SpringView.swift b/AdvanceSample-OSX/SpringView.swift
new file mode 100644
index 0000000..bcfaeec
--- /dev/null
+++ b/AdvanceSample-OSX/SpringView.swift
@@ -0,0 +1,62 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import Cocoa
+import Advance
+
+
+final class SpringView : NSView {
+    let centerSpring = Spring(value: CGPoint.zero)
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        wantsLayer = true
+        
+        layer?.backgroundColor = NSColor(calibratedRed: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0).CGColor
+        layer?.delegate = self
+        
+        let t = arc4random_uniform(120 - 20) + 20
+        let d = arc4random_uniform(20 - 4) + 4
+        
+        centerSpring.configuration.tension = Scalar(t)
+        centerSpring.configuration.damping = Scalar(d)
+        
+        centerSpring.changed.observe { [unowned self] (c) -> Void in
+            self.layer?.position = c
+        }
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func layoutSublayersOfLayer(layer: CALayer) {
+        layer.cornerRadius = min(bounds.width, bounds.height) / 2.0
+    }
+}
+
diff --git a/AdvanceSample-iOS/ActivityView.swift b/AdvanceSample-iOS/ActivityView.swift
new file mode 100644
index 0000000..2c0da84
--- /dev/null
+++ b/AdvanceSample-iOS/ActivityView.swift
@@ -0,0 +1,300 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import UIKit
+import Advance
+
+
+
+public final class ActivityView: UIView {
+    
+    private static let points: [CGPoint] = {
+        var points: [CGPoint] = Array(count: 10, repeatedValue: CGPoint.zero)
+        points[0] = CGPoint(x: 0.5, y: 0.0)
+        points[1] = CGPoint(x: 0.20928571428571, y: 0.16642857142857)
+        points[2] = CGPoint(x: 0.79071428571429, y: 0.16642857142857)
+        points[3] = CGPoint(x: 0.5, y: 0.3325)
+        points[4] = CGPoint(x: 0.20928571428571, y: 0.49964285714286)
+        points[5] = CGPoint(x: 0.79071428571429, y: 0.49964285714286)
+        points[6] = CGPoint(x: 0.5, y: 0.66607142857143)
+        points[7] = CGPoint(x: 0.20928571428571, y: 0.83357142857143)
+        points[8] = CGPoint(x: 0.79071428571429, y: 0.83357142857143)
+        points[9] = CGPoint(x: 0.5, y: 1.0)
+        return points
+    }()
+    
+    private let segments: [ActivitySegment] = {
+        var segments: [ActivitySegment] = []
+        segments.append(ActivitySegment(firstPoint: points[1], secondPoint: points[0]))
+        segments.append(ActivitySegment(firstPoint: points[0], secondPoint: points[2]))
+        segments.append(ActivitySegment(firstPoint: points[1], secondPoint: points[3]))
+        segments.append(ActivitySegment(firstPoint: points[2], secondPoint: points[3]))
+        segments.append(ActivitySegment(firstPoint: points[1], secondPoint: points[4]))
+        segments.append(ActivitySegment(firstPoint: points[4], secondPoint: points[3]))
+        segments.append(ActivitySegment(firstPoint: points[3], secondPoint: points[5]))
+        segments.append(ActivitySegment(firstPoint: points[4], secondPoint: points[6]))
+        segments.append(ActivitySegment(firstPoint: points[6], secondPoint: points[5]))
+        segments.append(ActivitySegment(firstPoint: points[5], secondPoint: points[8]))
+        segments.append(ActivitySegment(firstPoint: points[6], secondPoint: points[7]))
+        segments.append(ActivitySegment(firstPoint: points[6], secondPoint: points[8]))
+        segments.append(ActivitySegment(firstPoint: points[7], secondPoint: points[9]))
+        segments.append(ActivitySegment(firstPoint: points[8], secondPoint: points[9]))
+        return segments
+    }()
+    
+    private let visibilitySprings: [Spring<CGFloat>] = {
+        return (0...13).map {_ in
+            let s = Spring(value: CGFloat(1.0))
+            s.configuration.threshold = 0.001
+            s.configuration.tension = 220.0 + Double(arc4random() % 200);
+            s.configuration.damping = 30.0 + Double(arc4random() % 10);
+            return s
+        }
+    }()
+    
+    private var segmentLayers: [CAShapeLayer] = {
+        return (0...13).map {_ in
+            let sl = CAShapeLayer()
+            var actions: [String: AnyObject] = [:]
+            actions["position"] = NSNull()
+            actions["bounds"] = NSNull()
+            actions["lineWidth"] = NSNull()
+            actions["strokeColor"] = NSNull()
+            actions["path"] = NSNull()
+            return sl
+        }
+    }()
+    
+    private var strokeColor: UIColor {
+        return color.colorWithAlphaComponent(0.6)
+    }
+    
+    private var flashStrokeColor: UIColor {
+        return color
+    }
+    
+    private let lineWidth = CGFloat(1.0)
+    private let flashLineWidth = CGFloat(2.0)
+    
+    private var flashTimer: NSTimer? = nil
+    
+    public var color = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0) {
+        didSet {
+            for sl in segmentLayers {
+                sl.strokeColor = strokeColor.CGColor
+            }
+        }
+    }
+    
+    public var assembledAmount: CGFloat = 1.0 {
+        didSet {
+            updateSegmentVisibility(true)
+        }
+    }
+    
+    public var flashing: Bool = false {
+        didSet {
+            guard flashing != oldValue else { return }
+            if flashing {
+                let t = NSTimer(timeInterval: 1.0, target: self, selector: "flash", userInfo: nil, repeats: true)
+                NSRunLoop.mainRunLoop().addTimer(t, forMode: NSRunLoopCommonModes)
+                flashTimer = t
+                flash()
+            } else {
+                flashTimer?.invalidate()
+                flashTimer = nil
+            }
+        }
+    }
+    
+    required override public init(frame: CGRect) {
+        super.init(frame: frame)
+        
+        backgroundColor = UIColor.clearColor()
+        layer.allowsGroupOpacity = false
+        
+        for vs in visibilitySprings {
+            vs.changed.observe({ [weak self] (vis) in
+                self?.setNeedsLayout()
+            })
+        }
+        
+        for sl in segmentLayers {
+            sl.strokeColor = strokeColor.CGColor
+            sl.lineWidth = lineWidth
+            layer.addSublayer(sl)
+        }
+    }
+    
+    required public init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override public func layoutSubviews() {
+        super.layoutSubviews()
+        
+        for i in 0..<segments.count {
+            let s = segments[i]
+            let sl = segmentLayers[i]
+            let visibility = visibilitySprings[i].value
+            sl.frame = bounds
+            sl.path = s.path(bounds.size, visibility: visibility).CGPath
+            sl.opacity = Float(visibility)
+        }
+    }
+    
+    override public func sizeThatFits(size: CGSize) -> CGSize {
+        return CGSize(width: 40.0, height: 40.0)
+    }
+    
+    public func resetAssembledAmount(assembledAmount: CGFloat) {
+        self.assembledAmount = assembledAmount
+        updateSegmentVisibility(false)
+    }
+    
+    func updateSegmentVisibility(animated: Bool) {
+        for i in 0..<segments.count {
+            let positionInArray = CGFloat(i) / CGFloat(segments.count-1)
+            
+            let minVis = (1.0-positionInArray) * 0.4
+            let maxVis = minVis + 0.6
+            
+            var mappedVis = (assembledAmount - minVis) / (maxVis - minVis)
+            mappedVis = min(mappedVis, 1.0)
+            mappedVis = max(mappedVis, 0.0)
+            mappedVis = quadEaseInOut(mappedVis)
+            
+            if animated {
+                visibilitySprings[i].target = mappedVis
+            } else {
+                visibilitySprings[i].reset(mappedVis)
+            }
+        }
+    }
+
+    private dynamic func flash() {
+        for i in 0..<segmentLayers.count {
+            let t = dispatch_time(DISPATCH_TIME_NOW, Int64(Double(NSEC_PER_SEC) * 0.04 * Double(i)))
+            dispatch_after(t, dispatch_get_main_queue(), { 
+                self.flashSegment(i)
+            })
+        }
+    }
+    
+    private func flashSegment(index: Int) {
+        let sl = segmentLayers[index]
+        
+        CATransaction.begin()
+        
+        let c = CAKeyframeAnimation(keyPath: "strokeColor")
+        c.values = [strokeColor.CGColor, flashStrokeColor.CGColor, strokeColor.CGColor]
+        c.keyTimes = [0.0, 0.3, 1.0]
+        c.calculationMode = kCAAnimationCubic
+        c.duration = 0.5
+        sl.addAnimation(c, forKey: "flashStrokeColor")
+        
+        let s = CAKeyframeAnimation(keyPath: "lineWidth")
+        s.values = [lineWidth, flashLineWidth, lineWidth]
+        s.keyTimes = [0.0, 0.3, 1.0]
+        s.calculationMode = kCAAnimationCubic
+        s.duration = 0.5
+        sl.addAnimation(s, forKey: "flashLineWidth")
+        
+        CATransaction.commit()
+    }
+}
+
+private func quadEaseInOut(t: CGFloat) -> CGFloat {
+    var result = t / 0.5
+    if (result < 1.0) {
+        return 0.5*result*result
+    } else {
+        result -= 1.0
+        return -0.5 * (result * (result - 2.0) - 1.0)
+    }
+}
+
+
+private struct ActivitySegment {
+    
+    let firstPoint: CGPoint
+    let secondPoint: CGPoint
+    
+    let initialPosition: CGPoint = {
+        var p = CGPoint.zero
+        p.x = (CGFloat(arc4random() % 100) / 100.0)
+        p.y = (CGFloat(arc4random() % 100) / 100.0)
+        p.x = ((p.x - 0.5) * 2.0) + 0.5
+        p.y -= 0.6;
+        return p
+    }()
+    
+    let initialRotation = ((CGFloat(arc4random() % 100) / 100.0) * CGFloat(M_PI))
+    
+    func path(size: CGSize, visibility: CGFloat) -> UIBezierPath {
+        var p1 = initialPosition
+        var p2 = initialPosition
+        
+        p1.interpolateTo(firstPoint, alpha: Scalar(visibility))
+        p2.interpolateTo(secondPoint, alpha: Scalar(visibility))
+        
+        let rotation = initialRotation.interpolatedTo(0.0, alpha: Scalar(visibility))
+        let midX = p1.x + (p2.x - p1.x) * 0.5
+        let midY = p1.y + (p2.y - p1.y) * 0.5
+        
+        p1.x -= midX
+        p2.x -= midX
+        p1.y -= midY
+        p2.y -= midY
+        
+        p1 = CGPointApplyAffineTransform(p1, CGAffineTransformMakeRotation(rotation))
+        p2 = CGPointApplyAffineTransform(p2, CGAffineTransformMakeRotation(rotation))
+        
+        p1.x += midX
+        p2.x += midX
+        p1.y += midY
+        p2.y += midY
+        
+        // we store layout info in relative coords
+        p1.x *= size.width
+        p1.y *= size.height
+        p2.x *= size.width
+        p2.y *= size.height
+        
+        let p = UIBezierPath()
+        p.moveToPoint(p1)
+        p.addLineToPoint(p2)
+        
+        return p
+    }
+    
+    init(firstPoint: CGPoint, secondPoint: CGPoint) {
+        self.firstPoint = firstPoint
+        self.secondPoint = secondPoint
+    }
+}
diff --git a/AdvanceSample-iOS/ActivityViewController.swift b/AdvanceSample-iOS/ActivityViewController.swift
new file mode 100644
index 0000000..772c8c8
--- /dev/null
+++ b/AdvanceSample-iOS/ActivityViewController.swift
@@ -0,0 +1,86 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import UIKit
+
+class ActivityViewController: DemoViewController {
+    
+    let activityView = ActivityView()
+    
+    let slider = UISlider()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        title = "Logo"
+        note = "Drag the slider to disassemble."
+                
+        activityView.flashing = true
+        contentView.addSubview(activityView)
+        
+        contentView.addSubview(slider)
+        slider.alpha = 0.0
+        slider.minimumValue = 0.0
+        slider.maximumValue = 1.0
+        slider.value = 1.0
+        slider.addTarget(self, action: "sliderChanged", forControlEvents: .ValueChanged)
+        slider.minimumTrackTintColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0)
+    }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        let size = min(view.bounds.width, view.bounds.height) * 0.6
+        activityView.bounds.size = CGSize(width: size, height: size)
+        activityView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
+        
+        let sliderInset = CGFloat(32.0)
+        
+        var sliderFrame = CGRect.zero
+        sliderFrame.size.width = view.bounds.width - sliderInset*2.0
+        sliderFrame.size.height = 44.0
+        sliderFrame.origin.x = sliderInset
+        sliderFrame.origin.y = view.bounds.maxY - sliderInset - 44.0
+        slider.frame = sliderFrame
+    }
+    
+    dynamic func sliderChanged() {
+        activityView.assembledAmount = CGFloat(slider.value)
+        activityView.flashing = activityView.assembledAmount == 1.0
+    }
+    
+    override func didEnterFullScreen() {
+        super.didEnterFullScreen()
+        slider.alpha = 1.0
+    }
+    
+    override func didLeaveFullScreen() {
+        super.didLeaveFullScreen()
+        slider.alpha = 0.0
+    }
+
+}
diff --git a/AdvanceSample-iOS/AppDelegate.swift b/AdvanceSample-iOS/AppDelegate.swift
new file mode 100644
index 0000000..200e8a0
--- /dev/null
+++ b/AdvanceSample-iOS/AppDelegate.swift
@@ -0,0 +1,77 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import UIKit
+import Advance
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+    var window: UIWindow?
+
+    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
+        // Override point for customization after application launch.
+        
+        window = UIWindow(frame: UIScreen.mainScreen().bounds)
+        
+        let a = ActivityViewController()
+        let g = GesturesViewController()
+        let s = SpringsViewController()
+        let gravity = GravityViewController()
+        
+        window?.rootViewController = BrowserViewController(viewControllers: [a, gravity, s, g])
+        window?.makeKeyAndVisible()
+        
+        return true
+    }
+
+    func applicationWillResignActive(application: UIApplication) {
+        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+    }
+
+    func applicationDidEnterBackground(application: UIApplication) {
+        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+    }
+
+    func applicationWillEnterForeground(application: UIApplication) {
+        // 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.
+    }
+
+    func applicationDidBecomeActive(application: UIApplication) {
+        // 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.
+    }
+
+    func applicationWillTerminate(application: UIApplication) {
+        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+    }
+
+
+}
+
diff --git a/AdvanceSample-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/AdvanceSample-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..118c98f
--- /dev/null
+++ b/AdvanceSample-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,38 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/Assets.xcassets/Contents.json b/AdvanceSample-iOS/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/AdvanceSample-iOS/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/Assets.xcassets/background-blurred.imageset/Contents.json b/AdvanceSample-iOS/Assets.xcassets/background-blurred.imageset/Contents.json
new file mode 100644
index 0000000..611304d
--- /dev/null
+++ b/AdvanceSample-iOS/Assets.xcassets/background-blurred.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "background-blurred.jpg",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/Assets.xcassets/background-blurred.imageset/background-blurred.jpg b/AdvanceSample-iOS/Assets.xcassets/background-blurred.imageset/background-blurred.jpg
new file mode 100644
index 0000000..273216b
Binary files /dev/null and b/AdvanceSample-iOS/Assets.xcassets/background-blurred.imageset/background-blurred.jpg differ
diff --git a/AdvanceSample-iOS/Assets.xcassets/background.imageset/Contents.json b/AdvanceSample-iOS/Assets.xcassets/background.imageset/Contents.json
new file mode 100644
index 0000000..e97bcdb
--- /dev/null
+++ b/AdvanceSample-iOS/Assets.xcassets/background.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "background.jpg",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/Assets.xcassets/background.imageset/background.jpg b/AdvanceSample-iOS/Assets.xcassets/background.imageset/background.jpg
new file mode 100644
index 0000000..b496354
Binary files /dev/null and b/AdvanceSample-iOS/Assets.xcassets/background.imageset/background.jpg differ
diff --git a/AdvanceSample-iOS/Assets.xcassets/logo.imageset/Contents.json b/AdvanceSample-iOS/Assets.xcassets/logo.imageset/Contents.json
new file mode 100644
index 0000000..8d2722a
--- /dev/null
+++ b/AdvanceSample-iOS/Assets.xcassets/logo.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "logo.pdf"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  },
+  "properties" : {
+    "template-rendering-intent" : "template"
+  }
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/Assets.xcassets/logo.imageset/logo.pdf b/AdvanceSample-iOS/Assets.xcassets/logo.imageset/logo.pdf
new file mode 100644
index 0000000..616793b
Binary files /dev/null and b/AdvanceSample-iOS/Assets.xcassets/logo.imageset/logo.pdf differ
diff --git a/AdvanceSample-iOS/Base.lproj/LaunchScreen.storyboard b/AdvanceSample-iOS/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..90d6157
--- /dev/null
+++ b/AdvanceSample-iOS/Base.lproj/LaunchScreen.storyboard
@@ -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="9532" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
+                        <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <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="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+</document>
diff --git a/AdvanceSample-iOS/BrowserView.swift b/AdvanceSample-iOS/BrowserView.swift
new file mode 100644
index 0000000..649a4c0
--- /dev/null
+++ b/AdvanceSample-iOS/BrowserView.swift
@@ -0,0 +1,508 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import UIKit
+import Advance
+
+class BrowserItem: NSObject {
+    
+    let center = Spring(value: CGPoint.zero)
+    let transform = Spring(value: SimpleTransform())
+    let size = Spring(value: CGSize.zero)
+    
+    let tapRecognizer = UITapGestureRecognizer()
+
+    let recognizer = DirectManipulationGestureRecognizer()
+    let panRecognizer = UIPanGestureRecognizer()
+    
+    var anchorPoint = CGPoint(x: 0.5, y: 0.5) {
+        didSet { browserView?.setNeedsLayout() }
+    }
+    
+    let view: UIView = {
+        let v = UIView(frame: CGRect.zero)
+        v.backgroundColor = UIColor.blueColor()
+        v.layer.cornerRadius = 6.0
+        v.clipsToBounds = true
+        return v
+    }()
+    
+    private var transformWhenGestureBegan = SimpleTransform()
+    private var centerWhenGestureBegan = CGPoint.zero
+    private (set) var gestureInProgress = false
+    
+    var frame: CGRect {
+        var f = CGRect.zero
+        f.size = size.value
+        f.origin.x -= anchorPoint.x * f.size.width
+        f.origin.y -= anchorPoint.y * f.size.height
+        f = CGRectApplyAffineTransform(f, transform.value.affineTransform)
+        f.origin.x += center.value.x
+        f.origin.y += center.value.y
+        return f
+    }
+    
+    private (set) weak var browserView: BrowserView? = nil
+    
+    override init() {
+        super.init()
+        
+        center.configuration.threshold = 0.1
+        center.configuration.tension = 120.0
+        center.configuration.damping = 27.0
+        center.changed.observe { [unowned self] (p) -> Void in
+            self.browserView?.setNeedsLayout()
+        }
+        
+        transform.configuration.threshold = 0.001
+        transform.changed.observe { [unowned self] (p) -> Void in
+            self.browserView?.setNeedsLayout()
+        }
+        
+        size.configuration.threshold = 0.1
+        size.changed.observe { [unowned self] (p) -> Void in
+            self.browserView?.setNeedsLayout()
+        }
+        
+        tapRecognizer.addTarget(self, action: "tap")
+        view.addGestureRecognizer(tapRecognizer)
+        
+        recognizer.addTarget(self, action: "gesture:")
+        view.addGestureRecognizer(recognizer)
+        
+        panRecognizer.addTarget(self, action: "pan:")
+        panRecognizer.delegate = self
+        view.addGestureRecognizer(panRecognizer)
+    }
+    
+    private dynamic func tap() {
+        if browserView?.fullScreenItem != self {
+            browserView?.enterFullScreen(self)
+        }
+    }
+    
+    private dynamic func gesture(recognizer: DirectManipulationGestureRecognizer) {
+        switch recognizer.state {
+        case .Began:
+            
+            let gestureLocation = recognizer.locationInView(view)
+            let newCenter = view.superview!.convertPoint(gestureLocation, fromView: view)
+            center.reset(newCenter)
+            
+            var anchorPoint = gestureLocation
+            anchorPoint.x /= view.bounds.width
+            anchorPoint.y /= view.bounds.height
+            self.anchorPoint = anchorPoint
+            
+            gestureInProgress = true
+            centerWhenGestureBegan = center.value
+            transformWhenGestureBegan = transform.value
+            transform.reset(transform.value)
+        case .Changed:
+            var t = transformWhenGestureBegan
+            t.rotation += recognizer.rotation
+            t.scale *= recognizer.scale
+            transform.reset(t)
+            
+            var c = centerWhenGestureBegan
+            c.x += recognizer.translationInView(view.superview).x
+            c.y += recognizer.translationInView(view.superview).y
+            center.reset(c)
+            break
+        case .Ended, .Cancelled:
+            
+            // Reset the anchor point
+            let mid = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
+            let newCenter = view.superview!.convertPoint(mid, fromView: view)
+            center.reset(newCenter)
+            anchorPoint = CGPoint(x: 0.5, y: 0.5)
+            
+            var velocity = SimpleTransform.zero
+            velocity.scale = recognizer.scaleVelocity
+            velocity.rotation = recognizer.rotationVelocity
+            var config = SpringConfiguration()
+            config.threshold = 0.001
+            transform.velocity = velocity
+            
+            let centerVel = recognizer.translationVelocityInView(view.superview)
+            var centerConfig = SpringConfiguration()
+            centerConfig.tension = 40.0
+            centerConfig.damping = 5.0
+            center.velocity = centerVel
+            
+            gestureInProgress = false
+            
+            if recognizer.scaleVelocity <= 0.0 && transform.value.scale < 1.0 && browserView?.fullScreenItem == self {
+                browserView?.leaveFullScreen()
+            } else if recognizer.scaleVelocity >= 0.0 && transform.value.scale > 0.75 && browserView?.fullScreenItem != self {
+                browserView?.enterFullScreen(self)
+            } else {
+                browserView?.updateAllItems(true)
+            }
+            break
+        default:
+            break
+        }
+    }
+    
+    dynamic func pan(recognizer: UIPanGestureRecognizer) {
+        switch recognizer.state {
+        case .Began:
+            gestureInProgress = true
+            centerWhenGestureBegan = center.value
+            center.reset(center.value)
+            break
+        case .Changed:
+            var c = centerWhenGestureBegan
+            c.y += recognizer.translationInView(view.superview).y
+            center.reset(c)
+            break
+        case .Ended:
+            gestureInProgress = false
+            center.velocity.y = recognizer.velocityInView(view.superview).y
+            if abs(recognizer.translationInView(view.superview).y) > 10.0 {
+                browserView?.leaveFullScreen()
+            } else {
+                browserView?.updateAllItems(true)
+            }
+        default:
+            break
+        }
+    }
+}
+
+extension BrowserItem: UIGestureRecognizerDelegate {
+    func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
+        if gestureRecognizer == panRecognizer {
+            if browserView?.fullScreenItem != self {
+                return false
+            }
+            
+            let translation = panRecognizer.translationInView(view)
+            if abs(translation.x) > abs(translation.y) {
+                return false
+            }
+        }
+        return true
+    }
+}
+
+func ==(lhs: BrowserItem, rhs: BrowserItem) -> Bool {
+    return lhs === rhs
+}
+
+protocol BrowserViewDelegate: class {
+    func browserView(browserView: BrowserView, didShowItem item: BrowserItem)
+    func browserView(browserView: BrowserView, didHideItem item: BrowserItem)
+    func browserView(browserView: BrowserView, didEnterFullScreenForItem item: BrowserItem)
+    func browserView(browserView: BrowserView, didLeaveFullScreenForItem item: BrowserItem)
+    func browserViewDidScroll(browserView: BrowserView)
+}
+
+class BrowserView: UIView {
+    
+    weak var delegate: BrowserViewDelegate? = nil
+    
+    private let paginationRatio: CGFloat = 0.68
+    
+    private let index = Animatable(value: CGFloat.zero)
+    
+    private var panInProgress = false
+    private var indexWhenPanBegan: CGFloat = 0.0
+    
+    private var visibleItems: Set<BrowserItem> = []
+    
+    private let panRecognizer = UIPanGestureRecognizer()
+    
+    private var lastLayoutSize = CGSize.zero
+    
+    private var fullScreenItem: BrowserItem? = nil
+    
+    private let coverVisibilty: Spring<CGFloat> = {
+        let s = Spring(value: CGFloat(1.0))
+        s.configuration.threshold = 0.001
+        s.configuration.tension = 220.0
+        s.configuration.damping = 28.0
+        return s
+    }()
+    
+    var coverView: CoverView? = nil {
+        didSet {
+            guard coverView !== oldValue else { return }
+            oldValue?.removeFromSuperview()
+            if let v = coverView {
+                addSubview(v)
+            }
+            setNeedsLayout()
+        }
+    }
+    
+    
+    var currentIndex: CGFloat {
+        return index.value
+    }
+    
+    var items: [BrowserItem] = [] {
+        willSet {
+            leaveFullScreen()
+            for item in visibleItems {
+                hideItem(item)
+            }
+            
+            for item in items {
+                item.browserView = nil
+            }
+        }
+        didSet {
+            for item in items {
+                item.browserView = self
+            }
+            updateAllItems(false)
+            setNeedsLayout()
+        }
+    }
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        
+        index.changed.observe { [unowned self] (idx) -> Void in
+            self.setNeedsLayout()
+            self.delegate?.browserViewDidScroll(self)
+        }
+        
+        coverVisibilty.changed.observe { [unowned self] (v) in
+            self.setNeedsLayout()
+        }
+        
+        panRecognizer.addTarget(self, action: "pan:")
+        panRecognizer.delegate = self
+        addGestureRecognizer(panRecognizer)
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        guard bounds.size != CGSize.zero else { return }
+        
+        if lastLayoutSize != bounds.size {
+            lastLayoutSize = bounds.size
+            updateAllItems(false)
+        }
+        
+        var b = bounds
+        b.origin.x = 0.0 + (index.value * b.width * paginationRatio)
+        if bounds != b {
+            bounds = b
+        }
+        
+        // bring centermost item to front
+        var closestDistance = CGFloat.max
+        var closestItem: BrowserItem? = nil
+        for item in visibleItems {
+            let distance = abs(item.center.value.x - bounds.midX)
+            if distance < closestDistance {
+                closestDistance = distance
+                closestItem = item
+            }
+        }
+        if let item = closestItem {
+            bringSubviewToFront(item.view)
+        }
+        
+        updateAllItems(true)
+        updateVisibleItems()
+        
+        
+        // If present, update the cover view.
+        if let cv = coverView {
+            let cvVis = coverVisibilty.value
+            
+            let initialCenter = CGPoint(x: bounds.midX, y: bounds.midY)
+            let finalCenter = CGPoint(x: bounds.midX, y: bounds.height * 0.3 * 0.25)
+            cv.center = initialCenter.interpolatedTo(finalCenter, alpha: 1.0-Scalar(cvVis))
+            
+            let t = CGAffineTransformMakeScale(0.7 + cvVis*0.3, 0.7 + cvVis*0.3)
+            cv.transform = t
+            
+            cv.URLVisibility = CGFloat(cvVis)
+            cv.alpha = 0.5 + CGFloat(cvVis*0.5)
+        }
+        
+        var coverVisibility = 1.0 - currentIndex
+        coverVisibility = min(coverVisibility, 1.0)
+        coverVisibility = max(coverVisibility, 0.0)
+        coverVisibilty.target = coverVisibility
+    }
+    
+    private func updateAllItems(animated: Bool) {
+        for i in 0..<items.count {
+            updateItemAtIndex(i, animated: animated)
+        }
+    }
+    
+    private func updateItemAtIndex(index: Int, animated: Bool) {
+        let item = items[index]
+        guard item.gestureInProgress == false else { return }
+        
+        var center = CGPoint.zero
+        center.y = bounds.midY
+        center.x = bounds.width/2.0 + (CGFloat(index+1) * bounds.width * paginationRatio)
+        let size = bounds.size
+        
+        var transform = SimpleTransform()
+        
+        var distance = abs(center.x - bounds.midX)
+        distance = min(distance, bounds.width*paginationRatio) / bounds.width*paginationRatio
+        transform.scale = 0.7 - (0.3 * distance)
+        
+        let tension = 60.0 + Scalar(distance) * 40.0
+        
+        item.transform.configuration.tension = tension
+        item.transform.configuration.damping = 18.0
+        
+        if item == fullScreenItem {
+            transform.scale = 1.0
+            item.transform.configuration.tension = 160.0
+            item.transform.configuration.damping = 28.0
+        }
+        
+        if animated {
+            item.center.target = center
+            item.size.target = size
+            item.transform.target = transform
+        } else {
+            item.center.reset(center)
+            item.size.reset(size)
+            item.transform.reset(transform)
+        }
+    }
+
+    private func updateVisibleItems() {
+        
+        for item in items {
+            let isVisible = visibleItems.contains(item)
+            let shouldBeVisible = item.frame.intersects(bounds)
+            if isVisible && !shouldBeVisible {
+                hideItem(item)
+            } else if !isVisible && shouldBeVisible {
+                showItem(item)
+            }
+            
+            if shouldBeVisible {
+                updateViewForItemAtIndex(items.indexOf(item)!)
+            }
+        }
+        
+    }
+    
+    private func showItem(item: BrowserItem) {
+        assert(item.browserView == self)
+        assert(visibleItems.contains(item) == false)
+        visibleItems.insert(item)
+        updateViewForItemAtIndex(items.indexOf(item)!)
+        addSubview(item.view)
+        delegate?.browserView(self, didShowItem: item)
+    }
+
+    private func updateViewForItemAtIndex(index: Int) {
+        let item = items[index]
+        item.view.bounds = CGRect(origin: CGPoint.zero, size: item.size.value)
+        item.view.center = item.center.value
+        item.view.layer.anchorPoint = item.anchorPoint
+        item.view.transform = item.transform.value.affineTransform
+    }
+    
+    private func hideItem(item: BrowserItem) {
+        assert(item.browserView == self)
+        assert(visibleItems.contains(item))
+        visibleItems.remove(item)
+        item.view.removeFromSuperview()
+        delegate?.browserView(self, didHideItem: item)
+    }
+    
+    func enterFullScreen(item: BrowserItem) {
+        assert(item.browserView == self)
+        leaveFullScreen()
+        fullScreenItem = item
+        updateAllItems(true)
+        index.animateTo(CGFloat(items.indexOf(item)! + 1))
+        delegate?.browserView(self, didEnterFullScreenForItem: item)
+    }
+    
+    func leaveFullScreen() {
+        guard let item = fullScreenItem else { return }
+        fullScreenItem = nil
+        updateAllItems(true)
+        delegate?.browserView(self, didLeaveFullScreenForItem: item)
+    }
+    
+    private dynamic func pan(recognizer: UIPanGestureRecognizer) {
+        switch recognizer.state {
+        case .Began:
+            panInProgress = true
+            indexWhenPanBegan = index.value
+            index.cancelAnimation()
+        case .Changed:
+            index.value = indexWhenPanBegan - (recognizer.translationInView(self).x / bounds.width * paginationRatio)
+            break
+        case .Ended, .Cancelled:
+            panInProgress = false
+            let vel = -recognizer.velocityInView(self).x / bounds.width * paginationRatio
+            var destIndex = round(index.value + (vel/5.0))
+            if destIndex == round(index.value) {
+                if vel < 0.0 {
+                    destIndex -= 1.0
+                } else if vel > 0.0 {
+                    destIndex += 1.0
+                }
+            }
+            destIndex = min(destIndex, CGFloat(items.count))
+            destIndex = max(destIndex, CGFloat(0.0))
+            var config = SpringConfiguration()
+            config.tension = 120.0
+            config.damping = 20.0
+            config.threshold = 0.001
+            index.springTo(destIndex, initialVelocity: vel, configuration: config, completion: nil)
+        default:
+            break
+        }
+    }
+    
+}
+
+extension BrowserView: UIGestureRecognizerDelegate {
+    override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
+        if gestureRecognizer == panRecognizer && fullScreenItem != nil {
+            return false
+        }
+        return true
+    }
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/BrowserViewController.swift b/AdvanceSample-iOS/BrowserViewController.swift
new file mode 100644
index 0000000..e1ca914
--- /dev/null
+++ b/AdvanceSample-iOS/BrowserViewController.swift
@@ -0,0 +1,150 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import UIKit
+import Advance
+
+private final class DemoItem: BrowserItem {
+    let viewController: DemoViewController
+    init(viewController: DemoViewController) {
+        self.viewController = viewController
+        super.init()
+    }
+}
+
+final class BrowserViewController: UIViewController {
+    
+    let viewControllers: [DemoViewController]
+    
+    let backgroundImageView = UIImageView(frame: CGRect.zero)
+    let blurredBackgroundImageView = UIImageView(frame: CGRect.zero)
+    let backgroundDimmingView = UIView(frame: CGRect.zero)
+    
+    let blurSpring = Spring(value: CGFloat.zero)
+    
+    let browserView = BrowserView(frame: CGRect.zero)
+    
+    required init(viewControllers: [DemoViewController]) {
+        self.viewControllers = viewControllers
+        super.init(nibName: nil, bundle: nil)
+        
+        var cfg = SpringConfiguration()
+        cfg.threshold = 0.001
+        cfg.tension = 60.0
+        cfg.damping = 26.0
+        blurSpring.configuration = cfg
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        
+        
+        backgroundImageView.contentMode = .ScaleAspectFill
+        backgroundImageView.image = UIImage(named: "background")
+        view.addSubview(backgroundImageView)
+        
+        blurredBackgroundImageView.contentMode = .ScaleAspectFill
+        blurredBackgroundImageView.image = UIImage(named: "background-blurred")
+        blurredBackgroundImageView.alpha = 0.0
+        view.addSubview(blurredBackgroundImageView)
+        
+        backgroundDimmingView.backgroundColor = UIColor.blackColor()
+        backgroundDimmingView.alpha = 0.3
+        view.addSubview(backgroundDimmingView)
+        
+        let cv = CoverView(frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 300.0))
+        browserView.coverView = cv
+            
+        
+        
+        view.addSubview(browserView)
+        browserView.delegate = self
+        
+        browserView.items = viewControllers.map({ (vc) -> DemoItem in
+            return DemoItem(viewController: vc)
+        })
+        
+        blurSpring.changed.observe { [unowned self] (b) in
+            self.blurredBackgroundImageView.alpha = b
+        }
+    }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        backgroundImageView.frame = view.bounds
+        blurredBackgroundImageView.frame = view.bounds
+        backgroundDimmingView.frame = view.bounds
+        browserView.frame = view.bounds
+    }
+    
+    override func prefersStatusBarHidden() -> Bool {
+        return true
+    }
+}
+
+extension BrowserViewController: BrowserViewDelegate {
+    func browserView(browserView: BrowserView, didShowItem item: BrowserItem) {
+        guard let item = item as? DemoItem else { fatalError() }
+        assert(item.viewController.parentViewController != self)
+        addChildViewController(item.viewController)
+        item.viewController.view.frame = item.view.bounds
+        item.view.addSubview(item.viewController.view)
+        item.viewController.didMoveToParentViewController(self)
+    }
+    
+    func browserView(browserView: BrowserView, didHideItem item: BrowserItem) {
+        guard let item = item as? DemoItem else { fatalError() }
+        assert(item.viewController.parentViewController == self)
+        item.viewController.willMoveToParentViewController(nil)
+        item.viewController.view.removeFromSuperview()
+        item.viewController.removeFromParentViewController()
+    }
+    
+    func browserView(browserView: BrowserView, didEnterFullScreenForItem item: BrowserItem) {
+        guard let item = item as? DemoItem else { fatalError() }
+        item.viewController.fullScreen = true
+
+    }
+    
+    func browserView(browserView: BrowserView, didLeaveFullScreenForItem item: BrowserItem) {
+        guard let item = item as? DemoItem else { fatalError() }
+        item.viewController.fullScreen = false
+    }
+    
+    func browserViewDidScroll(browserView: BrowserView) {
+        var blurAmount = browserView.currentIndex
+        blurAmount = min(blurAmount, 1.0)
+        blurAmount = max(blurAmount, 0.0)
+        blurSpring.target = blurAmount
+    }
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/CoverView.swift b/AdvanceSample-iOS/CoverView.swift
new file mode 100644
index 0000000..14d3dad
--- /dev/null
+++ b/AdvanceSample-iOS/CoverView.swift
@@ -0,0 +1,76 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import UIKit
+
+final class CoverView: UIView {
+    
+    let logoView: UIImageView
+    
+    let urlLabel: UILabel
+    
+    var URLVisibility: CGFloat = 1.0 {
+        didSet {
+            urlLabel.alpha = URLVisibility
+        }
+    }
+    
+    override init(frame: CGRect) {
+        logoView = UIImageView(image: UIImage(named: "logo"))
+        logoView.tintColor = UIColor.whiteColor()
+        logoView.sizeToFit()
+        
+        urlLabel = UILabel(frame: CGRect.zero)
+        
+        var attribs: [String: AnyObject] = [:]
+        attribs[NSFontAttributeName] = UIFont.systemFontOfSize(12.0, weight: UIFontWeightRegular)
+        attribs[NSForegroundColorAttributeName] = UIColor.whiteColor()
+        
+        // attribs[NSUnderlineStyleAttributeName] = NSUnderlineStyle.StyleSingle.rawValue
+        
+        urlLabel.attributedText = NSAttributedString(string: "github.com/storehouse/Advance", attributes: attribs)
+        urlLabel.sizeToFit()
+        
+        super.init(frame: frame)
+        
+        addSubview(logoView)
+        addSubview(urlLabel)
+
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        logoView.center = CGPoint(x: bounds.midX, y: bounds.midY)
+        urlLabel.center = CGPoint(x: bounds.midX, y: logoView.frame.maxY + 4.0 + urlLabel.bounds.height/2.0)
+    }
+    
+}
diff --git a/AdvanceSample-iOS/DemoViewController.swift b/AdvanceSample-iOS/DemoViewController.swift
new file mode 100644
index 0000000..419f9fa
--- /dev/null
+++ b/AdvanceSample-iOS/DemoViewController.swift
@@ -0,0 +1,128 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import UIKit
+
+class DemoViewController: UIViewController {
+    
+    var note: String {
+        get { return noteLabel.text ?? "" }
+        set {
+            noteLabel.text = newValue
+            view.setNeedsLayout()
+        }
+    }
+    
+    override var title: String? {
+        didSet {
+            titleLabel.text = title
+            view.setNeedsLayout()
+        }
+    }
+    
+    let contentView = UIView()
+    
+    private let titleLabel = UILabel()
+    
+    private let noteLabel = UILabel()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        view.backgroundColor = UIColor(white: 1.0, alpha: 1.0)
+        
+        contentView.alpha = 0.4
+        contentView.layer.allowsGroupOpacity = false
+        contentView.userInteractionEnabled = false
+        contentView.frame = view.bounds
+        view.addSubview(contentView)
+        
+        titleLabel.font = UIFont.systemFontOfSize(32.0, weight: UIFontWeightMedium)
+        titleLabel.textColor = UIColor.darkGrayColor()
+        titleLabel.textAlignment = .Center
+        titleLabel.numberOfLines = 0
+        view.addSubview(titleLabel)
+        
+        noteLabel.font = UIFont.systemFontOfSize(16.0, weight: UIFontWeightThin)
+        noteLabel.textColor = UIColor.darkGrayColor()
+        noteLabel.textAlignment = .Center
+        noteLabel.numberOfLines = 0
+        noteLabel.alpha = 0.0
+        view.addSubview(noteLabel)
+    }
+    
+    final var fullScreen = false {
+        didSet {
+            guard fullScreen != oldValue else { return }
+            
+            UIView.animateWithDuration(0.4) {
+                if self.fullScreen {
+                    self.didEnterFullScreen()
+                } else {
+                    self.didLeaveFullScreen()
+                }
+            }
+        }
+    }
+    
+    func didEnterFullScreen() {
+        noteLabel.alpha = 1.0
+        contentView.alpha = 1.0
+        contentView.userInteractionEnabled = true
+        titleLabel.alpha = 0.0
+    }
+    
+    func didLeaveFullScreen() {
+        noteLabel.alpha = 0.0
+        contentView.alpha = 0.5
+        contentView.userInteractionEnabled = false
+        titleLabel.alpha = 1.0
+    }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        
+        contentView.frame = view.bounds
+        
+        let labelHeight = noteLabel.sizeThatFits(CGSize(width: view.bounds.width-64.0, height: CGFloat.max)).height
+        var labelFrame = CGRect.zero
+        labelFrame.origin.x = 32.0
+        labelFrame.origin.y = 32.0
+        labelFrame.size.width = view.bounds.width - 64.0
+        labelFrame.size.height = labelHeight
+        noteLabel.frame = labelFrame
+        
+        let titleHeight = titleLabel.sizeThatFits(CGSize(width: view.bounds.width-64.0, height: CGFloat.max)).height
+        var titleFrame = CGRect.zero
+        titleFrame.origin.x = 32.0
+        titleFrame.origin.y = 32.0
+        titleFrame.size.width = view.bounds.width - 64.0
+        titleFrame.size.height = titleHeight
+        titleLabel.frame = titleFrame
+    }
+    
+}
diff --git a/AdvanceSample-iOS/DirectManipulationGestureRecognizer.swift b/AdvanceSample-iOS/DirectManipulationGestureRecognizer.swift
new file mode 100644
index 0000000..8207d5d
--- /dev/null
+++ b/AdvanceSample-iOS/DirectManipulationGestureRecognizer.swift
@@ -0,0 +1,363 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import Foundation
+import UIKit.UIGestureRecognizerSubclass
+
+private let π = CGFloat(M_PI)
+
+
+public final class DirectManipulationGestureRecognizer: UIGestureRecognizer {
+    
+    public enum PinchDirection {
+        case In
+        case Out
+        case Any
+    }
+    
+    public var requiredPinchDirection: PinchDirection = .Any
+    
+    // The state of the touches when we first saw them
+    private var preliminaryState: GestureState = GestureState()
+    
+    // The state of the touches when the recognizer successfully began
+    private var initialState: GestureState = GestureState()
+    
+    // The current and previous states, for reference.
+    private var previousState: GestureState = GestureState()
+    private var currentState: GestureState = GestureState()
+    
+    // As per the UIKit documentation, we don't retain the touches. But
+    // we do remember their address so that we can compare against them as needed.
+    private var firstTouchPointer: UnsafePointer<Void>? = nil
+    private var secondTouchPointer: UnsafePointer<Void>? = nil
+    
+    private var previousCumulativeAngle: CGFloat = 0.0
+    private var cumulativeAngle: CGFloat = 0.0
+    
+    private var lastUpdateTime: NSTimeInterval = 0.0
+    private var lastUpdateDuration: NSTimeInterval = 0.0
+    
+    public override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
+        super.touchesBegan(touches, withEvent: event)
+        guard state == .Possible else { return }
+        guard touches.count > 0 else { return }
+        
+        var touches = touches
+        
+        while touches.count > 0 {
+            let t = touches.removeFirst()
+            if firstTouchPointer == nil {
+                firstTouchPointer = unsafeAddressOf(t)
+                let state = TouchState(t: t)
+                preliminaryState.firstTouchState = state
+                initialState.firstTouchState = state
+                previousState.firstTouchState = state
+                currentState.firstTouchState = state
+            } else if secondTouchPointer == nil {
+                secondTouchPointer = unsafeAddressOf(t)
+                let state = TouchState(t: t)
+                preliminaryState.secondTouchState = state
+                initialState.secondTouchState = state
+                previousState.secondTouchState = state
+                currentState.secondTouchState = state
+            } else {
+                continue
+            }
+        }
+        
+        if requiredPinchDirection == .Any && firstTouchPointer != nil && secondTouchPointer != nil {
+            begin()
+        }
+    }
+    
+    public override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
+        super.touchesMoved(touches, withEvent: event)
+        guard state == .Possible || state == .Began || state == .Changed else { return }
+        
+        previousState = currentState
+        
+        for t in touches {
+            if unsafeAddressOf(t) == firstTouchPointer {
+                currentState.firstTouchState = TouchState(t: t)
+            }
+            
+            if unsafeAddressOf(t) == secondTouchPointer {
+                currentState.secondTouchState = TouchState(t: t)
+            }
+        }
+        
+        // If either of the touches have just begun or ended, there's a good chance 
+        // that we just began or ended the gesture. It turns out that setting the state
+        // to .Changed stomps on the notification of the gesture beginning – so
+        // from the target's perspective, the recognizer just start claiming to
+        // have changed without going through a .Began phase. We can just check
+        // to make sure that neither touch has just started or ended to avoid this.
+        if currentState.firstTouchState.phase == .Began || currentState.secondTouchState.phase == .Began {
+            return
+        }
+        
+        if currentState.firstTouchState.phase == .Ended || currentState.secondTouchState.phase == .Ended {
+            return
+        }
+        
+        if currentState.firstTouchState.phase == .Cancelled || currentState.secondTouchState.phase == .Cancelled {
+            return
+        }
+        
+        if state == .Possible {
+            guard firstTouchPointer != nil && secondTouchPointer != nil else { return }
+            
+            // How far the user has pinched since we first saw the touches.
+            let pinchDistance = currentState.distanceBetween - preliminaryState.distanceBetween
+            
+            switch requiredPinchDirection {
+            case .Any:
+                begin()
+            case .In:
+                if pinchDistance > 0.0 {
+                    begin()
+                } else if pinchDistance < 0.0 {
+                    state = .Failed
+                }
+            case .Out:
+                if pinchDistance < 0.0 {
+                    begin()
+                } else if pinchDistance > 0.0 {
+                    state = .Failed
+                }
+            }
+            
+            if state == .Possible && currentState.distanceTo(preliminaryState) > 4.0 {
+                state = .Failed
+            }
+            
+            return
+        }
+        
+        var angleDelta = currentState.angle - previousState.angle
+        if (angleDelta > π) {
+            angleDelta -= π*2.0;
+        } else if (angleDelta < -π) {
+            angleDelta += π*2.0;
+        }
+        
+        previousCumulativeAngle = cumulativeAngle
+        cumulativeAngle += angleDelta
+        
+        let currentTime = CACurrentMediaTime()
+        lastUpdateDuration = currentTime - lastUpdateTime
+        lastUpdateTime = currentTime
+        
+        assert(state == .Began || state == .Changed)
+        state = .Changed
+    }
+    
+    public override func touchesCancelled(touches: Set<UITouch>, withEvent event: UIEvent) {
+        super.touchesCancelled(touches, withEvent: event)
+        
+        
+        for t in touches {
+            if unsafeAddressOf(t) == firstTouchPointer || unsafeAddressOf(t) == secondTouchPointer {
+                if state == .Possible {
+                    state = .Failed
+                } else if state == .Began || state == .Changed {
+                    state = .Cancelled
+                }
+                return
+            }
+        }
+    }
+    
+    public override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent) {
+        super.touchesEnded(touches, withEvent: event)
+        
+        for t in touches {
+            if unsafeAddressOf(t) == firstTouchPointer || unsafeAddressOf(t) == secondTouchPointer {
+                if state == .Possible {
+                    state = .Failed
+                } else if state == .Began || state == .Changed {
+                    state = .Ended
+                }
+                return
+            }
+        }
+    }
+    
+    private func begin() {
+        guard state == .Possible else { fatalError() }
+        initialState = currentState
+        previousState = currentState
+        cumulativeAngle = 0.0
+        previousCumulativeAngle = 0.0
+        lastUpdateTime = CACurrentMediaTime()
+        lastUpdateDuration = 0.0
+        state = .Began
+    }
+    
+    override public func reset() {
+        super.reset()
+        
+        firstTouchPointer = nil
+        secondTouchPointer = nil
+        
+        preliminaryState = GestureState()
+        initialState = GestureState()
+        previousState = GestureState()
+        currentState = GestureState()
+    }
+    
+    
+    public func translationInView(view: UIView?) -> CGPoint {
+        let current = currentState.convertedToView(view)
+        let initial = initialState.convertedToView(view)
+        return current.center - initial.center
+    }
+    
+    public func translationVelocityInView(view: UIView?) -> CGPoint {
+        guard active else { return CGPoint.zero }
+        guard lastUpdateDuration > 0.0 else { return CGPoint.zero }
+        let current = currentState.convertedToView(view)
+        let previous = previousState.convertedToView(view)
+        var vel = current.center - previous.center
+        vel.x /= CGFloat(lastUpdateDuration)
+        vel.y /= CGFloat(lastUpdateDuration)
+        return vel
+    }
+    
+    public var rotation: CGFloat {
+        guard active else { return 0.0 }
+        return cumulativeAngle
+    }
+    
+    public var rotationVelocity: CGFloat {
+        guard active else { return 0.0 }
+        guard lastUpdateDuration > 0.0 else { return 0.0 }
+        let delta = cumulativeAngle - previousCumulativeAngle
+        return delta / CGFloat(lastUpdateDuration)
+    }
+    
+    public var scale: CGFloat {
+        guard active else { return 1.0 }
+        return currentState.distanceBetween / initialState.distanceBetween
+    }
+    
+    public var scaleVelocity: CGFloat {
+        guard active else { return 0.0 }
+        guard lastUpdateDuration > 0.0 else { return 0.0 }
+        let current = currentState.distanceBetween / initialState.distanceBetween
+        let previous = previousState.distanceBetween / initialState.distanceBetween
+        return ((current - previous) / CGFloat(lastUpdateDuration))
+    }
+    
+    private var active: Bool {
+        return state == .Began || state == .Changed || state == .Ended
+    }
+}
+
+
+private extension DirectManipulationGestureRecognizer {
+    struct GestureState {
+        var firstTouchState: TouchState = TouchState()
+        var secondTouchState: TouchState = TouchState()
+        
+        
+        func convertedToView(view: UIView?) -> GestureState {
+            var s = self
+            s.firstTouchState = s.firstTouchState.convertedToView(view)
+            s.secondTouchState = s.secondTouchState.convertedToView(view)
+            return s
+        }
+        
+        func distanceTo(state: GestureState) -> CGFloat {
+            return (state.center - center).distance
+        }
+        
+        var distanceBetween: CGFloat {
+            return delta.distance
+        }
+        
+        var delta: CGPoint {
+            return secondTouchState.location - firstTouchState.location
+        }
+        
+        var angle: CGFloat {
+            return delta.angle
+        }
+        
+        var center: CGPoint {
+            var p = CGPoint.zero
+            p.x = firstTouchState.location.x + ((secondTouchState.location.x - firstTouchState.location.x) * 0.5)
+            p.y = firstTouchState.location.y + ((secondTouchState.location.y - firstTouchState.location.y) * 0.5)
+            return p
+        }
+    }
+    
+    struct TouchState {
+        var location: CGPoint = CGPoint.zero
+        var phase: UITouchPhase = UITouchPhase.Began
+        
+        init() {}
+        
+        init(t: UITouch) {
+            location = t.locationInView(nil)
+            phase = t.phase
+        }
+        
+        func convertedToView(view: UIView?) -> TouchState {
+            var s = self
+            if let view = view {
+                s.location = view.convertPoint(s.location, fromView: nil)
+            }
+            return s
+        }
+        
+        func distanceTo(state: TouchState) -> CGFloat {
+            return (state.location - location).distance
+        }
+    }
+}
+
+private extension CGPoint {
+    
+    var angle: CGFloat {
+        return atan2(y, x);
+    }
+    
+    var distance: CGFloat {
+        return sqrt((x*x) + (y*y))
+    }
+    
+}
+
+private func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
+    var result = CGPoint.zero
+    result.x = lhs.x - rhs.x
+    result.y = lhs.y - rhs.y
+    return result
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/GestureView.swift b/AdvanceSample-iOS/GestureView.swift
new file mode 100644
index 0000000..093807c
--- /dev/null
+++ b/AdvanceSample-iOS/GestureView.swift
@@ -0,0 +1,126 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import UIKit
+import Advance
+
+final class GestureView: UIView {
+    
+    let animatableCenter = Animatable(value: CGPoint.zero)
+    ;let animatableTransform = Animatable(value: SimpleTransform())
+    
+    private var centerWhenGestureBegan = CGPoint.zero
+    private var transformWhenGestureBegan = SimpleTransform.zero
+    
+    private let recognizer = DirectManipulationGestureRecognizer()
+
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        
+        backgroundColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0)
+        
+        recognizer.addTarget(self, action: "manipulate:")
+        addGestureRecognizer(recognizer)
+        
+        animatableCenter.changed.observe { [weak self] (c) -> Void in
+            self?.center = c
+        }
+        
+        animatableTransform.changed.observe { [weak self] (t) -> Void in
+            self?.transform = t.affineTransform
+        }
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override var frame: CGRect {
+        didSet {
+            animatableCenter.value = center
+        }
+    }
+    
+    private dynamic func manipulate(recognizer: DirectManipulationGestureRecognizer) {
+        switch recognizer.state {
+        case .Began:
+            
+            // Take the anchor point into consideration
+            let gestureLocation = recognizer.locationInView(self)
+            let newCenter = superview!.convertPoint(gestureLocation, fromView: self)
+            animatableCenter.value = newCenter
+            
+            var anchorPoint = gestureLocation
+            anchorPoint.x /= bounds.width
+            anchorPoint.y /= bounds.height
+            layer.anchorPoint = anchorPoint
+            
+            animatableTransform.cancelAnimation()
+            animatableCenter.cancelAnimation()
+            centerWhenGestureBegan = animatableCenter.value
+            transformWhenGestureBegan = animatableTransform.value
+            break
+        case .Changed:
+            var t = transformWhenGestureBegan
+            t.rotation += recognizer.rotation
+            t.scale *= recognizer.scale
+            animatableTransform.value = t
+            
+            var center = centerWhenGestureBegan
+            center.x += recognizer.translationInView(superview).x
+            center.y += recognizer.translationInView(superview).y
+            animatableCenter.value = center
+            
+            break
+        case .Ended, .Cancelled:
+            // Reset the anchor point
+            let mid = CGPoint(x: bounds.midX, y: bounds.midY)
+            let newCenter = superview!.convertPoint(mid, fromView: self)
+            animatableCenter.value = newCenter
+            layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
+            
+            var velocity = SimpleTransform.zero
+            velocity.scale = recognizer.scaleVelocity
+            velocity.rotation = recognizer.rotationVelocity
+            var config = SpringConfiguration()
+            config.threshold = 0.001
+            animatableTransform.springTo(SimpleTransform(), initialVelocity: velocity, configuration: config)
+            
+            let centerVel = recognizer.translationVelocityInView(superview)
+            var centerConfig = SpringConfiguration()
+            centerConfig.tension = 40.0
+            centerConfig.damping = 5.0
+            let c = CGPoint(x: superview!.bounds.midX, y: superview!.bounds.midY)
+            animatableCenter.springTo(c, initialVelocity: centerVel, configuration: centerConfig)
+            break
+        default:
+            break
+        }
+    }
+    
+}
diff --git a/AdvanceSample-iOS/GesturesViewController.swift b/AdvanceSample-iOS/GesturesViewController.swift
new file mode 100644
index 0000000..50cb533
--- /dev/null
+++ b/AdvanceSample-iOS/GesturesViewController.swift
@@ -0,0 +1,84 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import UIKit
+
+final class GesturesViewController: DemoViewController {
+    
+    let gestureView = GestureView()
+
+    required init() {
+        super.init(nibName: nil, bundle: nil)
+        title = "Gestures"
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        note = "Use two fingers to pick up the square."
+        
+        contentView.addSubview(gestureView)
+
+        // Do any additional setup after loading the view.
+    }
+    
+    override func viewWillAppear(animated: Bool) {
+        super.viewWillAppear(animated)
+    }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        var b = CGRect.zero
+        b.size.width = min(view.bounds.width, view.bounds.height) - 64.0
+        b.size.height = b.size.width
+        gestureView.bounds = b
+        gestureView.animatableCenter.value = CGPoint(x: contentView.bounds.midX, y: contentView.bounds.midY)
+        gestureView.animatableTransform.value = SimpleTransform()
+    }
+
+    override func didReceiveMemoryWarning() {
+        super.didReceiveMemoryWarning()
+        // Dispose of any resources that can be recreated.
+    }
+    
+
+    /*
+    // MARK: - Navigation
+
+    // In a storyboard-based application, you will often want to do a little preparation before navigation
+    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
+        // Get the new view controller using segue.destinationViewController.
+        // Pass the selected object to the new view controller.
+    }
+    */
+
+}
diff --git a/AdvanceSample-iOS/GravityFunction.swift b/AdvanceSample-iOS/GravityFunction.swift
new file mode 100644
index 0000000..9b6c3bd
--- /dev/null
+++ b/AdvanceSample-iOS/GravityFunction.swift
@@ -0,0 +1,81 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import Advance
+import Foundation
+
+struct GravityFunction: DynamicFunctionType {
+    
+    typealias Vector = Vector2
+    
+    var target: Vector
+    
+    var minRadius = 30.0
+    
+    var threshold: Scalar = 0.1
+    
+    init(target: Vector) {
+        self.target = target
+    }
+    
+    func acceleration(value: Vector, velocity: Vector) -> Vector {
+        
+        let delta = target - value
+        let heading = atan2(delta.y, delta.x)
+        
+        var distance = hypot(delta.x, delta.y)
+        distance = max(distance, minRadius)
+        
+        let accel = 1000000.0 / (distance*distance)
+        
+        var result = Vector.zero
+        result.x = accel * cos(heading)
+        result.y = accel * sin(heading)
+        return result
+    }
+    
+    func canSettle(value: Vector, velocity: Vector) -> Bool {
+        let min = Vector(scalar: -threshold)
+        let max = Vector(scalar: threshold)
+        
+        if velocity.clamped(min: min, max: max) != velocity {
+            return false
+        }
+        
+        let valueDelta = value - target
+        if valueDelta.clamped(min: min, max: max) != valueDelta {
+            return false
+        }
+        
+        return true
+    }
+    
+    func settledValue(value: Vector, velocity: Vector) -> Vector {
+        return target
+    }
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/GravitySimulation.swift b/AdvanceSample-iOS/GravitySimulation.swift
new file mode 100644
index 0000000..d3a2c77
--- /dev/null
+++ b/AdvanceSample-iOS/GravitySimulation.swift
@@ -0,0 +1,181 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import CoreGraphics
+import Advance
+
+
+private enum GravitySimulationNode: Advanceable {
+    case Static(Vector2)
+    case Decay(DynamicSolver<DecayFunction<Vector2>>)
+    case Pull(DynamicSolver<GravityFunction>)
+    
+    var value: Vector2 {
+        switch self {
+        case .Static(let v):
+            return v
+        case .Decay(let d):
+            return d.value
+        case .Pull(let s):
+            return s.value
+        }
+    }
+    
+    var velocity: Vector2 {
+        switch self {
+        case .Static(_):
+            return Vector2.zero
+        case .Decay(let d):
+            return d.velocity
+        case .Pull(let s):
+            return s.velocity
+        }
+    }
+    
+    mutating func advance(elapsed: Double) {
+        switch self {
+        case .Static(_):
+            break
+        case .Decay(var sim):
+            sim.advance(elapsed)
+            self = sim.settled ? .Static(sim.value) : .Decay(sim)
+        case .Pull(var sim):
+            sim.advance(elapsed)
+            self = sim.settled ? .Static(sim.value) : .Pull(sim)
+        }
+    }
+    
+    var settled: Bool {
+        switch self {
+        case .Static(_):
+            return true
+        case .Pull(_), .Decay(_):
+            return false
+        }
+    }
+    
+    mutating func pullTo(point: CGPoint) {
+        if case var .Pull(sim) = self {
+            sim.function.target = point.vector
+            self = .Pull(sim)
+            return
+        }
+        
+        let f = GravityFunction(target: point.vector)
+        let solver = DynamicSolver(function: f, value: value, velocity: velocity)
+        self = .Pull(solver)
+    }
+    
+    mutating func decay() {
+        guard case let .Pull(sim) = self else { return }
+        
+        let f = DecayFunction<Vector2>()
+        let solver = DynamicSolver(function: f, value: sim.value, velocity: sim.velocity)
+        self = .Decay(solver)
+    }
+    
+    mutating func reset(to: CGPoint) {
+        self = .Static(to.vector)
+    }
+}
+
+
+struct GravitySimulation: Advanceable {
+    
+    let rows: Int = 10
+    let cols: Int = 10
+    
+    init() {
+        for r in 0..<rows {
+            nodes.append([])
+            for _ in 0..<cols {
+                let node = GravitySimulationNode.Static(Vector2(0.0, 0.0))
+                nodes[r].append(node)
+            }
+        }
+    }
+    
+    private var nodes: [[GravitySimulationNode]] = []
+    
+    mutating func reset(layoutBounds: CGRect) {
+        target = nil
+        
+        let rowHeight = (layoutBounds.size.height / CGFloat(rows-1))
+        let colWidth = (layoutBounds.size.width / CGFloat(cols-1))
+        
+        for r in 0..<rows {
+            for c in 0..<cols {
+                var p = layoutBounds.origin
+                p.x += CGFloat(c) * colWidth
+                p.y += CGFloat(r) * rowHeight
+                nodes[r][c].reset(p)
+            }
+        }
+    }
+    
+    var target: CGPoint? = nil {
+        didSet {
+            updateNodes()
+        }
+    }
+    
+    private mutating func updateNodes() {
+        for r in 0..<rows {
+            for c in 0..<cols {
+                if let t = target {
+                    nodes[r][c].pullTo(t)
+                } else {
+                    nodes[r][c].decay()
+                }
+            }
+        }
+    }
+    
+    func getPosition(row: Int, col: Int) -> CGPoint {
+        return CGPoint(vector: nodes[row][col].value)
+    }
+    
+    mutating func advance(elapsed: Double) {
+        for r in 0..<rows {
+            for c in 0..<cols {
+                nodes[r][c].advance(elapsed)
+            }
+        }
+    }
+    
+    var settled: Bool {
+        for r in 0..<rows {
+            for c in 0..<cols {
+                if nodes[r][c].settled == false {
+                    return false
+                }
+            }
+        }
+        return true
+    }
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/GravityViewController.swift b/AdvanceSample-iOS/GravityViewController.swift
new file mode 100644
index 0000000..49d9b18
--- /dev/null
+++ b/AdvanceSample-iOS/GravityViewController.swift
@@ -0,0 +1,144 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import UIKit
+import Advance
+
+class GravityViewController: DemoViewController {
+    
+    var simulation = GravitySimulation() {
+        didSet {
+            if simulation.settled == false && subscription.paused == true {
+                subscription.paused = false
+            }
+            view.setNeedsLayout()
+        }
+    }
+    
+    private lazy var subscription: LoopSubscription = {
+        let s = Loop.shared.subscribe()
+        
+        s.advanced.observe({ [unowned self] (elapsed) -> Void in
+            self.simulation.advance(elapsed)
+            if self.simulation.settled {
+                self.subscription.paused = true
+            }
+        })
+        
+        return s
+    }()
+    
+    let resetButton = UIButton()
+    
+    private var nodeLayers: [[CALayer]] = []
+    
+    private var lastLayoutSize: CGSize = CGSize.zero
+    
+    private let recognizer = UILongPressGestureRecognizer()
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        title = "Gravity"
+        note = "Long press to add gravity."
+        
+        recognizer.minimumPressDuration = 0.3
+        recognizer.addTarget(self, action: "press:")
+        recognizer.enabled = false
+        contentView.addGestureRecognizer(recognizer)
+        
+        resetButton.setTitle("Reset", forState: UIControlState.Normal)
+        resetButton.setTitleColor(UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0), forState: .Normal)
+        resetButton.layer.cornerRadius = 4.0
+        resetButton.tintColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0)
+        resetButton.layer.borderColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0).CGColor
+        resetButton.layer.borderWidth = 1.0
+        resetButton.addTarget(self, action: "reset", forControlEvents: .TouchUpInside)
+        resetButton.alpha = 0.0
+        view.addSubview(resetButton)
+        
+        
+        for r in 0..<simulation.rows {
+            nodeLayers.append([])
+            for _ in 0..<simulation.cols {
+                let layer = CALayer()
+                layer.backgroundColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0).CGColor
+                layer.bounds = CGRect(x: 0.0, y: 0.0, width: 8.0, height: 8.0)
+                layer.cornerRadius = 4.0
+                layer.actions = ["position": NSNull()]
+                nodeLayers[r].append(layer)
+                contentView.layer.addSublayer(layer)
+            }
+        }
+    }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        if view.bounds.size != lastLayoutSize {
+            lastLayoutSize = view.bounds.size
+            reset()
+        }
+        
+        for r in 0..<simulation.rows {
+            for c in 0..<simulation.cols {
+                let position = simulation.getPosition(r, col: c)
+                nodeLayers[r][c].position = position
+            }
+        }
+        
+        resetButton.bounds = CGRect(x: 0.0, y: 0.0, width: 120.0, height: 44.0)
+        resetButton.center = CGPoint(x: contentView.bounds.midX, y: contentView.bounds.maxY - 64.0)
+    }
+    
+    dynamic func press(recognizer: UILongPressGestureRecognizer) {
+        switch recognizer.state {
+        case .Began, .Changed:
+            simulation.target = recognizer.locationInView(view)
+        case .Ended:
+            simulation.target = nil
+        default:
+            break
+        }
+    }
+    
+    dynamic func reset() {
+        simulation.reset(view.bounds.insetBy(dx: 64.0, dy: 128.0))
+    }
+    
+    override func didEnterFullScreen() {
+        super.didEnterFullScreen()
+        recognizer.enabled = true
+        resetButton.alpha = 1.0
+    }
+    
+    override func didLeaveFullScreen() {
+        super.didLeaveFullScreen()
+        recognizer.enabled = false
+        resetButton.alpha = 0.0
+    }
+}
diff --git a/AdvanceSample-iOS/Info.plist b/AdvanceSample-iOS/Info.plist
new file mode 100644
index 0000000..ff2910b
--- /dev/null
+++ b/AdvanceSample-iOS/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UIStatusBarHidden</key>
+	<true/>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+	</array>
+</dict>
+</plist>
diff --git a/AdvanceSample-iOS/SimpleTransform.swift b/AdvanceSample-iOS/SimpleTransform.swift
new file mode 100644
index 0000000..5629887
--- /dev/null
+++ b/AdvanceSample-iOS/SimpleTransform.swift
@@ -0,0 +1,68 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import Foundation
+import CoreGraphics
+import Advance
+
+struct SimpleTransform {
+    var scale: CGFloat = 1.0
+    var rotation: CGFloat = 0.0
+    
+    init() {}
+    
+    init(scale: CGFloat, rotation: CGFloat) {
+        self.scale = scale
+        self.rotation = rotation
+    }
+    
+    var affineTransform: CGAffineTransform {
+        var t = CGAffineTransformIdentity
+        t = CGAffineTransformRotate(t, rotation)
+        t = CGAffineTransformScale(t, scale, scale)
+        return t
+    }
+}
+
+extension SimpleTransform: VectorConvertible {
+    typealias Vector = Vector2
+    
+    var vector: Vector {
+        return Vector2(Scalar(scale), Scalar(rotation))
+    }
+    
+    init(vector: Vector) {
+        scale = CGFloat(vector.x)
+        rotation = CGFloat(vector.y)
+    }
+}
+
+func ==(lhs: SimpleTransform, rhs: SimpleTransform) -> Bool {
+    return lhs.scale == rhs.scale
+        && lhs.rotation == rhs.rotation
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/SpringConfigurationView.swift b/AdvanceSample-iOS/SpringConfigurationView.swift
new file mode 100644
index 0000000..dfe7f7e
--- /dev/null
+++ b/AdvanceSample-iOS/SpringConfigurationView.swift
@@ -0,0 +1,166 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import Foundation
+import UIKit
+
+protocol SpringConfigurationViewDelegate: class {
+    func springConfigurationViewDidChange(view: SpringConfigurationView)
+}
+
+
+class SpringConfigurationView: UIView {
+    
+    weak var delegate: SpringConfigurationViewDelegate? = nil
+    
+    var tension: CGFloat {
+        get { return CGFloat(tensionSlider.slider.value) }
+        set { tensionSlider.slider.value = Float(newValue) }
+    }
+    
+    var damping: CGFloat {
+        get { return CGFloat(dampingSlider.slider.value) }
+        set { dampingSlider.slider.value = Float(newValue) }
+    }
+    
+    private let tensionSlider = LabeledSliderView()
+    private let dampingSlider = LabeledSliderView()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        addSubview(tensionSlider)
+        addSubview(dampingSlider)
+        
+        tensionSlider.slider.addTarget(self, action: "changed", forControlEvents: .ValueChanged)
+        dampingSlider.slider.addTarget(self, action: "changed", forControlEvents: .ValueChanged)
+        
+        tensionSlider.slider.minimumValue = 1.0
+        tensionSlider.slider.maximumValue = 400.0
+        
+        dampingSlider.slider.minimumValue = 0.1
+        dampingSlider.slider.maximumValue = 80.0
+        
+        tensionSlider.slider.value = 120.0
+        dampingSlider.slider.value = 10.0
+        
+        tensionSlider.text = "Tension"
+        dampingSlider.text = "Damping"
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func sizeThatFits(size: CGSize) -> CGSize {
+        var s = CGSize.zero
+        s.width = size.width
+        s.height += tensionSlider.sizeThatFits(size).height
+        s.height += dampingSlider.sizeThatFits(size).height
+        return s
+    }
+
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        
+        var tensionSize = tensionSlider.sizeThatFits(bounds.size)
+        tensionSize.width = bounds.width
+        tensionSlider.frame = CGRect(origin: CGPoint.zero, size: tensionSize)
+        
+        var dampingSize = dampingSlider.sizeThatFits(bounds.size)
+        dampingSize.width = bounds.width
+        dampingSlider.frame = CGRect(x: 0.0, y: tensionSlider.frame.maxY, width: bounds.width, height: dampingSize.height)
+    }
+    
+    private dynamic func changed() {
+        delegate?.springConfigurationViewDidChange(self)
+    }
+}
+
+
+private class LabeledSliderView: UIView {
+    
+    var labelWidth: CGFloat = 90.0 {
+        didSet { setNeedsLayout() }
+    }
+    
+    var gutterWidth: CGFloat = 20.0 {
+        didSet { setNeedsLayout() }
+    }
+    
+    var sideMargin: CGFloat = 12.0 {
+        didSet { setNeedsLayout() }
+    }
+    
+    var text: String {
+        get { return label.text ?? "" }
+        set { label.text = newValue }
+    }
+    
+    private let label: UILabel
+    private let slider: UISlider
+    
+    override init(frame: CGRect) {
+        label = UILabel()
+        slider = UISlider()
+        super.init(frame: frame)
+        
+        slider.minimumTrackTintColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0)
+        
+        label.text = "Untitled"
+        label.textColor = UIColor.darkGrayColor()
+        
+        addSubview(label)
+        addSubview(slider)
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    private override func sizeThatFits(size: CGSize) -> CGSize {
+        var s = size
+        s.height = 44.0
+        return s
+    }
+    
+    private override func layoutSubviews() {
+        super.layoutSubviews()
+        
+        var labelSize = label.sizeThatFits(bounds.size)
+        labelSize.width = min(labelSize.width, labelWidth)
+        label.bounds = CGRect(origin: CGPoint.zero, size: labelSize)
+        label.center = CGPoint(x: sideMargin + labelSize.width/2.0, y: bounds.midY)
+        
+        var sliderFrame = CGRect.zero
+        sliderFrame.size.height = slider.sizeThatFits(bounds.size).height
+        sliderFrame.size.width = bounds.width - (sideMargin * 2.0) - labelWidth - gutterWidth
+        sliderFrame.origin.x = sideMargin + labelWidth + gutterWidth
+        sliderFrame.origin.y = (bounds.height - sliderFrame.height) / 2.0
+        slider.frame = sliderFrame
+    }
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/SpringView.swift b/AdvanceSample-iOS/SpringView.swift
new file mode 100644
index 0000000..3cd70d0
--- /dev/null
+++ b/AdvanceSample-iOS/SpringView.swift
@@ -0,0 +1,61 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import Foundation
+import UIKit
+import Advance
+
+final class SpringView: UIView {
+    
+    let centerSpring = Spring(value: CGPoint.zero)
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        backgroundColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0)
+        
+        let t = arc4random_uniform(120 - 20) + 20
+        let d = arc4random_uniform(20 - 4) + 4
+        
+        centerSpring.configuration.tension = Scalar(t)
+        centerSpring.configuration.damping = Scalar(d)
+        
+        centerSpring.changed.observe { [unowned self] (c) -> Void in
+            self.center = c
+        }
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        layer.cornerRadius = min(bounds.width, bounds.height) / 2.0
+    }
+    
+}
\ No newline at end of file
diff --git a/AdvanceSample-iOS/SpringsViewController.swift b/AdvanceSample-iOS/SpringsViewController.swift
new file mode 100644
index 0000000..d996e44
--- /dev/null
+++ b/AdvanceSample-iOS/SpringsViewController.swift
@@ -0,0 +1,113 @@
+/*
+
+Copyright (c) 2016, Storehouse Media 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.
+
+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.
+
+*/
+
+import Foundation
+import UIKit
+import Advance
+
+class SpringsViewController: DemoViewController {
+    
+    private let springView = SpringView()
+    
+    private let configView = SpringConfigurationView()
+    
+    private let tapRecognizer = UITapGestureRecognizer()
+    
+    required init() {
+        super.init(nibName: nil, bundle:    nil)
+        title = "Spring"
+        note = "Tap anywhere to move the dot using a spring."
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        view.backgroundColor = UIColor.whiteColor()
+        
+        view.addGestureRecognizer(tapRecognizer)
+        tapRecognizer.addTarget(self, action: "tap:")
+        tapRecognizer.enabled = false
+        
+        springView.bounds = CGRect(x: 0.0, y: 0.0, width: 24.0, height: 24.0)
+        
+        configView.delegate = self
+        configView.alpha = 0.0
+        contentView.addSubview(springView)
+        contentView.addSubview(configView)
+        
+        updateSprings()
+    }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        
+        var configFrame = CGRect.zero
+        configFrame.size.width = view.bounds.width
+        configFrame.size.height = configView.sizeThatFits(view.bounds.size).height
+        configFrame.origin.y = view.bounds.maxY - configFrame.height - bottomLayoutGuide.length
+        configView.frame = configFrame
+    }
+    
+    dynamic func tap(recognizer: UITapGestureRecognizer) {
+        let point = recognizer.locationInView(view)
+        springView.centerSpring.target = point
+    }
+    
+    override func viewWillAppear(animated: Bool) {
+        super.viewWillAppear(animated)
+        springView.centerSpring.reset(CGPoint(x: view.bounds.midX, y: view.bounds.midY))
+    }
+    
+    private func updateSprings() {
+        springView.centerSpring.configuration.tension = Scalar(configView.tension)
+        springView.centerSpring.configuration.damping = Scalar(configView.damping)
+    }
+    
+    override func didEnterFullScreen() {
+        super.didEnterFullScreen()
+        configView.alpha = 1.0
+        tapRecognizer.enabled = true
+    }
+    
+    override func didLeaveFullScreen() {
+        super.didLeaveFullScreen()
+        configView.alpha = 0.0
+        springView.centerSpring.target = CGPoint(x: contentView.bounds.midX, y: contentView.bounds.midY)
+        tapRecognizer.enabled = false
+    }
+}
+
+
+extension SpringsViewController: SpringConfigurationViewDelegate {
+    func springConfigurationViewDidChange(view: SpringConfigurationView) {
+        updateSprings()
+    }
+}
\ No newline at end of file
diff --git a/AdvanceSample/ActivityView.swift b/AdvanceSample/ActivityView.swift
deleted file mode 100644
index 2c0da84..0000000
--- a/AdvanceSample/ActivityView.swift
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import UIKit
-import Advance
-
-
-
-public final class ActivityView: UIView {
-    
-    private static let points: [CGPoint] = {
-        var points: [CGPoint] = Array(count: 10, repeatedValue: CGPoint.zero)
-        points[0] = CGPoint(x: 0.5, y: 0.0)
-        points[1] = CGPoint(x: 0.20928571428571, y: 0.16642857142857)
-        points[2] = CGPoint(x: 0.79071428571429, y: 0.16642857142857)
-        points[3] = CGPoint(x: 0.5, y: 0.3325)
-        points[4] = CGPoint(x: 0.20928571428571, y: 0.49964285714286)
-        points[5] = CGPoint(x: 0.79071428571429, y: 0.49964285714286)
-        points[6] = CGPoint(x: 0.5, y: 0.66607142857143)
-        points[7] = CGPoint(x: 0.20928571428571, y: 0.83357142857143)
-        points[8] = CGPoint(x: 0.79071428571429, y: 0.83357142857143)
-        points[9] = CGPoint(x: 0.5, y: 1.0)
-        return points
-    }()
-    
-    private let segments: [ActivitySegment] = {
-        var segments: [ActivitySegment] = []
-        segments.append(ActivitySegment(firstPoint: points[1], secondPoint: points[0]))
-        segments.append(ActivitySegment(firstPoint: points[0], secondPoint: points[2]))
-        segments.append(ActivitySegment(firstPoint: points[1], secondPoint: points[3]))
-        segments.append(ActivitySegment(firstPoint: points[2], secondPoint: points[3]))
-        segments.append(ActivitySegment(firstPoint: points[1], secondPoint: points[4]))
-        segments.append(ActivitySegment(firstPoint: points[4], secondPoint: points[3]))
-        segments.append(ActivitySegment(firstPoint: points[3], secondPoint: points[5]))
-        segments.append(ActivitySegment(firstPoint: points[4], secondPoint: points[6]))
-        segments.append(ActivitySegment(firstPoint: points[6], secondPoint: points[5]))
-        segments.append(ActivitySegment(firstPoint: points[5], secondPoint: points[8]))
-        segments.append(ActivitySegment(firstPoint: points[6], secondPoint: points[7]))
-        segments.append(ActivitySegment(firstPoint: points[6], secondPoint: points[8]))
-        segments.append(ActivitySegment(firstPoint: points[7], secondPoint: points[9]))
-        segments.append(ActivitySegment(firstPoint: points[8], secondPoint: points[9]))
-        return segments
-    }()
-    
-    private let visibilitySprings: [Spring<CGFloat>] = {
-        return (0...13).map {_ in
-            let s = Spring(value: CGFloat(1.0))
-            s.configuration.threshold = 0.001
-            s.configuration.tension = 220.0 + Double(arc4random() % 200);
-            s.configuration.damping = 30.0 + Double(arc4random() % 10);
-            return s
-        }
-    }()
-    
-    private var segmentLayers: [CAShapeLayer] = {
-        return (0...13).map {_ in
-            let sl = CAShapeLayer()
-            var actions: [String: AnyObject] = [:]
-            actions["position"] = NSNull()
-            actions["bounds"] = NSNull()
-            actions["lineWidth"] = NSNull()
-            actions["strokeColor"] = NSNull()
-            actions["path"] = NSNull()
-            return sl
-        }
-    }()
-    
-    private var strokeColor: UIColor {
-        return color.colorWithAlphaComponent(0.6)
-    }
-    
-    private var flashStrokeColor: UIColor {
-        return color
-    }
-    
-    private let lineWidth = CGFloat(1.0)
-    private let flashLineWidth = CGFloat(2.0)
-    
-    private var flashTimer: NSTimer? = nil
-    
-    public var color = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0) {
-        didSet {
-            for sl in segmentLayers {
-                sl.strokeColor = strokeColor.CGColor
-            }
-        }
-    }
-    
-    public var assembledAmount: CGFloat = 1.0 {
-        didSet {
-            updateSegmentVisibility(true)
-        }
-    }
-    
-    public var flashing: Bool = false {
-        didSet {
-            guard flashing != oldValue else { return }
-            if flashing {
-                let t = NSTimer(timeInterval: 1.0, target: self, selector: "flash", userInfo: nil, repeats: true)
-                NSRunLoop.mainRunLoop().addTimer(t, forMode: NSRunLoopCommonModes)
-                flashTimer = t
-                flash()
-            } else {
-                flashTimer?.invalidate()
-                flashTimer = nil
-            }
-        }
-    }
-    
-    required override public init(frame: CGRect) {
-        super.init(frame: frame)
-        
-        backgroundColor = UIColor.clearColor()
-        layer.allowsGroupOpacity = false
-        
-        for vs in visibilitySprings {
-            vs.changed.observe({ [weak self] (vis) in
-                self?.setNeedsLayout()
-            })
-        }
-        
-        for sl in segmentLayers {
-            sl.strokeColor = strokeColor.CGColor
-            sl.lineWidth = lineWidth
-            layer.addSublayer(sl)
-        }
-    }
-    
-    required public init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    override public func layoutSubviews() {
-        super.layoutSubviews()
-        
-        for i in 0..<segments.count {
-            let s = segments[i]
-            let sl = segmentLayers[i]
-            let visibility = visibilitySprings[i].value
-            sl.frame = bounds
-            sl.path = s.path(bounds.size, visibility: visibility).CGPath
-            sl.opacity = Float(visibility)
-        }
-    }
-    
-    override public func sizeThatFits(size: CGSize) -> CGSize {
-        return CGSize(width: 40.0, height: 40.0)
-    }
-    
-    public func resetAssembledAmount(assembledAmount: CGFloat) {
-        self.assembledAmount = assembledAmount
-        updateSegmentVisibility(false)
-    }
-    
-    func updateSegmentVisibility(animated: Bool) {
-        for i in 0..<segments.count {
-            let positionInArray = CGFloat(i) / CGFloat(segments.count-1)
-            
-            let minVis = (1.0-positionInArray) * 0.4
-            let maxVis = minVis + 0.6
-            
-            var mappedVis = (assembledAmount - minVis) / (maxVis - minVis)
-            mappedVis = min(mappedVis, 1.0)
-            mappedVis = max(mappedVis, 0.0)
-            mappedVis = quadEaseInOut(mappedVis)
-            
-            if animated {
-                visibilitySprings[i].target = mappedVis
-            } else {
-                visibilitySprings[i].reset(mappedVis)
-            }
-        }
-    }
-
-    private dynamic func flash() {
-        for i in 0..<segmentLayers.count {
-            let t = dispatch_time(DISPATCH_TIME_NOW, Int64(Double(NSEC_PER_SEC) * 0.04 * Double(i)))
-            dispatch_after(t, dispatch_get_main_queue(), { 
-                self.flashSegment(i)
-            })
-        }
-    }
-    
-    private func flashSegment(index: Int) {
-        let sl = segmentLayers[index]
-        
-        CATransaction.begin()
-        
-        let c = CAKeyframeAnimation(keyPath: "strokeColor")
-        c.values = [strokeColor.CGColor, flashStrokeColor.CGColor, strokeColor.CGColor]
-        c.keyTimes = [0.0, 0.3, 1.0]
-        c.calculationMode = kCAAnimationCubic
-        c.duration = 0.5
-        sl.addAnimation(c, forKey: "flashStrokeColor")
-        
-        let s = CAKeyframeAnimation(keyPath: "lineWidth")
-        s.values = [lineWidth, flashLineWidth, lineWidth]
-        s.keyTimes = [0.0, 0.3, 1.0]
-        s.calculationMode = kCAAnimationCubic
-        s.duration = 0.5
-        sl.addAnimation(s, forKey: "flashLineWidth")
-        
-        CATransaction.commit()
-    }
-}
-
-private func quadEaseInOut(t: CGFloat) -> CGFloat {
-    var result = t / 0.5
-    if (result < 1.0) {
-        return 0.5*result*result
-    } else {
-        result -= 1.0
-        return -0.5 * (result * (result - 2.0) - 1.0)
-    }
-}
-
-
-private struct ActivitySegment {
-    
-    let firstPoint: CGPoint
-    let secondPoint: CGPoint
-    
-    let initialPosition: CGPoint = {
-        var p = CGPoint.zero
-        p.x = (CGFloat(arc4random() % 100) / 100.0)
-        p.y = (CGFloat(arc4random() % 100) / 100.0)
-        p.x = ((p.x - 0.5) * 2.0) + 0.5
-        p.y -= 0.6;
-        return p
-    }()
-    
-    let initialRotation = ((CGFloat(arc4random() % 100) / 100.0) * CGFloat(M_PI))
-    
-    func path(size: CGSize, visibility: CGFloat) -> UIBezierPath {
-        var p1 = initialPosition
-        var p2 = initialPosition
-        
-        p1.interpolateTo(firstPoint, alpha: Scalar(visibility))
-        p2.interpolateTo(secondPoint, alpha: Scalar(visibility))
-        
-        let rotation = initialRotation.interpolatedTo(0.0, alpha: Scalar(visibility))
-        let midX = p1.x + (p2.x - p1.x) * 0.5
-        let midY = p1.y + (p2.y - p1.y) * 0.5
-        
-        p1.x -= midX
-        p2.x -= midX
-        p1.y -= midY
-        p2.y -= midY
-        
-        p1 = CGPointApplyAffineTransform(p1, CGAffineTransformMakeRotation(rotation))
-        p2 = CGPointApplyAffineTransform(p2, CGAffineTransformMakeRotation(rotation))
-        
-        p1.x += midX
-        p2.x += midX
-        p1.y += midY
-        p2.y += midY
-        
-        // we store layout info in relative coords
-        p1.x *= size.width
-        p1.y *= size.height
-        p2.x *= size.width
-        p2.y *= size.height
-        
-        let p = UIBezierPath()
-        p.moveToPoint(p1)
-        p.addLineToPoint(p2)
-        
-        return p
-    }
-    
-    init(firstPoint: CGPoint, secondPoint: CGPoint) {
-        self.firstPoint = firstPoint
-        self.secondPoint = secondPoint
-    }
-}
diff --git a/AdvanceSample/ActivityViewController.swift b/AdvanceSample/ActivityViewController.swift
deleted file mode 100644
index 772c8c8..0000000
--- a/AdvanceSample/ActivityViewController.swift
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import UIKit
-
-class ActivityViewController: DemoViewController {
-    
-    let activityView = ActivityView()
-    
-    let slider = UISlider()
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        title = "Logo"
-        note = "Drag the slider to disassemble."
-                
-        activityView.flashing = true
-        contentView.addSubview(activityView)
-        
-        contentView.addSubview(slider)
-        slider.alpha = 0.0
-        slider.minimumValue = 0.0
-        slider.maximumValue = 1.0
-        slider.value = 1.0
-        slider.addTarget(self, action: "sliderChanged", forControlEvents: .ValueChanged)
-        slider.minimumTrackTintColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0)
-    }
-    
-    override func viewDidLayoutSubviews() {
-        super.viewDidLayoutSubviews()
-        let size = min(view.bounds.width, view.bounds.height) * 0.6
-        activityView.bounds.size = CGSize(width: size, height: size)
-        activityView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
-        
-        let sliderInset = CGFloat(32.0)
-        
-        var sliderFrame = CGRect.zero
-        sliderFrame.size.width = view.bounds.width - sliderInset*2.0
-        sliderFrame.size.height = 44.0
-        sliderFrame.origin.x = sliderInset
-        sliderFrame.origin.y = view.bounds.maxY - sliderInset - 44.0
-        slider.frame = sliderFrame
-    }
-    
-    dynamic func sliderChanged() {
-        activityView.assembledAmount = CGFloat(slider.value)
-        activityView.flashing = activityView.assembledAmount == 1.0
-    }
-    
-    override func didEnterFullScreen() {
-        super.didEnterFullScreen()
-        slider.alpha = 1.0
-    }
-    
-    override func didLeaveFullScreen() {
-        super.didLeaveFullScreen()
-        slider.alpha = 0.0
-    }
-
-}
diff --git a/AdvanceSample/AppDelegate.swift b/AdvanceSample/AppDelegate.swift
deleted file mode 100644
index 200e8a0..0000000
--- a/AdvanceSample/AppDelegate.swift
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import UIKit
-import Advance
-
-@UIApplicationMain
-class AppDelegate: UIResponder, UIApplicationDelegate {
-
-    var window: UIWindow?
-
-    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
-        // Override point for customization after application launch.
-        
-        window = UIWindow(frame: UIScreen.mainScreen().bounds)
-        
-        let a = ActivityViewController()
-        let g = GesturesViewController()
-        let s = SpringsViewController()
-        let gravity = GravityViewController()
-        
-        window?.rootViewController = BrowserViewController(viewControllers: [a, gravity, s, g])
-        window?.makeKeyAndVisible()
-        
-        return true
-    }
-
-    func applicationWillResignActive(application: UIApplication) {
-        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
-        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
-    }
-
-    func applicationDidEnterBackground(application: UIApplication) {
-        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
-        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
-    }
-
-    func applicationWillEnterForeground(application: UIApplication) {
-        // 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.
-    }
-
-    func applicationDidBecomeActive(application: UIApplication) {
-        // 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.
-    }
-
-    func applicationWillTerminate(application: UIApplication) {
-        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
-    }
-
-
-}
-
diff --git a/AdvanceSample/Assets.xcassets/AppIcon.appiconset/Contents.json b/AdvanceSample/Assets.xcassets/AppIcon.appiconset/Contents.json
deleted file mode 100644
index 118c98f..0000000
--- a/AdvanceSample/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "iphone",
-      "size" : "29x29",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "iphone",
-      "size" : "29x29",
-      "scale" : "3x"
-    },
-    {
-      "idiom" : "iphone",
-      "size" : "40x40",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "iphone",
-      "size" : "40x40",
-      "scale" : "3x"
-    },
-    {
-      "idiom" : "iphone",
-      "size" : "60x60",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "iphone",
-      "size" : "60x60",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/AdvanceSample/Assets.xcassets/Contents.json b/AdvanceSample/Assets.xcassets/Contents.json
deleted file mode 100644
index da4a164..0000000
--- a/AdvanceSample/Assets.xcassets/Contents.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/AdvanceSample/Assets.xcassets/background-blurred.imageset/Contents.json b/AdvanceSample/Assets.xcassets/background-blurred.imageset/Contents.json
deleted file mode 100644
index 611304d..0000000
--- a/AdvanceSample/Assets.xcassets/background-blurred.imageset/Contents.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "background-blurred.jpg",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/AdvanceSample/Assets.xcassets/background-blurred.imageset/background-blurred.jpg b/AdvanceSample/Assets.xcassets/background-blurred.imageset/background-blurred.jpg
deleted file mode 100644
index 273216b..0000000
Binary files a/AdvanceSample/Assets.xcassets/background-blurred.imageset/background-blurred.jpg and /dev/null differ
diff --git a/AdvanceSample/Assets.xcassets/background.imageset/Contents.json b/AdvanceSample/Assets.xcassets/background.imageset/Contents.json
deleted file mode 100644
index e97bcdb..0000000
--- a/AdvanceSample/Assets.xcassets/background.imageset/Contents.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "background.jpg",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}
\ No newline at end of file
diff --git a/AdvanceSample/Assets.xcassets/background.imageset/background.jpg b/AdvanceSample/Assets.xcassets/background.imageset/background.jpg
deleted file mode 100644
index b496354..0000000
Binary files a/AdvanceSample/Assets.xcassets/background.imageset/background.jpg and /dev/null differ
diff --git a/AdvanceSample/Assets.xcassets/logo.imageset/Contents.json b/AdvanceSample/Assets.xcassets/logo.imageset/Contents.json
deleted file mode 100644
index 8d2722a..0000000
--- a/AdvanceSample/Assets.xcassets/logo.imageset/Contents.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "images" : [
-    {
-      "idiom" : "universal",
-      "filename" : "logo.pdf"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  },
-  "properties" : {
-    "template-rendering-intent" : "template"
-  }
-}
\ No newline at end of file
diff --git a/AdvanceSample/Assets.xcassets/logo.imageset/logo.pdf b/AdvanceSample/Assets.xcassets/logo.imageset/logo.pdf
deleted file mode 100644
index 616793b..0000000
Binary files a/AdvanceSample/Assets.xcassets/logo.imageset/logo.pdf and /dev/null differ
diff --git a/AdvanceSample/Base.lproj/LaunchScreen.storyboard b/AdvanceSample/Base.lproj/LaunchScreen.storyboard
deleted file mode 100644
index 90d6157..0000000
--- a/AdvanceSample/Base.lproj/LaunchScreen.storyboard
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9532" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
-    <dependencies>
-        <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530"/>
-    </dependencies>
-    <scenes>
-        <!--View Controller-->
-        <scene sceneID="EHf-IW-A2E">
-            <objects>
-                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
-                    <layoutGuides>
-                        <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
-                        <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
-                    </layoutGuides>
-                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
-                        <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="custom" customColorSpace="calibratedWhite"/>
-                    </view>
-                </viewController>
-                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
-            </objects>
-            <point key="canvasLocation" x="53" y="375"/>
-        </scene>
-    </scenes>
-</document>
diff --git a/AdvanceSample/BrowserView.swift b/AdvanceSample/BrowserView.swift
deleted file mode 100644
index 649a4c0..0000000
--- a/AdvanceSample/BrowserView.swift
+++ /dev/null
@@ -1,508 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import UIKit
-import Advance
-
-class BrowserItem: NSObject {
-    
-    let center = Spring(value: CGPoint.zero)
-    let transform = Spring(value: SimpleTransform())
-    let size = Spring(value: CGSize.zero)
-    
-    let tapRecognizer = UITapGestureRecognizer()
-
-    let recognizer = DirectManipulationGestureRecognizer()
-    let panRecognizer = UIPanGestureRecognizer()
-    
-    var anchorPoint = CGPoint(x: 0.5, y: 0.5) {
-        didSet { browserView?.setNeedsLayout() }
-    }
-    
-    let view: UIView = {
-        let v = UIView(frame: CGRect.zero)
-        v.backgroundColor = UIColor.blueColor()
-        v.layer.cornerRadius = 6.0
-        v.clipsToBounds = true
-        return v
-    }()
-    
-    private var transformWhenGestureBegan = SimpleTransform()
-    private var centerWhenGestureBegan = CGPoint.zero
-    private (set) var gestureInProgress = false
-    
-    var frame: CGRect {
-        var f = CGRect.zero
-        f.size = size.value
-        f.origin.x -= anchorPoint.x * f.size.width
-        f.origin.y -= anchorPoint.y * f.size.height
-        f = CGRectApplyAffineTransform(f, transform.value.affineTransform)
-        f.origin.x += center.value.x
-        f.origin.y += center.value.y
-        return f
-    }
-    
-    private (set) weak var browserView: BrowserView? = nil
-    
-    override init() {
-        super.init()
-        
-        center.configuration.threshold = 0.1
-        center.configuration.tension = 120.0
-        center.configuration.damping = 27.0
-        center.changed.observe { [unowned self] (p) -> Void in
-            self.browserView?.setNeedsLayout()
-        }
-        
-        transform.configuration.threshold = 0.001
-        transform.changed.observe { [unowned self] (p) -> Void in
-            self.browserView?.setNeedsLayout()
-        }
-        
-        size.configuration.threshold = 0.1
-        size.changed.observe { [unowned self] (p) -> Void in
-            self.browserView?.setNeedsLayout()
-        }
-        
-        tapRecognizer.addTarget(self, action: "tap")
-        view.addGestureRecognizer(tapRecognizer)
-        
-        recognizer.addTarget(self, action: "gesture:")
-        view.addGestureRecognizer(recognizer)
-        
-        panRecognizer.addTarget(self, action: "pan:")
-        panRecognizer.delegate = self
-        view.addGestureRecognizer(panRecognizer)
-    }
-    
-    private dynamic func tap() {
-        if browserView?.fullScreenItem != self {
-            browserView?.enterFullScreen(self)
-        }
-    }
-    
-    private dynamic func gesture(recognizer: DirectManipulationGestureRecognizer) {
-        switch recognizer.state {
-        case .Began:
-            
-            let gestureLocation = recognizer.locationInView(view)
-            let newCenter = view.superview!.convertPoint(gestureLocation, fromView: view)
-            center.reset(newCenter)
-            
-            var anchorPoint = gestureLocation
-            anchorPoint.x /= view.bounds.width
-            anchorPoint.y /= view.bounds.height
-            self.anchorPoint = anchorPoint
-            
-            gestureInProgress = true
-            centerWhenGestureBegan = center.value
-            transformWhenGestureBegan = transform.value
-            transform.reset(transform.value)
-        case .Changed:
-            var t = transformWhenGestureBegan
-            t.rotation += recognizer.rotation
-            t.scale *= recognizer.scale
-            transform.reset(t)
-            
-            var c = centerWhenGestureBegan
-            c.x += recognizer.translationInView(view.superview).x
-            c.y += recognizer.translationInView(view.superview).y
-            center.reset(c)
-            break
-        case .Ended, .Cancelled:
-            
-            // Reset the anchor point
-            let mid = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
-            let newCenter = view.superview!.convertPoint(mid, fromView: view)
-            center.reset(newCenter)
-            anchorPoint = CGPoint(x: 0.5, y: 0.5)
-            
-            var velocity = SimpleTransform.zero
-            velocity.scale = recognizer.scaleVelocity
-            velocity.rotation = recognizer.rotationVelocity
-            var config = SpringConfiguration()
-            config.threshold = 0.001
-            transform.velocity = velocity
-            
-            let centerVel = recognizer.translationVelocityInView(view.superview)
-            var centerConfig = SpringConfiguration()
-            centerConfig.tension = 40.0
-            centerConfig.damping = 5.0
-            center.velocity = centerVel
-            
-            gestureInProgress = false
-            
-            if recognizer.scaleVelocity <= 0.0 && transform.value.scale < 1.0 && browserView?.fullScreenItem == self {
-                browserView?.leaveFullScreen()
-            } else if recognizer.scaleVelocity >= 0.0 && transform.value.scale > 0.75 && browserView?.fullScreenItem != self {
-                browserView?.enterFullScreen(self)
-            } else {
-                browserView?.updateAllItems(true)
-            }
-            break
-        default:
-            break
-        }
-    }
-    
-    dynamic func pan(recognizer: UIPanGestureRecognizer) {
-        switch recognizer.state {
-        case .Began:
-            gestureInProgress = true
-            centerWhenGestureBegan = center.value
-            center.reset(center.value)
-            break
-        case .Changed:
-            var c = centerWhenGestureBegan
-            c.y += recognizer.translationInView(view.superview).y
-            center.reset(c)
-            break
-        case .Ended:
-            gestureInProgress = false
-            center.velocity.y = recognizer.velocityInView(view.superview).y
-            if abs(recognizer.translationInView(view.superview).y) > 10.0 {
-                browserView?.leaveFullScreen()
-            } else {
-                browserView?.updateAllItems(true)
-            }
-        default:
-            break
-        }
-    }
-}
-
-extension BrowserItem: UIGestureRecognizerDelegate {
-    func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
-        if gestureRecognizer == panRecognizer {
-            if browserView?.fullScreenItem != self {
-                return false
-            }
-            
-            let translation = panRecognizer.translationInView(view)
-            if abs(translation.x) > abs(translation.y) {
-                return false
-            }
-        }
-        return true
-    }
-}
-
-func ==(lhs: BrowserItem, rhs: BrowserItem) -> Bool {
-    return lhs === rhs
-}
-
-protocol BrowserViewDelegate: class {
-    func browserView(browserView: BrowserView, didShowItem item: BrowserItem)
-    func browserView(browserView: BrowserView, didHideItem item: BrowserItem)
-    func browserView(browserView: BrowserView, didEnterFullScreenForItem item: BrowserItem)
-    func browserView(browserView: BrowserView, didLeaveFullScreenForItem item: BrowserItem)
-    func browserViewDidScroll(browserView: BrowserView)
-}
-
-class BrowserView: UIView {
-    
-    weak var delegate: BrowserViewDelegate? = nil
-    
-    private let paginationRatio: CGFloat = 0.68
-    
-    private let index = Animatable(value: CGFloat.zero)
-    
-    private var panInProgress = false
-    private var indexWhenPanBegan: CGFloat = 0.0
-    
-    private var visibleItems: Set<BrowserItem> = []
-    
-    private let panRecognizer = UIPanGestureRecognizer()
-    
-    private var lastLayoutSize = CGSize.zero
-    
-    private var fullScreenItem: BrowserItem? = nil
-    
-    private let coverVisibilty: Spring<CGFloat> = {
-        let s = Spring(value: CGFloat(1.0))
-        s.configuration.threshold = 0.001
-        s.configuration.tension = 220.0
-        s.configuration.damping = 28.0
-        return s
-    }()
-    
-    var coverView: CoverView? = nil {
-        didSet {
-            guard coverView !== oldValue else { return }
-            oldValue?.removeFromSuperview()
-            if let v = coverView {
-                addSubview(v)
-            }
-            setNeedsLayout()
-        }
-    }
-    
-    
-    var currentIndex: CGFloat {
-        return index.value
-    }
-    
-    var items: [BrowserItem] = [] {
-        willSet {
-            leaveFullScreen()
-            for item in visibleItems {
-                hideItem(item)
-            }
-            
-            for item in items {
-                item.browserView = nil
-            }
-        }
-        didSet {
-            for item in items {
-                item.browserView = self
-            }
-            updateAllItems(false)
-            setNeedsLayout()
-        }
-    }
-    
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-        
-        index.changed.observe { [unowned self] (idx) -> Void in
-            self.setNeedsLayout()
-            self.delegate?.browserViewDidScroll(self)
-        }
-        
-        coverVisibilty.changed.observe { [unowned self] (v) in
-            self.setNeedsLayout()
-        }
-        
-        panRecognizer.addTarget(self, action: "pan:")
-        panRecognizer.delegate = self
-        addGestureRecognizer(panRecognizer)
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    override func layoutSubviews() {
-        super.layoutSubviews()
-        guard bounds.size != CGSize.zero else { return }
-        
-        if lastLayoutSize != bounds.size {
-            lastLayoutSize = bounds.size
-            updateAllItems(false)
-        }
-        
-        var b = bounds
-        b.origin.x = 0.0 + (index.value * b.width * paginationRatio)
-        if bounds != b {
-            bounds = b
-        }
-        
-        // bring centermost item to front
-        var closestDistance = CGFloat.max
-        var closestItem: BrowserItem? = nil
-        for item in visibleItems {
-            let distance = abs(item.center.value.x - bounds.midX)
-            if distance < closestDistance {
-                closestDistance = distance
-                closestItem = item
-            }
-        }
-        if let item = closestItem {
-            bringSubviewToFront(item.view)
-        }
-        
-        updateAllItems(true)
-        updateVisibleItems()
-        
-        
-        // If present, update the cover view.
-        if let cv = coverView {
-            let cvVis = coverVisibilty.value
-            
-            let initialCenter = CGPoint(x: bounds.midX, y: bounds.midY)
-            let finalCenter = CGPoint(x: bounds.midX, y: bounds.height * 0.3 * 0.25)
-            cv.center = initialCenter.interpolatedTo(finalCenter, alpha: 1.0-Scalar(cvVis))
-            
-            let t = CGAffineTransformMakeScale(0.7 + cvVis*0.3, 0.7 + cvVis*0.3)
-            cv.transform = t
-            
-            cv.URLVisibility = CGFloat(cvVis)
-            cv.alpha = 0.5 + CGFloat(cvVis*0.5)
-        }
-        
-        var coverVisibility = 1.0 - currentIndex
-        coverVisibility = min(coverVisibility, 1.0)
-        coverVisibility = max(coverVisibility, 0.0)
-        coverVisibilty.target = coverVisibility
-    }
-    
-    private func updateAllItems(animated: Bool) {
-        for i in 0..<items.count {
-            updateItemAtIndex(i, animated: animated)
-        }
-    }
-    
-    private func updateItemAtIndex(index: Int, animated: Bool) {
-        let item = items[index]
-        guard item.gestureInProgress == false else { return }
-        
-        var center = CGPoint.zero
-        center.y = bounds.midY
-        center.x = bounds.width/2.0 + (CGFloat(index+1) * bounds.width * paginationRatio)
-        let size = bounds.size
-        
-        var transform = SimpleTransform()
-        
-        var distance = abs(center.x - bounds.midX)
-        distance = min(distance, bounds.width*paginationRatio) / bounds.width*paginationRatio
-        transform.scale = 0.7 - (0.3 * distance)
-        
-        let tension = 60.0 + Scalar(distance) * 40.0
-        
-        item.transform.configuration.tension = tension
-        item.transform.configuration.damping = 18.0
-        
-        if item == fullScreenItem {
-            transform.scale = 1.0
-            item.transform.configuration.tension = 160.0
-            item.transform.configuration.damping = 28.0
-        }
-        
-        if animated {
-            item.center.target = center
-            item.size.target = size
-            item.transform.target = transform
-        } else {
-            item.center.reset(center)
-            item.size.reset(size)
-            item.transform.reset(transform)
-        }
-    }
-
-    private func updateVisibleItems() {
-        
-        for item in items {
-            let isVisible = visibleItems.contains(item)
-            let shouldBeVisible = item.frame.intersects(bounds)
-            if isVisible && !shouldBeVisible {
-                hideItem(item)
-            } else if !isVisible && shouldBeVisible {
-                showItem(item)
-            }
-            
-            if shouldBeVisible {
-                updateViewForItemAtIndex(items.indexOf(item)!)
-            }
-        }
-        
-    }
-    
-    private func showItem(item: BrowserItem) {
-        assert(item.browserView == self)
-        assert(visibleItems.contains(item) == false)
-        visibleItems.insert(item)
-        updateViewForItemAtIndex(items.indexOf(item)!)
-        addSubview(item.view)
-        delegate?.browserView(self, didShowItem: item)
-    }
-
-    private func updateViewForItemAtIndex(index: Int) {
-        let item = items[index]
-        item.view.bounds = CGRect(origin: CGPoint.zero, size: item.size.value)
-        item.view.center = item.center.value
-        item.view.layer.anchorPoint = item.anchorPoint
-        item.view.transform = item.transform.value.affineTransform
-    }
-    
-    private func hideItem(item: BrowserItem) {
-        assert(item.browserView == self)
-        assert(visibleItems.contains(item))
-        visibleItems.remove(item)
-        item.view.removeFromSuperview()
-        delegate?.browserView(self, didHideItem: item)
-    }
-    
-    func enterFullScreen(item: BrowserItem) {
-        assert(item.browserView == self)
-        leaveFullScreen()
-        fullScreenItem = item
-        updateAllItems(true)
-        index.animateTo(CGFloat(items.indexOf(item)! + 1))
-        delegate?.browserView(self, didEnterFullScreenForItem: item)
-    }
-    
-    func leaveFullScreen() {
-        guard let item = fullScreenItem else { return }
-        fullScreenItem = nil
-        updateAllItems(true)
-        delegate?.browserView(self, didLeaveFullScreenForItem: item)
-    }
-    
-    private dynamic func pan(recognizer: UIPanGestureRecognizer) {
-        switch recognizer.state {
-        case .Began:
-            panInProgress = true
-            indexWhenPanBegan = index.value
-            index.cancelAnimation()
-        case .Changed:
-            index.value = indexWhenPanBegan - (recognizer.translationInView(self).x / bounds.width * paginationRatio)
-            break
-        case .Ended, .Cancelled:
-            panInProgress = false
-            let vel = -recognizer.velocityInView(self).x / bounds.width * paginationRatio
-            var destIndex = round(index.value + (vel/5.0))
-            if destIndex == round(index.value) {
-                if vel < 0.0 {
-                    destIndex -= 1.0
-                } else if vel > 0.0 {
-                    destIndex += 1.0
-                }
-            }
-            destIndex = min(destIndex, CGFloat(items.count))
-            destIndex = max(destIndex, CGFloat(0.0))
-            var config = SpringConfiguration()
-            config.tension = 120.0
-            config.damping = 20.0
-            config.threshold = 0.001
-            index.springTo(destIndex, initialVelocity: vel, configuration: config, completion: nil)
-        default:
-            break
-        }
-    }
-    
-}
-
-extension BrowserView: UIGestureRecognizerDelegate {
-    override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
-        if gestureRecognizer == panRecognizer && fullScreenItem != nil {
-            return false
-        }
-        return true
-    }
-}
\ No newline at end of file
diff --git a/AdvanceSample/BrowserViewController.swift b/AdvanceSample/BrowserViewController.swift
deleted file mode 100644
index e1ca914..0000000
--- a/AdvanceSample/BrowserViewController.swift
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import UIKit
-import Advance
-
-private final class DemoItem: BrowserItem {
-    let viewController: DemoViewController
-    init(viewController: DemoViewController) {
-        self.viewController = viewController
-        super.init()
-    }
-}
-
-final class BrowserViewController: UIViewController {
-    
-    let viewControllers: [DemoViewController]
-    
-    let backgroundImageView = UIImageView(frame: CGRect.zero)
-    let blurredBackgroundImageView = UIImageView(frame: CGRect.zero)
-    let backgroundDimmingView = UIView(frame: CGRect.zero)
-    
-    let blurSpring = Spring(value: CGFloat.zero)
-    
-    let browserView = BrowserView(frame: CGRect.zero)
-    
-    required init(viewControllers: [DemoViewController]) {
-        self.viewControllers = viewControllers
-        super.init(nibName: nil, bundle: nil)
-        
-        var cfg = SpringConfiguration()
-        cfg.threshold = 0.001
-        cfg.tension = 60.0
-        cfg.damping = 26.0
-        blurSpring.configuration = cfg
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        
-        
-        backgroundImageView.contentMode = .ScaleAspectFill
-        backgroundImageView.image = UIImage(named: "background")
-        view.addSubview(backgroundImageView)
-        
-        blurredBackgroundImageView.contentMode = .ScaleAspectFill
-        blurredBackgroundImageView.image = UIImage(named: "background-blurred")
-        blurredBackgroundImageView.alpha = 0.0
-        view.addSubview(blurredBackgroundImageView)
-        
-        backgroundDimmingView.backgroundColor = UIColor.blackColor()
-        backgroundDimmingView.alpha = 0.3
-        view.addSubview(backgroundDimmingView)
-        
-        let cv = CoverView(frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 300.0))
-        browserView.coverView = cv
-            
-        
-        
-        view.addSubview(browserView)
-        browserView.delegate = self
-        
-        browserView.items = viewControllers.map({ (vc) -> DemoItem in
-            return DemoItem(viewController: vc)
-        })
-        
-        blurSpring.changed.observe { [unowned self] (b) in
-            self.blurredBackgroundImageView.alpha = b
-        }
-    }
-    
-    override func viewDidLayoutSubviews() {
-        super.viewDidLayoutSubviews()
-        backgroundImageView.frame = view.bounds
-        blurredBackgroundImageView.frame = view.bounds
-        backgroundDimmingView.frame = view.bounds
-        browserView.frame = view.bounds
-    }
-    
-    override func prefersStatusBarHidden() -> Bool {
-        return true
-    }
-}
-
-extension BrowserViewController: BrowserViewDelegate {
-    func browserView(browserView: BrowserView, didShowItem item: BrowserItem) {
-        guard let item = item as? DemoItem else { fatalError() }
-        assert(item.viewController.parentViewController != self)
-        addChildViewController(item.viewController)
-        item.viewController.view.frame = item.view.bounds
-        item.view.addSubview(item.viewController.view)
-        item.viewController.didMoveToParentViewController(self)
-    }
-    
-    func browserView(browserView: BrowserView, didHideItem item: BrowserItem) {
-        guard let item = item as? DemoItem else { fatalError() }
-        assert(item.viewController.parentViewController == self)
-        item.viewController.willMoveToParentViewController(nil)
-        item.viewController.view.removeFromSuperview()
-        item.viewController.removeFromParentViewController()
-    }
-    
-    func browserView(browserView: BrowserView, didEnterFullScreenForItem item: BrowserItem) {
-        guard let item = item as? DemoItem else { fatalError() }
-        item.viewController.fullScreen = true
-
-    }
-    
-    func browserView(browserView: BrowserView, didLeaveFullScreenForItem item: BrowserItem) {
-        guard let item = item as? DemoItem else { fatalError() }
-        item.viewController.fullScreen = false
-    }
-    
-    func browserViewDidScroll(browserView: BrowserView) {
-        var blurAmount = browserView.currentIndex
-        blurAmount = min(blurAmount, 1.0)
-        blurAmount = max(blurAmount, 0.0)
-        blurSpring.target = blurAmount
-    }
-}
\ No newline at end of file
diff --git a/AdvanceSample/CoverView.swift b/AdvanceSample/CoverView.swift
deleted file mode 100644
index 14d3dad..0000000
--- a/AdvanceSample/CoverView.swift
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import UIKit
-
-final class CoverView: UIView {
-    
-    let logoView: UIImageView
-    
-    let urlLabel: UILabel
-    
-    var URLVisibility: CGFloat = 1.0 {
-        didSet {
-            urlLabel.alpha = URLVisibility
-        }
-    }
-    
-    override init(frame: CGRect) {
-        logoView = UIImageView(image: UIImage(named: "logo"))
-        logoView.tintColor = UIColor.whiteColor()
-        logoView.sizeToFit()
-        
-        urlLabel = UILabel(frame: CGRect.zero)
-        
-        var attribs: [String: AnyObject] = [:]
-        attribs[NSFontAttributeName] = UIFont.systemFontOfSize(12.0, weight: UIFontWeightRegular)
-        attribs[NSForegroundColorAttributeName] = UIColor.whiteColor()
-        
-        // attribs[NSUnderlineStyleAttributeName] = NSUnderlineStyle.StyleSingle.rawValue
-        
-        urlLabel.attributedText = NSAttributedString(string: "github.com/storehouse/Advance", attributes: attribs)
-        urlLabel.sizeToFit()
-        
-        super.init(frame: frame)
-        
-        addSubview(logoView)
-        addSubview(urlLabel)
-
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    override func layoutSubviews() {
-        super.layoutSubviews()
-        logoView.center = CGPoint(x: bounds.midX, y: bounds.midY)
-        urlLabel.center = CGPoint(x: bounds.midX, y: logoView.frame.maxY + 4.0 + urlLabel.bounds.height/2.0)
-    }
-    
-}
diff --git a/AdvanceSample/DemoViewController.swift b/AdvanceSample/DemoViewController.swift
deleted file mode 100644
index 419f9fa..0000000
--- a/AdvanceSample/DemoViewController.swift
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import UIKit
-
-class DemoViewController: UIViewController {
-    
-    var note: String {
-        get { return noteLabel.text ?? "" }
-        set {
-            noteLabel.text = newValue
-            view.setNeedsLayout()
-        }
-    }
-    
-    override var title: String? {
-        didSet {
-            titleLabel.text = title
-            view.setNeedsLayout()
-        }
-    }
-    
-    let contentView = UIView()
-    
-    private let titleLabel = UILabel()
-    
-    private let noteLabel = UILabel()
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        view.backgroundColor = UIColor(white: 1.0, alpha: 1.0)
-        
-        contentView.alpha = 0.4
-        contentView.layer.allowsGroupOpacity = false
-        contentView.userInteractionEnabled = false
-        contentView.frame = view.bounds
-        view.addSubview(contentView)
-        
-        titleLabel.font = UIFont.systemFontOfSize(32.0, weight: UIFontWeightMedium)
-        titleLabel.textColor = UIColor.darkGrayColor()
-        titleLabel.textAlignment = .Center
-        titleLabel.numberOfLines = 0
-        view.addSubview(titleLabel)
-        
-        noteLabel.font = UIFont.systemFontOfSize(16.0, weight: UIFontWeightThin)
-        noteLabel.textColor = UIColor.darkGrayColor()
-        noteLabel.textAlignment = .Center
-        noteLabel.numberOfLines = 0
-        noteLabel.alpha = 0.0
-        view.addSubview(noteLabel)
-    }
-    
-    final var fullScreen = false {
-        didSet {
-            guard fullScreen != oldValue else { return }
-            
-            UIView.animateWithDuration(0.4) {
-                if self.fullScreen {
-                    self.didEnterFullScreen()
-                } else {
-                    self.didLeaveFullScreen()
-                }
-            }
-        }
-    }
-    
-    func didEnterFullScreen() {
-        noteLabel.alpha = 1.0
-        contentView.alpha = 1.0
-        contentView.userInteractionEnabled = true
-        titleLabel.alpha = 0.0
-    }
-    
-    func didLeaveFullScreen() {
-        noteLabel.alpha = 0.0
-        contentView.alpha = 0.5
-        contentView.userInteractionEnabled = false
-        titleLabel.alpha = 1.0
-    }
-    
-    override func viewDidLayoutSubviews() {
-        super.viewDidLayoutSubviews()
-        
-        contentView.frame = view.bounds
-        
-        let labelHeight = noteLabel.sizeThatFits(CGSize(width: view.bounds.width-64.0, height: CGFloat.max)).height
-        var labelFrame = CGRect.zero
-        labelFrame.origin.x = 32.0
-        labelFrame.origin.y = 32.0
-        labelFrame.size.width = view.bounds.width - 64.0
-        labelFrame.size.height = labelHeight
-        noteLabel.frame = labelFrame
-        
-        let titleHeight = titleLabel.sizeThatFits(CGSize(width: view.bounds.width-64.0, height: CGFloat.max)).height
-        var titleFrame = CGRect.zero
-        titleFrame.origin.x = 32.0
-        titleFrame.origin.y = 32.0
-        titleFrame.size.width = view.bounds.width - 64.0
-        titleFrame.size.height = titleHeight
-        titleLabel.frame = titleFrame
-    }
-    
-}
diff --git a/AdvanceSample/DirectManipulationGestureRecognizer.swift b/AdvanceSample/DirectManipulationGestureRecognizer.swift
deleted file mode 100644
index 8207d5d..0000000
--- a/AdvanceSample/DirectManipulationGestureRecognizer.swift
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import Foundation
-import UIKit.UIGestureRecognizerSubclass
-
-private let π = CGFloat(M_PI)
-
-
-public final class DirectManipulationGestureRecognizer: UIGestureRecognizer {
-    
-    public enum PinchDirection {
-        case In
-        case Out
-        case Any
-    }
-    
-    public var requiredPinchDirection: PinchDirection = .Any
-    
-    // The state of the touches when we first saw them
-    private var preliminaryState: GestureState = GestureState()
-    
-    // The state of the touches when the recognizer successfully began
-    private var initialState: GestureState = GestureState()
-    
-    // The current and previous states, for reference.
-    private var previousState: GestureState = GestureState()
-    private var currentState: GestureState = GestureState()
-    
-    // As per the UIKit documentation, we don't retain the touches. But
-    // we do remember their address so that we can compare against them as needed.
-    private var firstTouchPointer: UnsafePointer<Void>? = nil
-    private var secondTouchPointer: UnsafePointer<Void>? = nil
-    
-    private var previousCumulativeAngle: CGFloat = 0.0
-    private var cumulativeAngle: CGFloat = 0.0
-    
-    private var lastUpdateTime: NSTimeInterval = 0.0
-    private var lastUpdateDuration: NSTimeInterval = 0.0
-    
-    public override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
-        super.touchesBegan(touches, withEvent: event)
-        guard state == .Possible else { return }
-        guard touches.count > 0 else { return }
-        
-        var touches = touches
-        
-        while touches.count > 0 {
-            let t = touches.removeFirst()
-            if firstTouchPointer == nil {
-                firstTouchPointer = unsafeAddressOf(t)
-                let state = TouchState(t: t)
-                preliminaryState.firstTouchState = state
-                initialState.firstTouchState = state
-                previousState.firstTouchState = state
-                currentState.firstTouchState = state
-            } else if secondTouchPointer == nil {
-                secondTouchPointer = unsafeAddressOf(t)
-                let state = TouchState(t: t)
-                preliminaryState.secondTouchState = state
-                initialState.secondTouchState = state
-                previousState.secondTouchState = state
-                currentState.secondTouchState = state
-            } else {
-                continue
-            }
-        }
-        
-        if requiredPinchDirection == .Any && firstTouchPointer != nil && secondTouchPointer != nil {
-            begin()
-        }
-    }
-    
-    public override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
-        super.touchesMoved(touches, withEvent: event)
-        guard state == .Possible || state == .Began || state == .Changed else { return }
-        
-        previousState = currentState
-        
-        for t in touches {
-            if unsafeAddressOf(t) == firstTouchPointer {
-                currentState.firstTouchState = TouchState(t: t)
-            }
-            
-            if unsafeAddressOf(t) == secondTouchPointer {
-                currentState.secondTouchState = TouchState(t: t)
-            }
-        }
-        
-        // If either of the touches have just begun or ended, there's a good chance 
-        // that we just began or ended the gesture. It turns out that setting the state
-        // to .Changed stomps on the notification of the gesture beginning – so
-        // from the target's perspective, the recognizer just start claiming to
-        // have changed without going through a .Began phase. We can just check
-        // to make sure that neither touch has just started or ended to avoid this.
-        if currentState.firstTouchState.phase == .Began || currentState.secondTouchState.phase == .Began {
-            return
-        }
-        
-        if currentState.firstTouchState.phase == .Ended || currentState.secondTouchState.phase == .Ended {
-            return
-        }
-        
-        if currentState.firstTouchState.phase == .Cancelled || currentState.secondTouchState.phase == .Cancelled {
-            return
-        }
-        
-        if state == .Possible {
-            guard firstTouchPointer != nil && secondTouchPointer != nil else { return }
-            
-            // How far the user has pinched since we first saw the touches.
-            let pinchDistance = currentState.distanceBetween - preliminaryState.distanceBetween
-            
-            switch requiredPinchDirection {
-            case .Any:
-                begin()
-            case .In:
-                if pinchDistance > 0.0 {
-                    begin()
-                } else if pinchDistance < 0.0 {
-                    state = .Failed
-                }
-            case .Out:
-                if pinchDistance < 0.0 {
-                    begin()
-                } else if pinchDistance > 0.0 {
-                    state = .Failed
-                }
-            }
-            
-            if state == .Possible && currentState.distanceTo(preliminaryState) > 4.0 {
-                state = .Failed
-            }
-            
-            return
-        }
-        
-        var angleDelta = currentState.angle - previousState.angle
-        if (angleDelta > π) {
-            angleDelta -= π*2.0;
-        } else if (angleDelta < -π) {
-            angleDelta += π*2.0;
-        }
-        
-        previousCumulativeAngle = cumulativeAngle
-        cumulativeAngle += angleDelta
-        
-        let currentTime = CACurrentMediaTime()
-        lastUpdateDuration = currentTime - lastUpdateTime
-        lastUpdateTime = currentTime
-        
-        assert(state == .Began || state == .Changed)
-        state = .Changed
-    }
-    
-    public override func touchesCancelled(touches: Set<UITouch>, withEvent event: UIEvent) {
-        super.touchesCancelled(touches, withEvent: event)
-        
-        
-        for t in touches {
-            if unsafeAddressOf(t) == firstTouchPointer || unsafeAddressOf(t) == secondTouchPointer {
-                if state == .Possible {
-                    state = .Failed
-                } else if state == .Began || state == .Changed {
-                    state = .Cancelled
-                }
-                return
-            }
-        }
-    }
-    
-    public override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent) {
-        super.touchesEnded(touches, withEvent: event)
-        
-        for t in touches {
-            if unsafeAddressOf(t) == firstTouchPointer || unsafeAddressOf(t) == secondTouchPointer {
-                if state == .Possible {
-                    state = .Failed
-                } else if state == .Began || state == .Changed {
-                    state = .Ended
-                }
-                return
-            }
-        }
-    }
-    
-    private func begin() {
-        guard state == .Possible else { fatalError() }
-        initialState = currentState
-        previousState = currentState
-        cumulativeAngle = 0.0
-        previousCumulativeAngle = 0.0
-        lastUpdateTime = CACurrentMediaTime()
-        lastUpdateDuration = 0.0
-        state = .Began
-    }
-    
-    override public func reset() {
-        super.reset()
-        
-        firstTouchPointer = nil
-        secondTouchPointer = nil
-        
-        preliminaryState = GestureState()
-        initialState = GestureState()
-        previousState = GestureState()
-        currentState = GestureState()
-    }
-    
-    
-    public func translationInView(view: UIView?) -> CGPoint {
-        let current = currentState.convertedToView(view)
-        let initial = initialState.convertedToView(view)
-        return current.center - initial.center
-    }
-    
-    public func translationVelocityInView(view: UIView?) -> CGPoint {
-        guard active else { return CGPoint.zero }
-        guard lastUpdateDuration > 0.0 else { return CGPoint.zero }
-        let current = currentState.convertedToView(view)
-        let previous = previousState.convertedToView(view)
-        var vel = current.center - previous.center
-        vel.x /= CGFloat(lastUpdateDuration)
-        vel.y /= CGFloat(lastUpdateDuration)
-        return vel
-    }
-    
-    public var rotation: CGFloat {
-        guard active else { return 0.0 }
-        return cumulativeAngle
-    }
-    
-    public var rotationVelocity: CGFloat {
-        guard active else { return 0.0 }
-        guard lastUpdateDuration > 0.0 else { return 0.0 }
-        let delta = cumulativeAngle - previousCumulativeAngle
-        return delta / CGFloat(lastUpdateDuration)
-    }
-    
-    public var scale: CGFloat {
-        guard active else { return 1.0 }
-        return currentState.distanceBetween / initialState.distanceBetween
-    }
-    
-    public var scaleVelocity: CGFloat {
-        guard active else { return 0.0 }
-        guard lastUpdateDuration > 0.0 else { return 0.0 }
-        let current = currentState.distanceBetween / initialState.distanceBetween
-        let previous = previousState.distanceBetween / initialState.distanceBetween
-        return ((current - previous) / CGFloat(lastUpdateDuration))
-    }
-    
-    private var active: Bool {
-        return state == .Began || state == .Changed || state == .Ended
-    }
-}
-
-
-private extension DirectManipulationGestureRecognizer {
-    struct GestureState {
-        var firstTouchState: TouchState = TouchState()
-        var secondTouchState: TouchState = TouchState()
-        
-        
-        func convertedToView(view: UIView?) -> GestureState {
-            var s = self
-            s.firstTouchState = s.firstTouchState.convertedToView(view)
-            s.secondTouchState = s.secondTouchState.convertedToView(view)
-            return s
-        }
-        
-        func distanceTo(state: GestureState) -> CGFloat {
-            return (state.center - center).distance
-        }
-        
-        var distanceBetween: CGFloat {
-            return delta.distance
-        }
-        
-        var delta: CGPoint {
-            return secondTouchState.location - firstTouchState.location
-        }
-        
-        var angle: CGFloat {
-            return delta.angle
-        }
-        
-        var center: CGPoint {
-            var p = CGPoint.zero
-            p.x = firstTouchState.location.x + ((secondTouchState.location.x - firstTouchState.location.x) * 0.5)
-            p.y = firstTouchState.location.y + ((secondTouchState.location.y - firstTouchState.location.y) * 0.5)
-            return p
-        }
-    }
-    
-    struct TouchState {
-        var location: CGPoint = CGPoint.zero
-        var phase: UITouchPhase = UITouchPhase.Began
-        
-        init() {}
-        
-        init(t: UITouch) {
-            location = t.locationInView(nil)
-            phase = t.phase
-        }
-        
-        func convertedToView(view: UIView?) -> TouchState {
-            var s = self
-            if let view = view {
-                s.location = view.convertPoint(s.location, fromView: nil)
-            }
-            return s
-        }
-        
-        func distanceTo(state: TouchState) -> CGFloat {
-            return (state.location - location).distance
-        }
-    }
-}
-
-private extension CGPoint {
-    
-    var angle: CGFloat {
-        return atan2(y, x);
-    }
-    
-    var distance: CGFloat {
-        return sqrt((x*x) + (y*y))
-    }
-    
-}
-
-private func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
-    var result = CGPoint.zero
-    result.x = lhs.x - rhs.x
-    result.y = lhs.y - rhs.y
-    return result
-}
\ No newline at end of file
diff --git a/AdvanceSample/GestureView.swift b/AdvanceSample/GestureView.swift
deleted file mode 100644
index 093807c..0000000
--- a/AdvanceSample/GestureView.swift
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import UIKit
-import Advance
-
-final class GestureView: UIView {
-    
-    let animatableCenter = Animatable(value: CGPoint.zero)
-    ;let animatableTransform = Animatable(value: SimpleTransform())
-    
-    private var centerWhenGestureBegan = CGPoint.zero
-    private var transformWhenGestureBegan = SimpleTransform.zero
-    
-    private let recognizer = DirectManipulationGestureRecognizer()
-
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-        
-        backgroundColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0)
-        
-        recognizer.addTarget(self, action: "manipulate:")
-        addGestureRecognizer(recognizer)
-        
-        animatableCenter.changed.observe { [weak self] (c) -> Void in
-            self?.center = c
-        }
-        
-        animatableTransform.changed.observe { [weak self] (t) -> Void in
-            self?.transform = t.affineTransform
-        }
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    override var frame: CGRect {
-        didSet {
-            animatableCenter.value = center
-        }
-    }
-    
-    private dynamic func manipulate(recognizer: DirectManipulationGestureRecognizer) {
-        switch recognizer.state {
-        case .Began:
-            
-            // Take the anchor point into consideration
-            let gestureLocation = recognizer.locationInView(self)
-            let newCenter = superview!.convertPoint(gestureLocation, fromView: self)
-            animatableCenter.value = newCenter
-            
-            var anchorPoint = gestureLocation
-            anchorPoint.x /= bounds.width
-            anchorPoint.y /= bounds.height
-            layer.anchorPoint = anchorPoint
-            
-            animatableTransform.cancelAnimation()
-            animatableCenter.cancelAnimation()
-            centerWhenGestureBegan = animatableCenter.value
-            transformWhenGestureBegan = animatableTransform.value
-            break
-        case .Changed:
-            var t = transformWhenGestureBegan
-            t.rotation += recognizer.rotation
-            t.scale *= recognizer.scale
-            animatableTransform.value = t
-            
-            var center = centerWhenGestureBegan
-            center.x += recognizer.translationInView(superview).x
-            center.y += recognizer.translationInView(superview).y
-            animatableCenter.value = center
-            
-            break
-        case .Ended, .Cancelled:
-            // Reset the anchor point
-            let mid = CGPoint(x: bounds.midX, y: bounds.midY)
-            let newCenter = superview!.convertPoint(mid, fromView: self)
-            animatableCenter.value = newCenter
-            layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
-            
-            var velocity = SimpleTransform.zero
-            velocity.scale = recognizer.scaleVelocity
-            velocity.rotation = recognizer.rotationVelocity
-            var config = SpringConfiguration()
-            config.threshold = 0.001
-            animatableTransform.springTo(SimpleTransform(), initialVelocity: velocity, configuration: config)
-            
-            let centerVel = recognizer.translationVelocityInView(superview)
-            var centerConfig = SpringConfiguration()
-            centerConfig.tension = 40.0
-            centerConfig.damping = 5.0
-            let c = CGPoint(x: superview!.bounds.midX, y: superview!.bounds.midY)
-            animatableCenter.springTo(c, initialVelocity: centerVel, configuration: centerConfig)
-            break
-        default:
-            break
-        }
-    }
-    
-}
diff --git a/AdvanceSample/GesturesViewController.swift b/AdvanceSample/GesturesViewController.swift
deleted file mode 100644
index 50cb533..0000000
--- a/AdvanceSample/GesturesViewController.swift
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import UIKit
-
-final class GesturesViewController: DemoViewController {
-    
-    let gestureView = GestureView()
-
-    required init() {
-        super.init(nibName: nil, bundle: nil)
-        title = "Gestures"
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        note = "Use two fingers to pick up the square."
-        
-        contentView.addSubview(gestureView)
-
-        // Do any additional setup after loading the view.
-    }
-    
-    override func viewWillAppear(animated: Bool) {
-        super.viewWillAppear(animated)
-    }
-    
-    override func viewDidLayoutSubviews() {
-        super.viewDidLayoutSubviews()
-        var b = CGRect.zero
-        b.size.width = min(view.bounds.width, view.bounds.height) - 64.0
-        b.size.height = b.size.width
-        gestureView.bounds = b
-        gestureView.animatableCenter.value = CGPoint(x: contentView.bounds.midX, y: contentView.bounds.midY)
-        gestureView.animatableTransform.value = SimpleTransform()
-    }
-
-    override func didReceiveMemoryWarning() {
-        super.didReceiveMemoryWarning()
-        // Dispose of any resources that can be recreated.
-    }
-    
-
-    /*
-    // MARK: - Navigation
-
-    // In a storyboard-based application, you will often want to do a little preparation before navigation
-    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
-        // Get the new view controller using segue.destinationViewController.
-        // Pass the selected object to the new view controller.
-    }
-    */
-
-}
diff --git a/AdvanceSample/GravityFunction.swift b/AdvanceSample/GravityFunction.swift
deleted file mode 100644
index 9b6c3bd..0000000
--- a/AdvanceSample/GravityFunction.swift
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import Advance
-import Foundation
-
-struct GravityFunction: DynamicFunctionType {
-    
-    typealias Vector = Vector2
-    
-    var target: Vector
-    
-    var minRadius = 30.0
-    
-    var threshold: Scalar = 0.1
-    
-    init(target: Vector) {
-        self.target = target
-    }
-    
-    func acceleration(value: Vector, velocity: Vector) -> Vector {
-        
-        let delta = target - value
-        let heading = atan2(delta.y, delta.x)
-        
-        var distance = hypot(delta.x, delta.y)
-        distance = max(distance, minRadius)
-        
-        let accel = 1000000.0 / (distance*distance)
-        
-        var result = Vector.zero
-        result.x = accel * cos(heading)
-        result.y = accel * sin(heading)
-        return result
-    }
-    
-    func canSettle(value: Vector, velocity: Vector) -> Bool {
-        let min = Vector(scalar: -threshold)
-        let max = Vector(scalar: threshold)
-        
-        if velocity.clamped(min: min, max: max) != velocity {
-            return false
-        }
-        
-        let valueDelta = value - target
-        if valueDelta.clamped(min: min, max: max) != valueDelta {
-            return false
-        }
-        
-        return true
-    }
-    
-    func settledValue(value: Vector, velocity: Vector) -> Vector {
-        return target
-    }
-}
\ No newline at end of file
diff --git a/AdvanceSample/GravitySimulation.swift b/AdvanceSample/GravitySimulation.swift
deleted file mode 100644
index d3a2c77..0000000
--- a/AdvanceSample/GravitySimulation.swift
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import CoreGraphics
-import Advance
-
-
-private enum GravitySimulationNode: Advanceable {
-    case Static(Vector2)
-    case Decay(DynamicSolver<DecayFunction<Vector2>>)
-    case Pull(DynamicSolver<GravityFunction>)
-    
-    var value: Vector2 {
-        switch self {
-        case .Static(let v):
-            return v
-        case .Decay(let d):
-            return d.value
-        case .Pull(let s):
-            return s.value
-        }
-    }
-    
-    var velocity: Vector2 {
-        switch self {
-        case .Static(_):
-            return Vector2.zero
-        case .Decay(let d):
-            return d.velocity
-        case .Pull(let s):
-            return s.velocity
-        }
-    }
-    
-    mutating func advance(elapsed: Double) {
-        switch self {
-        case .Static(_):
-            break
-        case .Decay(var sim):
-            sim.advance(elapsed)
-            self = sim.settled ? .Static(sim.value) : .Decay(sim)
-        case .Pull(var sim):
-            sim.advance(elapsed)
-            self = sim.settled ? .Static(sim.value) : .Pull(sim)
-        }
-    }
-    
-    var settled: Bool {
-        switch self {
-        case .Static(_):
-            return true
-        case .Pull(_), .Decay(_):
-            return false
-        }
-    }
-    
-    mutating func pullTo(point: CGPoint) {
-        if case var .Pull(sim) = self {
-            sim.function.target = point.vector
-            self = .Pull(sim)
-            return
-        }
-        
-        let f = GravityFunction(target: point.vector)
-        let solver = DynamicSolver(function: f, value: value, velocity: velocity)
-        self = .Pull(solver)
-    }
-    
-    mutating func decay() {
-        guard case let .Pull(sim) = self else { return }
-        
-        let f = DecayFunction<Vector2>()
-        let solver = DynamicSolver(function: f, value: sim.value, velocity: sim.velocity)
-        self = .Decay(solver)
-    }
-    
-    mutating func reset(to: CGPoint) {
-        self = .Static(to.vector)
-    }
-}
-
-
-struct GravitySimulation: Advanceable {
-    
-    let rows: Int = 10
-    let cols: Int = 10
-    
-    init() {
-        for r in 0..<rows {
-            nodes.append([])
-            for _ in 0..<cols {
-                let node = GravitySimulationNode.Static(Vector2(0.0, 0.0))
-                nodes[r].append(node)
-            }
-        }
-    }
-    
-    private var nodes: [[GravitySimulationNode]] = []
-    
-    mutating func reset(layoutBounds: CGRect) {
-        target = nil
-        
-        let rowHeight = (layoutBounds.size.height / CGFloat(rows-1))
-        let colWidth = (layoutBounds.size.width / CGFloat(cols-1))
-        
-        for r in 0..<rows {
-            for c in 0..<cols {
-                var p = layoutBounds.origin
-                p.x += CGFloat(c) * colWidth
-                p.y += CGFloat(r) * rowHeight
-                nodes[r][c].reset(p)
-            }
-        }
-    }
-    
-    var target: CGPoint? = nil {
-        didSet {
-            updateNodes()
-        }
-    }
-    
-    private mutating func updateNodes() {
-        for r in 0..<rows {
-            for c in 0..<cols {
-                if let t = target {
-                    nodes[r][c].pullTo(t)
-                } else {
-                    nodes[r][c].decay()
-                }
-            }
-        }
-    }
-    
-    func getPosition(row: Int, col: Int) -> CGPoint {
-        return CGPoint(vector: nodes[row][col].value)
-    }
-    
-    mutating func advance(elapsed: Double) {
-        for r in 0..<rows {
-            for c in 0..<cols {
-                nodes[r][c].advance(elapsed)
-            }
-        }
-    }
-    
-    var settled: Bool {
-        for r in 0..<rows {
-            for c in 0..<cols {
-                if nodes[r][c].settled == false {
-                    return false
-                }
-            }
-        }
-        return true
-    }
-}
\ No newline at end of file
diff --git a/AdvanceSample/GravityViewController.swift b/AdvanceSample/GravityViewController.swift
deleted file mode 100644
index 49d9b18..0000000
--- a/AdvanceSample/GravityViewController.swift
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import UIKit
-import Advance
-
-class GravityViewController: DemoViewController {
-    
-    var simulation = GravitySimulation() {
-        didSet {
-            if simulation.settled == false && subscription.paused == true {
-                subscription.paused = false
-            }
-            view.setNeedsLayout()
-        }
-    }
-    
-    private lazy var subscription: LoopSubscription = {
-        let s = Loop.shared.subscribe()
-        
-        s.advanced.observe({ [unowned self] (elapsed) -> Void in
-            self.simulation.advance(elapsed)
-            if self.simulation.settled {
-                self.subscription.paused = true
-            }
-        })
-        
-        return s
-    }()
-    
-    let resetButton = UIButton()
-    
-    private var nodeLayers: [[CALayer]] = []
-    
-    private var lastLayoutSize: CGSize = CGSize.zero
-    
-    private let recognizer = UILongPressGestureRecognizer()
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        title = "Gravity"
-        note = "Long press to add gravity."
-        
-        recognizer.minimumPressDuration = 0.3
-        recognizer.addTarget(self, action: "press:")
-        recognizer.enabled = false
-        contentView.addGestureRecognizer(recognizer)
-        
-        resetButton.setTitle("Reset", forState: UIControlState.Normal)
-        resetButton.setTitleColor(UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0), forState: .Normal)
-        resetButton.layer.cornerRadius = 4.0
-        resetButton.tintColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0)
-        resetButton.layer.borderColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0).CGColor
-        resetButton.layer.borderWidth = 1.0
-        resetButton.addTarget(self, action: "reset", forControlEvents: .TouchUpInside)
-        resetButton.alpha = 0.0
-        view.addSubview(resetButton)
-        
-        
-        for r in 0..<simulation.rows {
-            nodeLayers.append([])
-            for _ in 0..<simulation.cols {
-                let layer = CALayer()
-                layer.backgroundColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0).CGColor
-                layer.bounds = CGRect(x: 0.0, y: 0.0, width: 8.0, height: 8.0)
-                layer.cornerRadius = 4.0
-                layer.actions = ["position": NSNull()]
-                nodeLayers[r].append(layer)
-                contentView.layer.addSublayer(layer)
-            }
-        }
-    }
-    
-    override func viewDidLayoutSubviews() {
-        super.viewDidLayoutSubviews()
-        if view.bounds.size != lastLayoutSize {
-            lastLayoutSize = view.bounds.size
-            reset()
-        }
-        
-        for r in 0..<simulation.rows {
-            for c in 0..<simulation.cols {
-                let position = simulation.getPosition(r, col: c)
-                nodeLayers[r][c].position = position
-            }
-        }
-        
-        resetButton.bounds = CGRect(x: 0.0, y: 0.0, width: 120.0, height: 44.0)
-        resetButton.center = CGPoint(x: contentView.bounds.midX, y: contentView.bounds.maxY - 64.0)
-    }
-    
-    dynamic func press(recognizer: UILongPressGestureRecognizer) {
-        switch recognizer.state {
-        case .Began, .Changed:
-            simulation.target = recognizer.locationInView(view)
-        case .Ended:
-            simulation.target = nil
-        default:
-            break
-        }
-    }
-    
-    dynamic func reset() {
-        simulation.reset(view.bounds.insetBy(dx: 64.0, dy: 128.0))
-    }
-    
-    override func didEnterFullScreen() {
-        super.didEnterFullScreen()
-        recognizer.enabled = true
-        resetButton.alpha = 1.0
-    }
-    
-    override func didLeaveFullScreen() {
-        super.didLeaveFullScreen()
-        recognizer.enabled = false
-        resetButton.alpha = 0.0
-    }
-}
diff --git a/AdvanceSample/Info.plist b/AdvanceSample/Info.plist
deleted file mode 100644
index ff2910b..0000000
--- a/AdvanceSample/Info.plist
+++ /dev/null
@@ -1,38 +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>CFBundleDevelopmentRegion</key>
-	<string>en</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>APPL</string>
-	<key>CFBundleShortVersionString</key>
-	<string>1.0</string>
-	<key>CFBundleSignature</key>
-	<string>????</string>
-	<key>CFBundleVersion</key>
-	<string>1</string>
-	<key>LSRequiresIPhoneOS</key>
-	<true/>
-	<key>UILaunchStoryboardName</key>
-	<string>LaunchScreen</string>
-	<key>UIRequiredDeviceCapabilities</key>
-	<array>
-		<string>armv7</string>
-	</array>
-	<key>UIStatusBarHidden</key>
-	<true/>
-	<key>UISupportedInterfaceOrientations</key>
-	<array>
-		<string>UIInterfaceOrientationPortrait</string>
-	</array>
-</dict>
-</plist>
diff --git a/AdvanceSample/SimpleTransform.swift b/AdvanceSample/SimpleTransform.swift
deleted file mode 100644
index 5629887..0000000
--- a/AdvanceSample/SimpleTransform.swift
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import Foundation
-import CoreGraphics
-import Advance
-
-struct SimpleTransform {
-    var scale: CGFloat = 1.0
-    var rotation: CGFloat = 0.0
-    
-    init() {}
-    
-    init(scale: CGFloat, rotation: CGFloat) {
-        self.scale = scale
-        self.rotation = rotation
-    }
-    
-    var affineTransform: CGAffineTransform {
-        var t = CGAffineTransformIdentity
-        t = CGAffineTransformRotate(t, rotation)
-        t = CGAffineTransformScale(t, scale, scale)
-        return t
-    }
-}
-
-extension SimpleTransform: VectorConvertible {
-    typealias Vector = Vector2
-    
-    var vector: Vector {
-        return Vector2(Scalar(scale), Scalar(rotation))
-    }
-    
-    init(vector: Vector) {
-        scale = CGFloat(vector.x)
-        rotation = CGFloat(vector.y)
-    }
-}
-
-func ==(lhs: SimpleTransform, rhs: SimpleTransform) -> Bool {
-    return lhs.scale == rhs.scale
-        && lhs.rotation == rhs.rotation
-}
\ No newline at end of file
diff --git a/AdvanceSample/SpringConfigurationView.swift b/AdvanceSample/SpringConfigurationView.swift
deleted file mode 100644
index dfe7f7e..0000000
--- a/AdvanceSample/SpringConfigurationView.swift
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import Foundation
-import UIKit
-
-protocol SpringConfigurationViewDelegate: class {
-    func springConfigurationViewDidChange(view: SpringConfigurationView)
-}
-
-
-class SpringConfigurationView: UIView {
-    
-    weak var delegate: SpringConfigurationViewDelegate? = nil
-    
-    var tension: CGFloat {
-        get { return CGFloat(tensionSlider.slider.value) }
-        set { tensionSlider.slider.value = Float(newValue) }
-    }
-    
-    var damping: CGFloat {
-        get { return CGFloat(dampingSlider.slider.value) }
-        set { dampingSlider.slider.value = Float(newValue) }
-    }
-    
-    private let tensionSlider = LabeledSliderView()
-    private let dampingSlider = LabeledSliderView()
-    
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-        addSubview(tensionSlider)
-        addSubview(dampingSlider)
-        
-        tensionSlider.slider.addTarget(self, action: "changed", forControlEvents: .ValueChanged)
-        dampingSlider.slider.addTarget(self, action: "changed", forControlEvents: .ValueChanged)
-        
-        tensionSlider.slider.minimumValue = 1.0
-        tensionSlider.slider.maximumValue = 400.0
-        
-        dampingSlider.slider.minimumValue = 0.1
-        dampingSlider.slider.maximumValue = 80.0
-        
-        tensionSlider.slider.value = 120.0
-        dampingSlider.slider.value = 10.0
-        
-        tensionSlider.text = "Tension"
-        dampingSlider.text = "Damping"
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    override func sizeThatFits(size: CGSize) -> CGSize {
-        var s = CGSize.zero
-        s.width = size.width
-        s.height += tensionSlider.sizeThatFits(size).height
-        s.height += dampingSlider.sizeThatFits(size).height
-        return s
-    }
-
-    override func layoutSubviews() {
-        super.layoutSubviews()
-        
-        var tensionSize = tensionSlider.sizeThatFits(bounds.size)
-        tensionSize.width = bounds.width
-        tensionSlider.frame = CGRect(origin: CGPoint.zero, size: tensionSize)
-        
-        var dampingSize = dampingSlider.sizeThatFits(bounds.size)
-        dampingSize.width = bounds.width
-        dampingSlider.frame = CGRect(x: 0.0, y: tensionSlider.frame.maxY, width: bounds.width, height: dampingSize.height)
-    }
-    
-    private dynamic func changed() {
-        delegate?.springConfigurationViewDidChange(self)
-    }
-}
-
-
-private class LabeledSliderView: UIView {
-    
-    var labelWidth: CGFloat = 90.0 {
-        didSet { setNeedsLayout() }
-    }
-    
-    var gutterWidth: CGFloat = 20.0 {
-        didSet { setNeedsLayout() }
-    }
-    
-    var sideMargin: CGFloat = 12.0 {
-        didSet { setNeedsLayout() }
-    }
-    
-    var text: String {
-        get { return label.text ?? "" }
-        set { label.text = newValue }
-    }
-    
-    private let label: UILabel
-    private let slider: UISlider
-    
-    override init(frame: CGRect) {
-        label = UILabel()
-        slider = UISlider()
-        super.init(frame: frame)
-        
-        slider.minimumTrackTintColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0)
-        
-        label.text = "Untitled"
-        label.textColor = UIColor.darkGrayColor()
-        
-        addSubview(label)
-        addSubview(slider)
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    private override func sizeThatFits(size: CGSize) -> CGSize {
-        var s = size
-        s.height = 44.0
-        return s
-    }
-    
-    private override func layoutSubviews() {
-        super.layoutSubviews()
-        
-        var labelSize = label.sizeThatFits(bounds.size)
-        labelSize.width = min(labelSize.width, labelWidth)
-        label.bounds = CGRect(origin: CGPoint.zero, size: labelSize)
-        label.center = CGPoint(x: sideMargin + labelSize.width/2.0, y: bounds.midY)
-        
-        var sliderFrame = CGRect.zero
-        sliderFrame.size.height = slider.sizeThatFits(bounds.size).height
-        sliderFrame.size.width = bounds.width - (sideMargin * 2.0) - labelWidth - gutterWidth
-        sliderFrame.origin.x = sideMargin + labelWidth + gutterWidth
-        sliderFrame.origin.y = (bounds.height - sliderFrame.height) / 2.0
-        slider.frame = sliderFrame
-    }
-}
\ No newline at end of file
diff --git a/AdvanceSample/SpringView.swift b/AdvanceSample/SpringView.swift
deleted file mode 100644
index 3cd70d0..0000000
--- a/AdvanceSample/SpringView.swift
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import Foundation
-import UIKit
-import Advance
-
-final class SpringView: UIView {
-    
-    let centerSpring = Spring(value: CGPoint.zero)
-    
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-        backgroundColor = UIColor(red: 0.0, green: 196.0/255.0, blue: 1.0, alpha: 1.0)
-        
-        let t = arc4random_uniform(120 - 20) + 20
-        let d = arc4random_uniform(20 - 4) + 4
-        
-        centerSpring.configuration.tension = Scalar(t)
-        centerSpring.configuration.damping = Scalar(d)
-        
-        centerSpring.changed.observe { [unowned self] (c) -> Void in
-            self.center = c
-        }
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    override func layoutSubviews() {
-        super.layoutSubviews()
-        layer.cornerRadius = min(bounds.width, bounds.height) / 2.0
-    }
-    
-}
\ No newline at end of file
diff --git a/AdvanceSample/SpringsViewController.swift b/AdvanceSample/SpringsViewController.swift
deleted file mode 100644
index d996e44..0000000
--- a/AdvanceSample/SpringsViewController.swift
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-
-Copyright (c) 2016, Storehouse Media 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.
-
-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.
-
-*/
-
-import Foundation
-import UIKit
-import Advance
-
-class SpringsViewController: DemoViewController {
-    
-    private let springView = SpringView()
-    
-    private let configView = SpringConfigurationView()
-    
-    private let tapRecognizer = UITapGestureRecognizer()
-    
-    required init() {
-        super.init(nibName: nil, bundle:    nil)
-        title = "Spring"
-        note = "Tap anywhere to move the dot using a spring."
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        view.backgroundColor = UIColor.whiteColor()
-        
-        view.addGestureRecognizer(tapRecognizer)
-        tapRecognizer.addTarget(self, action: "tap:")
-        tapRecognizer.enabled = false
-        
-        springView.bounds = CGRect(x: 0.0, y: 0.0, width: 24.0, height: 24.0)
-        
-        configView.delegate = self
-        configView.alpha = 0.0
-        contentView.addSubview(springView)
-        contentView.addSubview(configView)
-        
-        updateSprings()
-    }
-    
-    override func viewDidLayoutSubviews() {
-        super.viewDidLayoutSubviews()
-        
-        var configFrame = CGRect.zero
-        configFrame.size.width = view.bounds.width
-        configFrame.size.height = configView.sizeThatFits(view.bounds.size).height
-        configFrame.origin.y = view.bounds.maxY - configFrame.height - bottomLayoutGuide.length
-        configView.frame = configFrame
-    }
-    
-    dynamic func tap(recognizer: UITapGestureRecognizer) {
-        let point = recognizer.locationInView(view)
-        springView.centerSpring.target = point
-    }
-    
-    override func viewWillAppear(animated: Bool) {
-        super.viewWillAppear(animated)
-        springView.centerSpring.reset(CGPoint(x: view.bounds.midX, y: view.bounds.midY))
-    }
-    
-    private func updateSprings() {
-        springView.centerSpring.configuration.tension = Scalar(configView.tension)
-        springView.centerSpring.configuration.damping = Scalar(configView.damping)
-    }
-    
-    override func didEnterFullScreen() {
-        super.didEnterFullScreen()
-        configView.alpha = 1.0
-        tapRecognizer.enabled = true
-    }
-    
-    override func didLeaveFullScreen() {
-        super.didLeaveFullScreen()
-        configView.alpha = 0.0
-        springView.centerSpring.target = CGPoint(x: contentView.bounds.midX, y: contentView.bounds.midY)
-        tapRecognizer.enabled = false
-    }
-}
-
-
-extension SpringsViewController: SpringConfigurationViewDelegate {
-    func springConfigurationViewDidChange(view: SpringConfigurationView) {
-        updateSprings()
-    }
-}
\ No newline at end of file
diff --git a/AdvanceTests-OSX/Info.plist b/AdvanceTests-OSX/Info.plist
new file mode 100644
index 0000000..ba72822
--- /dev/null
+++ b/AdvanceTests-OSX/Info.plist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/AdvanceTests-iOS/BasicAnimationTests.swift b/AdvanceTests-iOS/BasicAnimationTests.swift
new file mode 100644
index 0000000..683d233
--- /dev/null
+++ b/AdvanceTests-iOS/BasicAnimationTests.swift
@@ -0,0 +1,34 @@
+import XCTest
+@testable import Advance
+
+
+class BasicAnimationTests: XCTestCase {
+    
+    let animation = BasicAnimation(from: Scalar(0.0), to: Scalar(10.0), duration: 2.0, timingFunction: LinearTimingFunction())
+
+    func testDuration() {
+        var a = animation
+        var elapsed: Double = 0
+        while true {
+            elapsed += 0.1
+            a.advance(0.1)
+            guard elapsed < a.duration else { break }
+            XCTAssert(a.finished == false)
+        }
+        XCTAssert(a.finished == true)
+    }
+    
+    func testInterpolation() {
+        var a = animation
+        var elapsed: Double = 0
+        while true {
+            elapsed += 0.1
+            a.advance(0.1)
+            guard elapsed < a.duration else { break }
+            let current = a.from.interpolatedTo(a.to, alpha: elapsed/a.duration)
+            XCTAssert(a.value == current)
+        }
+        XCTAssert(a.value == a.to)
+    }
+
+}
diff --git a/AdvanceTests-iOS/CAMediaTimingFunctionTests.swift b/AdvanceTests-iOS/CAMediaTimingFunctionTests.swift
new file mode 100644
index 0000000..de3ba56
--- /dev/null
+++ b/AdvanceTests-iOS/CAMediaTimingFunctionTests.swift
@@ -0,0 +1,20 @@
+import XCTest
+@testable import Advance
+
+
+class CAMediaTimingFunctionTests : XCTestCase {
+    func testConversion() {
+        let p1x: Float = 0.42
+        let p1y: Float = 0.0
+        let p2x: Float = 0.58
+        let p2y: Float = 1.0
+        
+        let ca = CAMediaTimingFunction(controlPoints: p1x, p1y, p2x, p2y)
+        let ub = ca.unitBezier
+        
+        XCTAssertEqual(ub.p1x, Scalar(p1x))
+        XCTAssertEqual(ub.p1y, Scalar(p1y))
+        XCTAssertEqual(ub.p2x, Scalar(p2x))
+        XCTAssertEqual(ub.p2y, Scalar(p2y))
+    }
+}
diff --git a/AdvanceTests-iOS/DisplayLinkTests.swift b/AdvanceTests-iOS/DisplayLinkTests.swift
new file mode 100644
index 0000000..ae87b88
--- /dev/null
+++ b/AdvanceTests-iOS/DisplayLinkTests.swift
@@ -0,0 +1,78 @@
+import XCTest
+@testable import Advance
+
+
+class DisplayLinkTests : XCTestCase {
+    
+    var displayLink: DisplayLink! = nil
+    
+    override func setUp() {
+        displayLink = DisplayLink()
+    }
+    
+    override func tearDown() {
+        displayLink = nil
+    }
+    
+    func testCallback() {
+        let exp = expectationWithDescription("callback")
+        
+        displayLink.callback = { (frame) in
+            exp.fulfill()
+        }
+        
+        displayLink.paused = false
+        
+        waitForExpectationsWithTimeout(0.5) { (error) -> Void in
+            guard error == nil else { XCTFail(); return }
+        }
+    }
+    
+    func testPausing() {
+        displayLink.paused = false
+        
+        var gotCallback = false
+        
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
+            self.displayLink.paused = true
+            self.displayLink.callback = { (frame) in
+                gotCallback = true
+            }
+        }
+        
+        let timeoutDate = NSDate(timeIntervalSinceNow: 1.0)
+        
+        repeat {
+            NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: timeoutDate)
+            if timeoutDate.timeIntervalSinceNow <= 0.0 {
+                break
+            }
+        } while true
+        
+        XCTAssertEqual(gotCallback, false)
+    }
+
+    func testTimestamp() {
+        let exp = expectationWithDescription("callback")
+        
+        var callbacks = 0
+        var lastTimestamp: Double = 0
+        
+        displayLink.callback = { (frame) in
+            XCTAssertTrue(frame.timestamp > lastTimestamp, "timestamp \(frame.timestamp) was not larger than \(lastTimestamp) (frame #\(callbacks))")
+            lastTimestamp = frame.timestamp
+            
+            if callbacks == 10 { // test 10 frames before fulfilling
+                exp.fulfill()
+            }
+            
+            callbacks += 1
+        }
+        
+        displayLink.paused = false
+        
+        waitForExpectationsWithTimeout(0.5) { (error) -> Void in
+            guard error == nil else { XCTFail(); return }
+        }
+    }
+}
diff --git a/AdvanceTests-iOS/EventTests.swift b/AdvanceTests-iOS/EventTests.swift
new file mode 100644
index 0000000..d10410b
--- /dev/null
+++ b/AdvanceTests-iOS/EventTests.swift
@@ -0,0 +1,48 @@
+import XCTest
+@testable import Advance
+
+
+class EventTests : XCTestCase {
+    func testEvent() {
+        let payload = 123
+        let event = Event<Int>()
+        
+        let exp1 = expectationWithDescription("non-keyed")
+        let exp2 = expectationWithDescription("keyed")
+        
+        event.observe { (p) -> Void in
+            XCTAssertEqual(p, payload)
+            exp1.fulfill()
+        }
+        
+        event.observe({ (p) -> Void in
+            XCTAssertEqual(p, payload)
+            exp2.fulfill()
+            }, key: "keyed")
+        
+        event.fire(payload)
+        XCTAssertFalse(event.closed)
+        
+        waitForExpectationsWithTimeout(3.0) { (error) -> Void in
+            guard error == nil else { XCTFail(); return }
+        }
+    }
+    
+    func testClosing() {
+        let payload = 123
+        let event = Event<Int>()
+        let exp = expectationWithDescription("exp")
+        
+        event.observe { (p) -> Void in
+            XCTAssertEqual(p, payload)
+            exp.fulfill()
+        }
+        
+        event.close(payload)
+        XCTAssertTrue(event.closed)
+        
+        waitForExpectationsWithTimeout(3.0) { (error) -> Void in
+            guard error == nil else { XCTFail(); return }
+        }
+    }
+}
diff --git a/AdvanceTests-iOS/Info.plist b/AdvanceTests-iOS/Info.plist
new file mode 100644
index 0000000..ba72822
--- /dev/null
+++ b/AdvanceTests-iOS/Info.plist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/AdvanceTests-iOS/UnitBezierTests.swift b/AdvanceTests-iOS/UnitBezierTests.swift
new file mode 100644
index 0000000..1562a61
--- /dev/null
+++ b/AdvanceTests-iOS/UnitBezierTests.swift
@@ -0,0 +1,29 @@
+import XCTest
+@testable import Advance
+
+
+class UnitBezierTests : XCTestCase {
+    let eps: Scalar = 0.001
+    
+    func testLinear() {
+        let values: [Scalar] = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
+        let b = UnitBezier(p1x: 0.0, p1y: 0.0, p2x: 1.0, p2y: 1.0)
+        
+        for i in 0..<values.count {
+            let v = b.solve(Scalar(i) / Scalar(values.count - 1), epsilon: eps)
+            let expected = values[i]
+            XCTAssertEqualWithAccuracy(v, expected, accuracy: eps)
+        }
+    }
+    
+    func testCurve() {
+        let values: [Scalar] = [0, 0.01965137076241203, 0.08141362197218191, 0.1871705774437696, 0.331832067207064, 0.5, 0.668167932792936, 0.8128294225562304, 0.9185863780278181, 0.980348629237588, 1]
+        let b = UnitBezier(p1x: 0.42, p1y: 0.0, p2x: 0.58, p2y: 1.0) // ease in/out
+        
+        for i in 0..<values.count {
+            let v = b.solve(Scalar(i) / Scalar(values.count - 1), epsilon: eps)
+            let expected = values[i]
+            XCTAssertEqualWithAccuracy(v, expected, accuracy: eps)
+        }
+    }
+}
diff --git a/AdvanceTests-iOS/VectorConvenienceTests.swift b/AdvanceTests-iOS/VectorConvenienceTests.swift
new file mode 100644
index 0000000..bef553b
--- /dev/null
+++ b/AdvanceTests-iOS/VectorConvenienceTests.swift
@@ -0,0 +1,47 @@
+import XCTest
+@testable import Advance
+
+
+class VectorConvenienceTests : XCTestCase {
+    func testCGFloatConversion() {
+        let f = CGFloat(12.0)
+        let v = f.vector
+        let f2 = CGFloat(vector: v)
+        XCTAssert(f == f2)
+    }
+    
+    func testDoubleConversion() {
+        let d = Double(12.0)
+        let v = d.vector
+        let d2 = Double(vector: v)
+        XCTAssert(d == d2)
+    }
+    
+    func testCGPointConversion() {
+        let p = CGPoint(x: 12.0, y: 60.0)
+        let v = p.vector
+        let p2 = CGPoint(vector: v)
+        XCTAssert(p == p2)
+    }
+    
+    func testCGSizeConversion() {
+        let s = CGSize(width: 12.0, height: 60.0)
+        let v = s.vector
+        let s2 = CGSize(vector: v)
+        XCTAssert(s == s2)
+    }
+    
+    func testCGVectorConversion() {
+        let cg = CGVector(dx: 12.0, dy: 60.0)
+        let v = cg.vector
+        let cg2 = CGVector(vector: v)
+        XCTAssert(cg == cg2)
+    }
+    
+    func testCGRectConversion() {
+        let r = CGRect(x: 12.0, y: 60.0, width: 100.0, height: 3.0)
+        let v = r.vector
+        let r2 = CGRect(vector: v)
+        XCTAssert(r == r2)
+    }
+}
diff --git a/AdvanceTests-iOS/VectorTypeTests.swift b/AdvanceTests-iOS/VectorTypeTests.swift
new file mode 100644
index 0000000..8d56d35
--- /dev/null
+++ b/AdvanceTests-iOS/VectorTypeTests.swift
@@ -0,0 +1,113 @@
+import XCTest
+@testable import Advance
+
+
+class VectorTypeTests: XCTestCase {
+    func testVector1() {
+        XCTAssert(Vector1.length == 1)
+        VectorTester<Vector1>.runTests()
+    }
+    
+    func testVector2() {
+        XCTAssert(Vector2.length == 2)
+        VectorTester<Vector2>.runTests()
+    }
+    
+    func testVector3() {
+        XCTAssert(Vector3.length == 3)
+        VectorTester<Vector3>.runTests()
+    }
+    
+    func testVector4() {
+        XCTAssert(Vector4.length == 4)
+        VectorTester<Vector4>.runTests()
+    }
+}
+
+
+struct VectorTester<T: VectorType> {
+    static func runTests() {
+        testScalarInit()
+        testZero()
+        testSubscripting()
+        testEquatable()
+        testClamp()
+        testInterpolatable()
+        testMath()
+    }
+    
+    static func testScalarInit() {
+        let v = T(scalar: 5.2)
+        for i in 0..<T.length {
+            XCTAssert(v[i] == 5.2)
+        }
+    }
+    
+    static func testZero() {
+        XCTAssert(T(scalar: 0.0) == T.zero)
+    }
+    
+    static func testSubscripting() {
+        var v = T.zero
+        for i in 0..<T.length {
+            v[i] = Scalar(i)
+        }
+        for i in 0..<T.length {
+            XCTAssert(v[i] == Scalar(i))
+        }
+    }
+    
+    static func testEquatable() {
+        let v1 = T(scalar: 123.0)
+        let v2 = T(scalar: 123.0)
+        let v3 = T(scalar: 10.0)
+        XCTAssert(v1 == v2)
+        XCTAssert(v1 != v3)
+    }
+    
+    static func testClamp() {
+        let min = T(scalar: -10.0)
+        let max = T(scalar: 20.0)
+        
+        let v1 = T(scalar: -20.0)
+        let v2 = T(scalar: 30.0)
+        let v3 = T(scalar: 15.0)
+        
+        XCTAssert(v1.clamped(min: min, max: max) == min)
+        XCTAssert(v2.clamped(min: min, max: max) == max)
+        XCTAssert(v3.clamped(min: min, max: max) == v3)
+    }
+    
+    static func testInterpolatable() {
+        let v1 = T(scalar: 0.0)
+        let v2 = T(scalar: 10.0)
+        XCTAssert(v1.interpolatedTo(v2, alpha: 0.0) == v1)
+        XCTAssert(v1.interpolatedTo(v2, alpha: 0.55) == T(scalar: 5.5))
+        XCTAssert(v1.interpolatedTo(v2, alpha: 1.0) == v2)
+    }
+    
+    static func testMath() {
+        let s1 = Scalar(9.0)
+        let s2 = Scalar(17.3)
+        let v1 = T(scalar: s1)
+        let v2 = T(scalar: s2)
+        
+        XCTAssert(v1 + v2 == T(scalar: s1 + s2))
+        XCTAssert(v1 - v2 == T(scalar: s1 - s2))
+        XCTAssert(v1 * v2 == T(scalar: s1 * s2))
+        XCTAssert(v1 / v2 == T(scalar: s1 / s2))
+        
+        XCTAssert(Scalar(2.0) * v2 == T(scalar: 2.0 * s2))
+        
+        func testInPlaceMath(function: (inout T, T) -> Void, expectedValue: T) {
+            var m = v1
+            function(&m, v2)
+            XCTAssert(m == expectedValue)
+        }
+        
+        testInPlaceMath(+=, expectedValue: v1 + v2)
+        testInPlaceMath(-=, expectedValue: v1 - v2)
+        testInPlaceMath(*=, expectedValue: v1 * v2)
+        testInPlaceMath(/=, expectedValue: v1 / v2)
+    }
+}
diff --git a/AdvanceTests/BasicAnimationTests.swift b/AdvanceTests/BasicAnimationTests.swift
deleted file mode 100644
index 683d233..0000000
--- a/AdvanceTests/BasicAnimationTests.swift
+++ /dev/null
@@ -1,34 +0,0 @@
-import XCTest
-@testable import Advance
-
-
-class BasicAnimationTests: XCTestCase {
-    
-    let animation = BasicAnimation(from: Scalar(0.0), to: Scalar(10.0), duration: 2.0, timingFunction: LinearTimingFunction())
-
-    func testDuration() {
-        var a = animation
-        var elapsed: Double = 0
-        while true {
-            elapsed += 0.1
-            a.advance(0.1)
-            guard elapsed < a.duration else { break }
-            XCTAssert(a.finished == false)
-        }
-        XCTAssert(a.finished == true)
-    }
-    
-    func testInterpolation() {
-        var a = animation
-        var elapsed: Double = 0
-        while true {
-            elapsed += 0.1
-            a.advance(0.1)
-            guard elapsed < a.duration else { break }
-            let current = a.from.interpolatedTo(a.to, alpha: elapsed/a.duration)
-            XCTAssert(a.value == current)
-        }
-        XCTAssert(a.value == a.to)
-    }
-
-}
diff --git a/AdvanceTests/CAMediaTimingFunctionTests.swift b/AdvanceTests/CAMediaTimingFunctionTests.swift
deleted file mode 100644
index de3ba56..0000000
--- a/AdvanceTests/CAMediaTimingFunctionTests.swift
+++ /dev/null
@@ -1,20 +0,0 @@
-import XCTest
-@testable import Advance
-
-
-class CAMediaTimingFunctionTests : XCTestCase {
-    func testConversion() {
-        let p1x: Float = 0.42
-        let p1y: Float = 0.0
-        let p2x: Float = 0.58
-        let p2y: Float = 1.0
-        
-        let ca = CAMediaTimingFunction(controlPoints: p1x, p1y, p2x, p2y)
-        let ub = ca.unitBezier
-        
-        XCTAssertEqual(ub.p1x, Scalar(p1x))
-        XCTAssertEqual(ub.p1y, Scalar(p1y))
-        XCTAssertEqual(ub.p2x, Scalar(p2x))
-        XCTAssertEqual(ub.p2y, Scalar(p2y))
-    }
-}
diff --git a/AdvanceTests/EventTests.swift b/AdvanceTests/EventTests.swift
deleted file mode 100644
index d10410b..0000000
--- a/AdvanceTests/EventTests.swift
+++ /dev/null
@@ -1,48 +0,0 @@
-import XCTest
-@testable import Advance
-
-
-class EventTests : XCTestCase {
-    func testEvent() {
-        let payload = 123
-        let event = Event<Int>()
-        
-        let exp1 = expectationWithDescription("non-keyed")
-        let exp2 = expectationWithDescription("keyed")
-        
-        event.observe { (p) -> Void in
-            XCTAssertEqual(p, payload)
-            exp1.fulfill()
-        }
-        
-        event.observe({ (p) -> Void in
-            XCTAssertEqual(p, payload)
-            exp2.fulfill()
-            }, key: "keyed")
-        
-        event.fire(payload)
-        XCTAssertFalse(event.closed)
-        
-        waitForExpectationsWithTimeout(3.0) { (error) -> Void in
-            guard error == nil else { XCTFail(); return }
-        }
-    }
-    
-    func testClosing() {
-        let payload = 123
-        let event = Event<Int>()
-        let exp = expectationWithDescription("exp")
-        
-        event.observe { (p) -> Void in
-            XCTAssertEqual(p, payload)
-            exp.fulfill()
-        }
-        
-        event.close(payload)
-        XCTAssertTrue(event.closed)
-        
-        waitForExpectationsWithTimeout(3.0) { (error) -> Void in
-            guard error == nil else { XCTFail(); return }
-        }
-    }
-}
diff --git a/AdvanceTests/Info.plist b/AdvanceTests/Info.plist
deleted file mode 100644
index ba72822..0000000
--- a/AdvanceTests/Info.plist
+++ /dev/null
@@ -1,24 +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>CFBundleDevelopmentRegion</key>
-	<string>en</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>BNDL</string>
-	<key>CFBundleShortVersionString</key>
-	<string>1.0</string>
-	<key>CFBundleSignature</key>
-	<string>????</string>
-	<key>CFBundleVersion</key>
-	<string>1</string>
-</dict>
-</plist>
diff --git a/AdvanceTests/UnitBezierTests.swift b/AdvanceTests/UnitBezierTests.swift
deleted file mode 100644
index 1562a61..0000000
--- a/AdvanceTests/UnitBezierTests.swift
+++ /dev/null
@@ -1,29 +0,0 @@
-import XCTest
-@testable import Advance
-
-
-class UnitBezierTests : XCTestCase {
-    let eps: Scalar = 0.001
-    
-    func testLinear() {
-        let values: [Scalar] = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
-        let b = UnitBezier(p1x: 0.0, p1y: 0.0, p2x: 1.0, p2y: 1.0)
-        
-        for i in 0..<values.count {
-            let v = b.solve(Scalar(i) / Scalar(values.count - 1), epsilon: eps)
-            let expected = values[i]
-            XCTAssertEqualWithAccuracy(v, expected, accuracy: eps)
-        }
-    }
-    
-    func testCurve() {
-        let values: [Scalar] = [0, 0.01965137076241203, 0.08141362197218191, 0.1871705774437696, 0.331832067207064, 0.5, 0.668167932792936, 0.8128294225562304, 0.9185863780278181, 0.980348629237588, 1]
-        let b = UnitBezier(p1x: 0.42, p1y: 0.0, p2x: 0.58, p2y: 1.0) // ease in/out
-        
-        for i in 0..<values.count {
-            let v = b.solve(Scalar(i) / Scalar(values.count - 1), epsilon: eps)
-            let expected = values[i]
-            XCTAssertEqualWithAccuracy(v, expected, accuracy: eps)
-        }
-    }
-}
diff --git a/AdvanceTests/VectorConvenienceTests.swift b/AdvanceTests/VectorConvenienceTests.swift
deleted file mode 100644
index bef553b..0000000
--- a/AdvanceTests/VectorConvenienceTests.swift
+++ /dev/null
@@ -1,47 +0,0 @@
-import XCTest
-@testable import Advance
-
-
-class VectorConvenienceTests : XCTestCase {
-    func testCGFloatConversion() {
-        let f = CGFloat(12.0)
-        let v = f.vector
-        let f2 = CGFloat(vector: v)
-        XCTAssert(f == f2)
-    }
-    
-    func testDoubleConversion() {
-        let d = Double(12.0)
-        let v = d.vector
-        let d2 = Double(vector: v)
-        XCTAssert(d == d2)
-    }
-    
-    func testCGPointConversion() {
-        let p = CGPoint(x: 12.0, y: 60.0)
-        let v = p.vector
-        let p2 = CGPoint(vector: v)
-        XCTAssert(p == p2)
-    }
-    
-    func testCGSizeConversion() {
-        let s = CGSize(width: 12.0, height: 60.0)
-        let v = s.vector
-        let s2 = CGSize(vector: v)
-        XCTAssert(s == s2)
-    }
-    
-    func testCGVectorConversion() {
-        let cg = CGVector(dx: 12.0, dy: 60.0)
-        let v = cg.vector
-        let cg2 = CGVector(vector: v)
-        XCTAssert(cg == cg2)
-    }
-    
-    func testCGRectConversion() {
-        let r = CGRect(x: 12.0, y: 60.0, width: 100.0, height: 3.0)
-        let v = r.vector
-        let r2 = CGRect(vector: v)
-        XCTAssert(r == r2)
-    }
-}
diff --git a/AdvanceTests/VectorTypeTests.swift b/AdvanceTests/VectorTypeTests.swift
deleted file mode 100644
index 8d56d35..0000000
--- a/AdvanceTests/VectorTypeTests.swift
+++ /dev/null
@@ -1,113 +0,0 @@
-import XCTest
-@testable import Advance
-
-
-class VectorTypeTests: XCTestCase {
-    func testVector1() {
-        XCTAssert(Vector1.length == 1)
-        VectorTester<Vector1>.runTests()
-    }
-    
-    func testVector2() {
-        XCTAssert(Vector2.length == 2)
-        VectorTester<Vector2>.runTests()
-    }
-    
-    func testVector3() {
-        XCTAssert(Vector3.length == 3)
-        VectorTester<Vector3>.runTests()
-    }
-    
-    func testVector4() {
-        XCTAssert(Vector4.length == 4)
-        VectorTester<Vector4>.runTests()
-    }
-}
-
-
-struct VectorTester<T: VectorType> {
-    static func runTests() {
-        testScalarInit()
-        testZero()
-        testSubscripting()
-        testEquatable()
-        testClamp()
-        testInterpolatable()
-        testMath()
-    }
-    
-    static func testScalarInit() {
-        let v = T(scalar: 5.2)
-        for i in 0..<T.length {
-            XCTAssert(v[i] == 5.2)
-        }
-    }
-    
-    static func testZero() {
-        XCTAssert(T(scalar: 0.0) == T.zero)
-    }
-    
-    static func testSubscripting() {
-        var v = T.zero
-        for i in 0..<T.length {
-            v[i] = Scalar(i)
-        }
-        for i in 0..<T.length {
-            XCTAssert(v[i] == Scalar(i))
-        }
-    }
-    
-    static func testEquatable() {
-        let v1 = T(scalar: 123.0)
-        let v2 = T(scalar: 123.0)
-        let v3 = T(scalar: 10.0)
-        XCTAssert(v1 == v2)
-        XCTAssert(v1 != v3)
-    }
-    
-    static func testClamp() {
-        let min = T(scalar: -10.0)
-        let max = T(scalar: 20.0)
-        
-        let v1 = T(scalar: -20.0)
-        let v2 = T(scalar: 30.0)
-        let v3 = T(scalar: 15.0)
-        
-        XCTAssert(v1.clamped(min: min, max: max) == min)
-        XCTAssert(v2.clamped(min: min, max: max) == max)
-        XCTAssert(v3.clamped(min: min, max: max) == v3)
-    }
-    
-    static func testInterpolatable() {
-        let v1 = T(scalar: 0.0)
-        let v2 = T(scalar: 10.0)
-        XCTAssert(v1.interpolatedTo(v2, alpha: 0.0) == v1)
-        XCTAssert(v1.interpolatedTo(v2, alpha: 0.55) == T(scalar: 5.5))
-        XCTAssert(v1.interpolatedTo(v2, alpha: 1.0) == v2)
-    }
-    
-    static func testMath() {
-        let s1 = Scalar(9.0)
-        let s2 = Scalar(17.3)
-        let v1 = T(scalar: s1)
-        let v2 = T(scalar: s2)
-        
-        XCTAssert(v1 + v2 == T(scalar: s1 + s2))
-        XCTAssert(v1 - v2 == T(scalar: s1 - s2))
-        XCTAssert(v1 * v2 == T(scalar: s1 * s2))
-        XCTAssert(v1 / v2 == T(scalar: s1 / s2))
-        
-        XCTAssert(Scalar(2.0) * v2 == T(scalar: 2.0 * s2))
-        
-        func testInPlaceMath(function: (inout T, T) -> Void, expectedValue: T) {
-            var m = v1
-            function(&m, v2)
-            XCTAssert(m == expectedValue)
-        }
-        
-        testInPlaceMath(+=, expectedValue: v1 + v2)
-        testInPlaceMath(-=, expectedValue: v1 - v2)
-        testInPlaceMath(*=, expectedValue: v1 * v2)
-        testInPlaceMath(/=, expectedValue: v1 / v2)
-    }
-}
