Browse Source

Merge branch 'develop'

Robert Payne 11 years ago
parent
commit
f37b56b7a2
7 changed files with 299 additions and 129 deletions
  1. 3 1
      .gitignore
  2. 6 0
      CHANGELOG.md
  3. 4 0
      Snap.xcodeproj/project.pbxproj
  4. 105 78
      Snap/Constraint.swift
  5. 31 36
      Snap/ConstraintMaker.swift
  6. 10 7
      Snap/View+Snap.swift
  7. 140 7
      SnapTests/SnapTests.swift

+ 3 - 1
.gitignore

@@ -1,4 +1,6 @@
 project.xcworkspace
 xcuserdata
 Examples/
-.DS_Store
+.DS_Store
+Gemfile
+Gemfile.lock

+ 6 - 0
CHANGELOG.md

@@ -1,6 +1,12 @@
 Beta
 =======
 
+# Next Release
+
+* Re-worked some internal API to allow for future updates
+* Added `snp_prepareConstraints -> [Constraint]` which allows pre-building of constraints
+* Added a fatal error to `and` when it is used after relation has been set
+
 # 0.0.6 - February 11th, 2015
 
 * Renamed `maker` to `make` in all block APIs

+ 4 - 0
Snap.xcodeproj/project.pbxproj

@@ -7,6 +7,8 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		EEAED5481A8F56A500777EF9 /* Snap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEBCC9D819CC627D0083B827 /* Snap.framework */; };
+		EEAED5491A8F56BF00777EF9 /* SnapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE91728C19CB304E007888CF /* SnapTests.swift */; };
 		EEBCC9F019CC64F80083B827 /* EdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBCC9EF19CC64F70083B827 /* EdgeInsets.swift */; };
 		EEBCC9F219CC65050083B827 /* View+Snap.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBCC9F119CC65040083B827 /* View+Snap.swift */; };
 		EEBCC9F419CC65110083B827 /* ConstraintAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBCC9F319CC65110083B827 /* ConstraintAttributes.swift */; };
@@ -46,6 +48,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EEAED5481A8F56A500777EF9 /* Snap.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -233,6 +236,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EEAED5491A8F56BF00777EF9 /* SnapTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 105 - 78
Snap/Constraint.swift

@@ -31,19 +31,24 @@ import AppKit
  * Constraint is a single item that defines all the properties for a single ConstraintMaker chain
  */
 public class Constraint {
-    public var left: Constraint { return addConstraint(ConstraintAttributes.Left) }
-    public var top: Constraint { return addConstraint(ConstraintAttributes.Top) }
-    public var right: Constraint { return addConstraint(ConstraintAttributes.Right) }
-    public var bottom: Constraint { return addConstraint(ConstraintAttributes.Bottom) }
-    public var leading: Constraint { return addConstraint(ConstraintAttributes.Leading) }
-    public var trailing: Constraint { return addConstraint(ConstraintAttributes.Trailing) }
-    public var width: Constraint { return addConstraint(ConstraintAttributes.Width) }
-    public var height: Constraint { return addConstraint(ConstraintAttributes.Height) }
-    public var centerX: Constraint { return addConstraint(ConstraintAttributes.CenterX) }
-    public var centerY: Constraint { return addConstraint(ConstraintAttributes.CenterY) }
-    public var baseline: Constraint { return addConstraint(ConstraintAttributes.Baseline) }
-    
-    public var and: Constraint { return self }
+    public var left: Constraint { return self.addConstraint(ConstraintAttributes.Left) }
+    public var top: Constraint { return self.addConstraint(ConstraintAttributes.Top) }
+    public var right: Constraint { return self.addConstraint(ConstraintAttributes.Right) }
+    public var bottom: Constraint { return self.addConstraint(ConstraintAttributes.Bottom) }
+    public var leading: Constraint { return self.addConstraint(ConstraintAttributes.Leading) }
+    public var trailing: Constraint { return self.addConstraint(ConstraintAttributes.Trailing) }
+    public var width: Constraint { return self.addConstraint(ConstraintAttributes.Width) }
+    public var height: Constraint { return self.addConstraint(ConstraintAttributes.Height) }
+    public var centerX: Constraint { return self.addConstraint(ConstraintAttributes.CenterX) }
+    public var centerY: Constraint { return self.addConstraint(ConstraintAttributes.CenterY) }
+    public var baseline: Constraint { return self.addConstraint(ConstraintAttributes.Baseline) }
+    
+    public var and: Constraint {
+        if self.relation != nil {
+            fatalError("And is semantic only and can only be used before a relation is set.")
+        }
+        return self
+    }
     public var with: Constraint { return self }
     
     // MARK: initializer
@@ -56,115 +61,115 @@ public class Constraint {
     // MARK: equalTo
     
     public func equalTo(other: ConstraintItem) -> Constraint {
-        return constrainTo(other, relation: .Equal)
+        return self.constrainTo(other, relation: .Equal)
     }
     public func equalTo(other: View) -> Constraint {
-        return constrainTo(other, relation: .Equal)
+        return self.constrainTo(other, relation: .Equal)
     }
     #if os(iOS)
     public func equalTo(other: UILayoutSupport) -> Constraint {
-        return constrainTo(other, relation: .Equal)
+        return self.constrainTo(other, relation: .Equal)
     }
     #endif
     public func equalTo(other: Float) -> Constraint {
-        return constrainTo(other, relation: .Equal)
+        return self.constrainTo(other, relation: .Equal)
     }
     public func equalTo(other: Double) -> Constraint {
-        return constrainTo(Float(other), relation: .Equal)
+        return self.constrainTo(Float(other), relation: .Equal)
     }
     public func equalTo(other: CGFloat) -> Constraint {
-        return constrainTo(Float(other), relation: .Equal)
+        return self.constrainTo(Float(other), relation: .Equal)
     }
     public func equalTo(other: Int) -> Constraint {
-        return constrainTo(Float(other), relation: .Equal)
+        return self.constrainTo(Float(other), relation: .Equal)
     }
     public func equalTo(other: UInt) -> Constraint {
-        return constrainTo(Float(other), relation: .Equal)
+        return self.constrainTo(Float(other), relation: .Equal)
     }
     public func equalTo(other: CGSize) -> Constraint {
-        return constrainTo(other, relation: .Equal)
+        return self.constrainTo(other, relation: .Equal)
     }
     public func equalTo(other: CGPoint) -> Constraint {
-        return constrainTo(other, relation: .Equal)
+        return self.constrainTo(other, relation: .Equal)
     }
     public func equalTo(other: EdgeInsets) -> Constraint {
-        return constrainTo(other, relation: .Equal)
+        return self.constrainTo(other, relation: .Equal)
     }
     
     // MARK: lessThanOrEqualTo
     
     public func lessThanOrEqualTo(other: ConstraintItem) -> Constraint {
-        return constrainTo(other, relation: .LessThanOrEqualTo)
+        return self.constrainTo(other, relation: .LessThanOrEqualTo)
     }
     public func lessThanOrEqualTo(other: View) -> Constraint {
-        return constrainTo(other, relation: .LessThanOrEqualTo)
+        return self.constrainTo(other, relation: .LessThanOrEqualTo)
     }
     #if os(iOS)
     public func lessThanOrEqualTo(other: UILayoutSupport) -> Constraint {
-        return constrainTo(other, relation: .LessThanOrEqualTo)
+        return self.constrainTo(other, relation: .LessThanOrEqualTo)
     }
     #endif
     public func lessThanOrEqualTo(other: Float) -> Constraint {
-        return constrainTo(other, relation: .LessThanOrEqualTo)
+        return self.constrainTo(other, relation: .LessThanOrEqualTo)
     }
     public func lessThanOrEqualTo(other: Double) -> Constraint {
-        return constrainTo(Float(other), relation: .LessThanOrEqualTo)
+        return self.constrainTo(Float(other), relation: .LessThanOrEqualTo)
     }
     public func lessThanOrEqualTo(other: CGFloat) -> Constraint {
-        return constrainTo(Float(other), relation: .LessThanOrEqualTo)
+        return self.constrainTo(Float(other), relation: .LessThanOrEqualTo)
     }
     public func lessThanOrEqualTo(other: Int) -> Constraint {
-        return constrainTo(Float(other), relation: .LessThanOrEqualTo)
+        return self.constrainTo(Float(other), relation: .LessThanOrEqualTo)
     }
     public func lessThanOrEqualTo(other: UInt) -> Constraint {
-        return constrainTo(Float(other), relation: .LessThanOrEqualTo)
+        return self.constrainTo(Float(other), relation: .LessThanOrEqualTo)
     }
     public func lessThanOrEqualTo(other: CGSize) -> Constraint {
-        return constrainTo(other, relation: .LessThanOrEqualTo)
+        return self.constrainTo(other, relation: .LessThanOrEqualTo)
     }
     public func lessThanOrEqualTo(other: CGPoint) -> Constraint {
-        return constrainTo(other, relation: .LessThanOrEqualTo)
+        return self.constrainTo(other, relation: .LessThanOrEqualTo)
     }
     public func lessThanOrEqualTo(other: EdgeInsets) -> Constraint {
-        return constrainTo(other, relation: .LessThanOrEqualTo)
+        return self.constrainTo(other, relation: .LessThanOrEqualTo)
     }
     
     // MARK: greaterThanOrEqualTo
     
     public func greaterThanOrEqualTo(other: ConstraintItem) -> Constraint {
-        return constrainTo(other, relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(other, relation: .GreaterThanOrEqualTo)
     }
     public func greaterThanOrEqualTo(other: View) -> Constraint {
-        return constrainTo(other, relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(other, relation: .GreaterThanOrEqualTo)
     }
     #if os(iOS)
     public func greaterThanOrEqualTo(other: UILayoutSupport) -> Constraint {
-        return constrainTo(other, relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(other, relation: .GreaterThanOrEqualTo)
     }
     #endif
     public func greaterThanOrEqualTo(other: Float) -> Constraint {
-        return constrainTo(other, relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(other, relation: .GreaterThanOrEqualTo)
     }
     public func greaterThanOrEqualTo(other: Double) -> Constraint {
-        return constrainTo(Float(other), relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(Float(other), relation: .GreaterThanOrEqualTo)
     }
     public func greaterThanOrEqualTo(other: CGFloat) -> Constraint {
-        return constrainTo(Float(other), relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(Float(other), relation: .GreaterThanOrEqualTo)
     }
     public func greaterThanOrEqualTo(other: Int) -> Constraint {
-        return constrainTo(Float(other), relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(Float(other), relation: .GreaterThanOrEqualTo)
     }
     public func greaterThanOrEqualTo(other: UInt) -> Constraint {
-        return constrainTo(Float(other), relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(Float(other), relation: .GreaterThanOrEqualTo)
     }
     public func greaterThanOrEqualTo(other: CGSize) -> Constraint {
-        return constrainTo(other, relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(other, relation: .GreaterThanOrEqualTo)
     }
     public func greaterThanOrEqualTo(other: CGPoint) -> Constraint {
-        return constrainTo(other, relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(other, relation: .GreaterThanOrEqualTo)
     }
     public func greaterThanOrEqualTo(other: EdgeInsets) -> Constraint {
-        return constrainTo(other, relation: .GreaterThanOrEqualTo)
+        return self.constrainTo(other, relation: .GreaterThanOrEqualTo)
     }
     
     // MARK: multiplier
@@ -282,11 +287,6 @@ public class Constraint {
     // MARK: internal
     
     internal func installOnView(updateExisting: Bool = false) -> Array<LayoutConstraint> {
-        if self.installedOnView != nil {
-            NSException(name: "Cannot Install Constraint", reason: "Already installed", userInfo: nil).raise()
-            return []
-        }
-        
         var installOnView: View? = nil
         if self.toItem.view != nil {
             installOnView = Constraint.closestCommonSuperviewFromView(self.fromItem.view, toView: self.toItem.view)
@@ -301,14 +301,22 @@ public class Constraint {
                     installOnView = self.fromItem.view
                 }
                 
-                if installedOnView == nil {
+                if installOnView == nil {
                     NSException(name: "Cannot Install Constraint", reason: "Missing superview", userInfo: nil).raise()
                     return []
                 }
             }
         }
         
-        var layoutConstraints: Array<LayoutConstraint> = []
+        if self.installedOnView != nil {
+            if self.installedOnView != installOnView {
+                NSException(name: "Cannot Install Constraint", reason: "Already installed on different view.", userInfo: nil).raise()
+                return []
+            }
+            return self.installedLayoutConstraints?.allObjects as Array<LayoutConstraint>
+        }
+        
+        var newLayoutConstraints = Array<LayoutConstraint>()
         let layoutFromAttributes = self.fromItem.attributes.layoutAttributes
         let layoutToAttributes = self.toItem.attributes.layoutAttributes
         
@@ -348,7 +356,7 @@ public class Constraint {
             // set constraint
             layoutConstraint.constraint = self
             
-            layoutConstraints.append(layoutConstraint)
+            newLayoutConstraints.append(layoutConstraint)
         }
         
         // special logic for updating
@@ -356,11 +364,11 @@ public class Constraint {
             // get existing constraints for this view
             let existingLayoutConstraints = reverse(layoutFrom!.snp_installedLayoutConstraints)
             
-            // array that will contain only new layout constraints
-            var newLayoutConstraints = Array<LayoutConstraint>()
-            
+            // array that will contain only new layout constraints to keep
+            var newLayoutConstraintsToKeep = Array<LayoutConstraint>()
+
             // begin looping
-            for layoutConstraint in layoutConstraints {
+            for layoutConstraint in newLayoutConstraints {
                 // layout constraint that should be updated
                 var updateLayoutConstraint: LayoutConstraint? = nil
                 
@@ -376,34 +384,45 @@ public class Constraint {
                 if updateLayoutConstraint != nil {
                     updateLayoutConstraint!.constant = layoutConstraint.constant
                 }
-                // otherwise add this layout constraint to new list
+                // otherwise add this layout constraint to new keep list
                 else {
-                    newLayoutConstraints.append(layoutConstraint)
+                    newLayoutConstraintsToKeep.append(layoutConstraint)
                 }
             }
-            
+
             // set constraints to only new ones
-            layoutConstraints = newLayoutConstraints
+            newLayoutConstraints = newLayoutConstraintsToKeep
         }
         
         // add constraints
-        installOnView!.addConstraints(layoutConstraints)
+        installOnView!.addConstraints(newLayoutConstraints)
         
+        // store which view this constraint was installed on
         self.installedOnView = installOnView
+        
+        // store which layout constraints are installed for this constraint
         self.installedLayoutConstraints = NSHashTable.weakObjectsHashTable()
-        for layoutConstraint in layoutConstraints {
+        for layoutConstraint in newLayoutConstraints {
             self.installedLayoutConstraints!.addObject(layoutConstraint)
         }
         
-        return layoutConstraints
+        // store the layout constraints against the installed on view
+        var layoutConstraints = Array<LayoutConstraint>(layoutFrom!.snp_installedLayoutConstraints)
+        layoutConstraints += newLayoutConstraints
+        layoutFrom!.snp_installedLayoutConstraints = layoutConstraints
+        
+        // return the new constraints
+        return newLayoutConstraints
     }
     
     internal func uninstallFromView() {
         if let view = self.installedOnView {
             // remove all installed layout constraints
             var layoutConstraintsToRemove = Array<LayoutConstraint>()
-            if let installedLayoutConstraints = self.installedLayoutConstraints?.allObjects as? Array<LayoutConstraint> {
-                layoutConstraintsToRemove += installedLayoutConstraints
+            if let allObjects = self.installedLayoutConstraints?.allObjects {
+                if let installedLayoutConstraints = allObjects as? Array<LayoutConstraint> {
+                    layoutConstraintsToRemove += installedLayoutConstraints
+                }
             }
             
             if layoutConstraintsToRemove.count > 0 {
@@ -490,19 +509,27 @@ public class Constraint {
     }
     
     private class func closestCommonSuperviewFromView(fromView: View?, toView: View?) -> View? {
-        var closestCommonSuperview: View?
-        var secondViewSuperview: View? = toView
-        while closestCommonSuperview == nil && secondViewSuperview != nil {
-            var firstViewSuperview = fromView
-            while closestCommonSuperview == nil && firstViewSuperview != nil {
-                if secondViewSuperview == firstViewSuperview {
-                    closestCommonSuperview = secondViewSuperview
+        var views = NSMutableSet()
+        var fromView = fromView
+        var toView = toView
+        do {
+            if let view = toView {
+                if views.containsObject(view) {
+                    return view
                 }
-                firstViewSuperview = firstViewSuperview?.superview
+                views.addObject(view)
+                toView = view.superview
             }
-            secondViewSuperview = secondViewSuperview?.superview
-        }
-        return closestCommonSuperview
+            if let view = fromView {
+                if views.containsObject(view) {
+                    return view
+                }
+                views.addObject(view)
+                fromView = view.superview
+            }
+        } while (fromView != nil || toView != nil)
+        
+        return nil
     }
 }
 

+ 31 - 36
Snap/ConstraintMaker.swift

@@ -31,21 +31,21 @@ import AppKit
  * ConstraintMaker is the maker in snap that gets all constraints kickstarted
  */
 public class ConstraintMaker {
-    public var left: Constraint { return addConstraint(ConstraintAttributes.Left) }
-    public var top: Constraint { return addConstraint(ConstraintAttributes.Top) }
-    public var right: Constraint { return addConstraint(ConstraintAttributes.Right) }
-    public var bottom: Constraint { return addConstraint(ConstraintAttributes.Bottom) }
-    public var leading: Constraint { return addConstraint(ConstraintAttributes.Leading) }
-    public var trailing: Constraint { return addConstraint(ConstraintAttributes.Trailing) }
-    public var width: Constraint { return addConstraint(ConstraintAttributes.Width) }
-    public var height: Constraint { return addConstraint(ConstraintAttributes.Height) }
-    public var centerX: Constraint { return addConstraint(ConstraintAttributes.CenterX) }
-    public var centerY: Constraint { return addConstraint(ConstraintAttributes.CenterY) }
-    public var baseline: Constraint { return addConstraint(ConstraintAttributes.Baseline) }
+    public var left: Constraint { return self.addConstraint(ConstraintAttributes.Left) }
+    public var top: Constraint { return self.addConstraint(ConstraintAttributes.Top) }
+    public var right: Constraint { return self.addConstraint(ConstraintAttributes.Right) }
+    public var bottom: Constraint { return self.addConstraint(ConstraintAttributes.Bottom) }
+    public var leading: Constraint { return self.addConstraint(ConstraintAttributes.Leading) }
+    public var trailing: Constraint { return self.addConstraint(ConstraintAttributes.Trailing) }
+    public var width: Constraint { return self.addConstraint(ConstraintAttributes.Width) }
+    public var height: Constraint { return self.addConstraint(ConstraintAttributes.Height) }
+    public var centerX: Constraint { return self.addConstraint(ConstraintAttributes.CenterX) }
+    public var centerY: Constraint { return self.addConstraint(ConstraintAttributes.CenterY) }
+    public var baseline: Constraint { return self.addConstraint(ConstraintAttributes.Baseline) }
     
-    public var edges: Constraint { return addConstraint(ConstraintAttributes.Edges) }
-    public var size: Constraint { return addConstraint(ConstraintAttributes.Size) }
-    public var center: Constraint { return addConstraint(ConstraintAttributes.Center) }
+    public var edges: Constraint { return self.addConstraint(ConstraintAttributes.Edges) }
+    public var size: Constraint { return self.addConstraint(ConstraintAttributes.Size) }
+    public var center: Constraint { return self.addConstraint(ConstraintAttributes.Center) }
     
     init(view: View) {
         self.view = view
@@ -61,7 +61,13 @@ public class ConstraintMaker {
         return constraint
     }
     
-    internal class func makeConstraints(view: View, block: (make: ConstraintMaker) -> ()) {
+    internal class func prepareConstraints(view: View, block: (make: ConstraintMaker) -> Void) -> Array<Constraint> {
+        let maker = ConstraintMaker(view: view)
+        block(make: maker)
+        return maker.constraints
+    }
+    
+    internal class func makeConstraints(view: View, block: (make: ConstraintMaker) -> Void) {
         #if os(iOS)
         view.setTranslatesAutoresizingMaskIntoConstraints(false)
         #else
@@ -69,16 +75,12 @@ public class ConstraintMaker {
         #endif
         let maker = ConstraintMaker(view: view)
         block(make: maker)
-        
-        var layoutConstraints = view.snp_installedLayoutConstraints
         for constraint in maker.constraints {
-            layoutConstraints += constraint.install()
+            constraint.installOnView(updateExisting: false)
         }
-        
-        view.snp_installedLayoutConstraints = layoutConstraints
     }
     
-    internal class func remakeConstraints(view: View, block: (make: ConstraintMaker) -> ()) {
+    internal class func remakeConstraints(view: View, block: (make: ConstraintMaker) -> Void) {
         #if os(iOS)
         view.setTranslatesAutoresizingMaskIntoConstraints(false)
         #else
@@ -87,20 +89,17 @@ public class ConstraintMaker {
         let maker = ConstraintMaker(view: view)
         block(make: maker)
         
-        var layoutConstraints: Array<LayoutConstraint> = view.snp_installedLayoutConstraints
+        var layoutConstraints = Array<LayoutConstraint>(view.snp_installedLayoutConstraints)
         for existingLayoutConstraint in layoutConstraints {
-            existingLayoutConstraint.constraint?.uninstall()
+            existingLayoutConstraint.constraint?.uninstallFromView()
         }
-        layoutConstraints = []
         
         for constraint in maker.constraints {
-            layoutConstraints += constraint.install()
+            constraint.installOnView(updateExisting: false)
         }
-        
-        view.snp_installedLayoutConstraints = layoutConstraints
     }
     
-    internal class func updateConstraints(view: View, block: (make: ConstraintMaker) -> ()) {
+    internal class func updateConstraints(view: View, block: (make: ConstraintMaker) -> Void) {
         #if os(iOS)
         view.setTranslatesAutoresizingMaskIntoConstraints(false)
         #else
@@ -109,19 +108,15 @@ public class ConstraintMaker {
         let maker = ConstraintMaker(view: view)
         block(make: maker)
         
-        var layoutConstraints = view.snp_installedLayoutConstraints
         for constraint in maker.constraints {
-            layoutConstraints += constraint.installOnView(updateExisting: true)
+            constraint.installOnView(updateExisting: true)
         }
-        
-        view.snp_installedLayoutConstraints = layoutConstraints
     }
     
     internal class func removeConstraints(view: View) {
-        for existingLayoutConstraint in view.snp_installedLayoutConstraints {
-            existingLayoutConstraint.constraint?.uninstall()
+        let existingLayoutConstraints = Array<LayoutConstraint>(view.snp_installedLayoutConstraints)
+        for existingLayoutConstraint in existingLayoutConstraints {
+            existingLayoutConstraint.constraint?.uninstallFromView()
         }
-        
-        view.snp_installedLayoutConstraints = []
     }
 }

+ 10 - 7
Snap/View+Snap.swift

@@ -68,15 +68,19 @@ public extension View {
     public var snp_centerWithinMargins: ConstraintItem { return ConstraintItem(object: self, attributes: ConstraintAttributes.CenterWithinMargins) }
     #endif
     
-    public func snp_makeConstraints(block: (make: ConstraintMaker) -> ()) {
+    public func snp_prepareConstraints(block: (make: ConstraintMaker) -> Void) -> Array<Constraint> {
+        return ConstraintMaker.prepareConstraints(self, block: block)
+    }
+    
+    public func snp_makeConstraints(block: (make: ConstraintMaker) -> Void) {
         ConstraintMaker.makeConstraints(self, block: block)
     }
     
-    public func snp_updateConstraints(block: (make: ConstraintMaker) -> ()) {
+    public func snp_updateConstraints(block: (make: ConstraintMaker) -> Void) {
         ConstraintMaker.updateConstraints(self, block: block)
     }
     
-    public func snp_remakeConstraints(block: (make: ConstraintMaker) -> ()) {
+    public func snp_remakeConstraints(block: (make: ConstraintMaker) -> Void) {
         ConstraintMaker.remakeConstraints(self, block: block)
     }
     
@@ -88,11 +92,10 @@ public extension View {
     
     internal var snp_installedLayoutConstraints: Array<LayoutConstraint> {
         get {
-            var constraints = objc_getAssociatedObject(self, &installedLayoutConstraintsKey) as? Array<LayoutConstraint>
-            if constraints != nil {
-                return constraints!
+            if let constraints = objc_getAssociatedObject(self, &installedLayoutConstraintsKey) as? Array<LayoutConstraint> {
+                return constraints
             }
-            return []
+            return Array<LayoutConstraint>()
         }
         set {
             objc_setAssociatedObject(self, &installedLayoutConstraintsKey, newValue, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC))

+ 140 - 7
SnapTests/SnapTests.swift

@@ -8,9 +8,12 @@
 
 import UIKit
 import XCTest
+import Snap
 
 class SnapTests: XCTestCase {
     
+    let container = UIView()
+    
     override func setUp() {
         super.setUp()
         // Put setup code here. This method is called before the invocation of each test method in the class.
@@ -21,16 +24,146 @@ class SnapTests: XCTestCase {
         super.tearDown()
     }
     
-    func testExample() {
-        // This is an example of a functional test case.
-        XCTAssert(true, "Pass")
+    func testMakeConstraints() {
+        let v1 = UIView()
+        let v2 = UIView()
+        self.container.addSubview(v1)
+        self.container.addSubview(v2)
+        
+        v1.snp_makeConstraints { (make) -> Void in
+            make.top.equalTo(v2.snp_top).offset(50)
+            make.left.equalTo(v2.snp_top).offset(50)
+            return
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 2, "Should have 2 constraints installed")
+        
+        v2.snp_makeConstraints { (make) -> Void in
+            make.edges.equalTo(v1)
+            return
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 6, "Should have 6 constraints installed")
+        
+    }
+    
+    func testUpdateConstraints() {
+        let v1 = UIView()
+        let v2 = UIView()
+        self.container.addSubview(v1)
+        self.container.addSubview(v2)
+        
+        v1.snp_makeConstraints { (make) -> Void in
+            make.top.equalTo(v2.snp_top).offset(50)
+            make.left.equalTo(v2.snp_top).offset(50)
+            return
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 2, "Should have 2 constraints installed")
+        
+        v1.snp_updateConstraints { (make) -> Void in
+            make.top.equalTo(v2.snp_top).offset(15)
+            return
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 2, "Should still have 2 constraints installed")
+        
+    }
+    
+    func testRemakeConstraints() {
+        let v1 = UIView()
+        let v2 = UIView()
+        self.container.addSubview(v1)
+        self.container.addSubview(v2)
+        
+        v1.snp_makeConstraints { (make) -> Void in
+            make.top.equalTo(v2.snp_top).offset(50)
+            make.left.equalTo(v2.snp_top).offset(50)
+            return
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 2, "Should have 2 constraints installed")
+        
+        v1.snp_remakeConstraints { (make) -> Void in
+            make.edges.equalTo(v2)
+            return
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 4, "Should have 4 constraints installed")
+        
     }
     
-    func testPerformanceExample() {
-        // This is an example of a performance test case.
-        self.measureBlock() {
-            // Put the code you want to measure the time of here.
+    func testRemoveConstraints() {
+        let v1 = UIView()
+        let v2 = UIView()
+        self.container.addSubview(v1)
+        self.container.addSubview(v2)
+        
+        v1.snp_makeConstraints { (make) -> Void in
+            make.top.equalTo(v2.snp_top).offset(50)
+            make.left.equalTo(v2.snp_top).offset(50)
+            return
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 2, "Should have 2 constraints installed")
+        
+        v1.snp_removeConstraints()
+        
+        XCTAssertEqual(self.container.constraints().count, 0, "Should have 0 constraints installed")
+        
+    }
+    
+    func testPrepareConstraints() {
+        let v1 = UIView()
+        let v2 = UIView()
+        self.container.addSubview(v1)
+        self.container.addSubview(v2)
+        
+        let constraints = v1.snp_prepareConstraints { (make) -> Void in
+            make.edges.equalTo(v2)
+            return
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 0, "Should have 0 constraints installed")
+        
+        for constraint in constraints {
+            constraint.install()
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 4, "Should have 4 constraints installed")
+        
+        for constraint in constraints {
+            constraint.uninstall()
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 0, "Should have 0 constraints installed")
+        
+    }
+    
+    func testReinstallConstraints() {
+        let v1 = UIView()
+        let v2 = UIView()
+        self.container.addSubview(v1)
+        self.container.addSubview(v2)
+        
+        let constraints = v1.snp_prepareConstraints { (make) -> Void in
+            make.edges.equalTo(v2)
+            return
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 0, "Should have 0 constraints installed")
+        
+        for constraint in constraints {
+            constraint.install()
+        }
+        
+        XCTAssertEqual(self.container.constraints().count, 4, "Should have 4 constraints installed")
+        
+        for constraint in constraints {
+            constraint.install()
         }
+        
+        XCTAssertEqual(self.container.constraints().count, 4, "Should have 0 constraints installed")
     }
     
 }