diff --git a/AnimationRestoration.xcodeproj/project.pbxproj b/AnimationRestoration.xcodeproj/project.pbxproj
index 0553458..afecf5b 100644
--- a/AnimationRestoration.xcodeproj/project.pbxproj
+++ b/AnimationRestoration.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		2EA1DA391E96E26B00255843 /* AnimationPreservingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA1DA381E96E26B00255843 /* AnimationPreservingView.swift */; };
+		2EA1DA3B1E96E2A700255843 /* ObjectAssociation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA1DA3A1E96E2A700255843 /* ObjectAssociation.swift */; };
 		2ECCB76F1E96C01F00CD12C6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ECCB76E1E96C01F00CD12C6 /* AppDelegate.swift */; };
 		2ECCB7711E96C01F00CD12C6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ECCB7701E96C01F00CD12C6 /* ViewController.swift */; };
 		2ECCB7741E96C01F00CD12C6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2ECCB7721E96C01F00CD12C6 /* Main.storyboard */; };
@@ -15,6 +17,8 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+		2EA1DA381E96E26B00255843 /* AnimationPreservingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationPreservingView.swift; sourceTree = "<group>"; };
+		2EA1DA3A1E96E2A700255843 /* ObjectAssociation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectAssociation.swift; sourceTree = "<group>"; };
 		2ECCB76B1E96C01F00CD12C6 /* AnimationRestoration.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AnimationRestoration.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		2ECCB76E1E96C01F00CD12C6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		2ECCB7701E96C01F00CD12C6 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@@ -54,6 +58,8 @@
 		2ECCB76D1E96C01F00CD12C6 /* AnimationRestoration */ = {
 			isa = PBXGroup;
 			children = (
+				2EA1DA3A1E96E2A700255843 /* ObjectAssociation.swift */,
+				2EA1DA381E96E26B00255843 /* AnimationPreservingView.swift */,
 				2ECCB76E1E96C01F00CD12C6 /* AppDelegate.swift */,
 				2ECCB7701E96C01F00CD12C6 /* ViewController.swift */,
 				2ECCB7721E96C01F00CD12C6 /* Main.storyboard */,
@@ -136,7 +142,9 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				2EA1DA391E96E26B00255843 /* AnimationPreservingView.swift in Sources */,
 				2ECCB7711E96C01F00CD12C6 /* ViewController.swift in Sources */,
+				2EA1DA3B1E96E2A700255843 /* ObjectAssociation.swift in Sources */,
 				2ECCB76F1E96C01F00CD12C6 /* AppDelegate.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -301,6 +309,7 @@
 				2ECCB77F1E96C01F00CD12C6 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
 		};
 /* End XCConfigurationList section */
 	};
diff --git a/AnimationRestoration/AnimationPreservingView.swift b/AnimationRestoration/AnimationPreservingView.swift
new file mode 100644
index 0000000..d80f6d4
--- /dev/null
+++ b/AnimationRestoration/AnimationPreservingView.swift
@@ -0,0 +1,141 @@
+//
+//  AnimationPreservingView.swift
+//  AnimationPreservingView
+//
+//  Created by Wojciech Nagrodzki on 08/03/2017.
+//  Copyright © 2017 Wojciech Nagrodzki. All rights reserved.
+//
+
+import UIKit
+
+
+/// The `AnimationPreservingView` class keeps it's layer tree animations safe from being removed.
+/// There are two cases when `CAAnimation` can be removed from `CALayer` automatically:
+/// - when application goes to background
+/// - when view backed by the layer is removed from window
+class AnimationPreservingView: UIView {
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        registerForNotifications()
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        registerForNotifications()
+    }
+    
+    override func willMove(toWindow newWindow: UIWindow?) {
+        super.willMove(toWindow: newWindow)
+        if newWindow == nil { layer.storeAnimations() }
+    }
+    
+    override func didMoveToWindow() {
+        super.didMoveToWindow()
+        if window != nil { layer.restoreAnimations() }
+    }
+}
+
+
+extension AnimationPreservingView {
+    
+    fileprivate func registerForNotifications() {
+        
+        NotificationCenter.default.addObserver(self,
+                                               selector: #selector(AnimationPreservingView.applicationWillResignActive),
+                                               name: .UIApplicationWillResignActive,
+                                               object: nil)
+        NotificationCenter.default.addObserver(self,
+                                               selector: #selector(AnimationPreservingView.applicationDidBecomeActive),
+                                               name: .UIApplicationDidBecomeActive,
+                                               object: nil)
+    }
+    
+    @objc private func applicationWillResignActive() {
+        
+        guard window != nil else { return }
+        layer.storeAnimations()
+    }
+    
+    @objc private func applicationDidBecomeActive() {
+        
+        guard window != nil else { return }
+        layer.restoreAnimations()
+    }
+}
+
+
+extension CALayer {
+    
+    private static let association = ObjectAssociation<NSDictionary>()
+    
+    private var animationsStorage: [String: CAAnimation] {
+
+        get { return CALayer.association[self] as? [String : CAAnimation] ?? [:] }
+        set { CALayer.association[self] = newValue as NSDictionary }
+    }
+    
+    /// Returns a dictionary of copies of animations currently attached to the layer along with their's keys.
+    private var animationsForKeys: [String: CAAnimation] {
+        
+        guard let keys = animationKeys() else { return [:] }
+        return keys.reduce([:], {
+            var result = $0
+            let key = $1
+            result[key] = (animation(forKey: key)!.copy() as! CAAnimation)
+            return result
+        })
+    }
+    
+    /// Pauses the layer tree and stores it's animations.
+    func storeAnimations() {
+        
+        pause()
+        depositAnimations()
+    }
+    
+    /// Resumes the layer tree and restores it's animations.
+    func restoreAnimations() {
+        
+        withdrawAnimations()
+        resume()
+    }
+    
+    private func depositAnimations() {
+        
+        animationsStorage = animationsForKeys
+        sublayers?.forEach { $0.depositAnimations() }
+    }
+    
+    private func withdrawAnimations() {
+        
+        sublayers?.forEach { $0.withdrawAnimations() }
+        animationsStorage.forEach { add($0.value, forKey: $0.key) }
+        animationsStorage = [:]
+    }
+}
+
+
+extension CALayer {
+    
+    /// Pauses animations in layer tree.
+    /// - note: [Technical Q&A QA1673](https://developer.apple.com/library/ios/qa/qa1673/_index.html#//apple_ref/doc/uid/DTS40010053)
+    fileprivate func pause() {
+        
+        let pausedTime = convertTime(CACurrentMediaTime(), from: nil)
+        speed = 0.0;
+        timeOffset = pausedTime;
+    }
+    
+    /// Resumes animations in layer tree.
+    /// - note: [Technical Q&A QA1673](https://developer.apple.com/library/ios/qa/qa1673/_index.html#//apple_ref/doc/uid/DTS40010053)
+    fileprivate func resume() {
+        
+        let pausedTime = timeOffset;
+        speed = 1.0;
+        timeOffset = 0.0;
+        beginTime = 0.0;
+        let timeSincePause = convertTime(CACurrentMediaTime(), from: nil) - pausedTime;
+        beginTime = timeSincePause;
+    }
+}
diff --git a/AnimationRestoration/ObjectAssociation.swift b/AnimationRestoration/ObjectAssociation.swift
new file mode 100644
index 0000000..4c0a7e5
--- /dev/null
+++ b/AnimationRestoration/ObjectAssociation.swift
@@ -0,0 +1,55 @@
+//
+// MIT License
+//
+// Copyright (c) 2017 Trifork Kraków Office
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+import Foundation
+
+/// Wraps Objective-C runtime object associations. Assumes one instance per association key.
+/// 
+/// Example usage when simulating stored property with a computed one:
+///     
+///     extension SomeType {
+///         private static let association = ObjectAssociation<NSObject>()
+///         var simulatedProperty: NSObject? {
+///             get { return SomeType.association[self] }
+///             set { SomeType.association[self] = newValue }
+///         }
+///     }
+public final class ObjectAssociation<T: AnyObject> {
+    
+    private let policy: objc_AssociationPolicy
+    
+    /// - Parameter policy: An association policy that will be used when linking objects.
+    public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
+        
+        self.policy = policy
+    }
+    
+    /// Accesses associated object.
+    /// - Parameter index: An object whose associated object is to be accessed.
+    public subscript(index: AnyObject) -> T? {
+        
+        get { return objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T? }
+        set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) }
+    }
+}