Merge branch 'release-candidate' into stable
diff --git a/.jazzy.yaml b/.jazzy.yaml
index 5109d14..f9986df 100644
--- a/.jazzy.yaml
+++ b/.jazzy.yaml
@@ -1,8 +1,8 @@
 module: MotionInterchange
-module_version: 1.5.0
+module_version: 1.6.0
 sdk: iphonesimulator
 umbrella_header: src/MotionInterchange.h
 objc: true
 github_url: https://github.com/material-motion/motion-interchange-objc
-github_file_prefix: https://github.com/material-motion/motion-interchange-objc/tree/v1.5.0
+github_file_prefix: https://github.com/material-motion/motion-interchange-objc/tree/v1.6.0
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4874616..7a2299a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,23 @@
+# 1.6.0
+
+This patch release introduces a compatibility initializer for creating Objective-C animation traits from C-style motion timings.
+
+## Source changes
+
+* [Add a legacy initializer for animation traits. (#34)](https://github.com/material-motion/motion-interchange-objc/commit/e3321d0e4081de1f3d8edee115af0a9ea2142c48) (featherless)
+
+## API changes
+
+Auto-generated by running:
+
+    apidiff origin/stable release-candidate objc src/MotionInterchange.h
+
+#### MDMAnimationTraits(Legacy)
+
+*new* method: `-initWithMotionTiming:` in `MDMAnimationTraits(Legacy)`
+
+*new* category: `MDMAnimationTraits(Legacy)`
+
 # 1.5.0
 
 This minor release introduces new Objective-C APIs for creating and storing animation traits.
diff --git a/MotionInterchange.podspec b/MotionInterchange.podspec
index 0c293a4..8779c48 100644
--- a/MotionInterchange.podspec
+++ b/MotionInterchange.podspec
@@ -1,7 +1,7 @@
 Pod::Spec.new do |s|
   s.name         = "MotionInterchange"
   s.summary      = "Motion interchange format."
-  s.version      = "1.5.0"
+  s.version      = "1.6.0"
   s.authors      = "The Material Motion Authors"
   s.license      = "Apache 2.0"
   s.homepage     = "https://github.com/material-motion/motion-interchange-objc"
diff --git a/Podfile.lock b/Podfile.lock
index 5fd06b9..9a7416b 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -1,6 +1,6 @@
 PODS:
   - CatalogByConvention (2.1.1)
-  - MotionInterchange (1.5.0)
+  - MotionInterchange (1.6.0)
 
 DEPENDENCIES:
   - CatalogByConvention
@@ -12,7 +12,7 @@
 
 SPEC CHECKSUMS:
   CatalogByConvention: c3a5319de04250a7cd4649127fcfca5fe3322a43
-  MotionInterchange: 27bef5bbb1e7a59242c38cf2f1ccab83ca569f55
+  MotionInterchange: ead0e3ae1f3a5fb539e289debbc7ae036160a10d
 
 PODFILE CHECKSUM: 09090d12db5aab00a13fe82da94f338ebd03f5dc
 
diff --git a/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj b/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj
index df857cb..1312d1c 100644
--- a/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj
+++ b/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj
@@ -27,6 +27,7 @@
 		667A3F541DEE273000CB3A99 /* TableOfContents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667A3F531DEE273000CB3A99 /* TableOfContents.swift */; };
 		66C6FB9E1FE175CA0039F790 /* MDMMotionCurveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C6FB9A1FE175B80039F790 /* MDMMotionCurveTests.swift */; };
 		66C6FB9F1FE175D00039F790 /* MDMMotionCurveTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 66C6FB9B1FE175B80039F790 /* MDMMotionCurveTests.m */; };
+		66C6FBA11FE1A0E30039F790 /* MDMLegacyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C6FBA01FE1A0E30039F790 /* MDMLegacyAPITests.swift */; };
 		8867B13812C3FF5D47DAB6A6 /* Pods_UnitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7FF982F18E05B85C840EA24 /* Pods_UnitTests.framework */; };
 		A433D59338049BD05D945199 /* Pods_MotionInterchangeCatalog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB060E9F4ECA42A5E69FB42A /* Pods_MotionInterchangeCatalog.framework */; };
 /* End PBXBuildFile section */
@@ -78,6 +79,7 @@
 		667A3F531DEE273000CB3A99 /* TableOfContents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableOfContents.swift; sourceTree = "<group>"; };
 		66C6FB9A1FE175B80039F790 /* MDMMotionCurveTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDMMotionCurveTests.swift; sourceTree = "<group>"; };
 		66C6FB9B1FE175B80039F790 /* MDMMotionCurveTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDMMotionCurveTests.m; sourceTree = "<group>"; };
+		66C6FBA01FE1A0E30039F790 /* MDMLegacyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MDMLegacyAPITests.swift; sourceTree = "<group>"; };
 		CF426CCC8068036D56265374 /* Pods-UnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.debug.xcconfig"; path = "../../../Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.debug.xcconfig"; sourceTree = "<group>"; };
 		EB060E9F4ECA42A5E69FB42A /* Pods_MotionInterchangeCatalog.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MotionInterchangeCatalog.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		F7FF982F18E05B85C840EA24 /* Pods_UnitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_UnitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -174,6 +176,7 @@
 			children = (
 				664C8C521FD5A555004ED471 /* CAMediaTimingFunctionTests.swift */,
 				664C8C581FD5A8AC004ED471 /* MDMAnimationTraitsTests.swift */,
+				66C6FBA01FE1A0E30039F790 /* MDMLegacyAPITests.swift */,
 				6619E1D81FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m */,
 				664C8C541FD5A7B7004ED471 /* MDMRepetitionTests.swift */,
 				664C8C561FD5A831004ED471 /* MDMRepetitionOverTimeTests.swift */,
@@ -496,6 +499,7 @@
 				664C8C531FD5A555004ED471 /* CAMediaTimingFunctionTests.swift in Sources */,
 				660248AE1FD1EE78004C0147 /* MDMSpringTimingCurve.swift in Sources */,
 				66C6FB9E1FE175CA0039F790 /* MDMMotionCurveTests.swift in Sources */,
+				66C6FBA11FE1A0E30039F790 /* MDMLegacyAPITests.swift in Sources */,
 				6619E1D91FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m in Sources */,
 				66C6FB9F1FE175D00039F790 /* MDMMotionCurveTests.m in Sources */,
 				664C8C571FD5A831004ED471 /* MDMRepetitionOverTimeTests.swift in Sources */,
diff --git a/src/MDMAnimationTraits.h b/src/MDMAnimationTraits.h
index f31c776..b0539a5 100644
--- a/src/MDMAnimationTraits.h
+++ b/src/MDMAnimationTraits.h
@@ -18,6 +18,7 @@
 #import <Foundation/Foundation.h>
 #import <UIKit/UIKit.h>
 
+#import "MDMMotionTiming.h"
 #import "MDMRepetitionTraits.h"
 #import "MDMSubclassingRestricted.h"
 #import "MDMTimingCurve.h"
@@ -145,3 +146,13 @@
 
 @end
 
+@interface MDMAnimationTraits (Legacy)
+
+/**
+ Initializes the instance with the provided legacy C-style animation trait structure.
+
+ @param timing A legacy C-style representation of animation traits.
+ */
+- (nonnull instancetype)initWithMotionTiming:(MDMMotionTiming)timing;
+
+@end
diff --git a/src/MDMAnimationTraits.m b/src/MDMAnimationTraits.m
index 3fd975b..5b97bec 100644
--- a/src/MDMAnimationTraits.m
+++ b/src/MDMAnimationTraits.m
@@ -17,6 +17,8 @@
 #import "MDMAnimationTraits.h"
 
 #import "CAMediaTimingFunction+MDMTimingCurve.h"
+#import "MDMRepetition.h"
+#import "MDMRepetitionOverTime.h"
 #import "MDMSpringTimingCurve.h"
 
 @implementation MDMAnimationTraits
@@ -80,6 +82,53 @@
   return self;
 }
 
+- (nonnull instancetype)initWithMotionTiming:(MDMMotionTiming)timing {
+  id<MDMTimingCurve> timingCurve;
+  switch (timing.curve.type) {
+    case MDMMotionCurveTypeInstant:
+      timingCurve = nil;
+      break;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    case MDMMotionCurveTypeDefault:
+#pragma clang diagnostic pop
+    case MDMMotionCurveTypeBezier:
+      timingCurve = [CAMediaTimingFunction functionWithControlPoints:(float)timing.curve.data[0]
+                                                                    :(float)timing.curve.data[1]
+                                                                    :(float)timing.curve.data[2]
+                                                                    :(float)timing.curve.data[3]];
+      break;
+    case MDMMotionCurveTypeSpring: {
+      CGFloat *data = timing.curve.data;
+      timingCurve =
+          [[MDMSpringTimingCurve alloc] initWithMass:data[MDMSpringMotionCurveDataIndexMass]
+                                             tension:data[MDMSpringMotionCurveDataIndexTension]
+                                            friction:data[MDMSpringMotionCurveDataIndexFriction]
+                                     initialVelocity:data[MDMSpringMotionCurveDataIndexInitialVelocity]];
+      break;
+    }
+  }
+  id<MDMRepetitionTraits> repetition;
+  switch (timing.repetition.type) {
+    case MDMMotionRepetitionTypeNone:
+      repetition = nil;
+      break;
+
+    case MDMMotionRepetitionTypeCount:
+      repetition = [[MDMRepetition alloc] initWithNumberOfRepetitions:timing.repetition.amount
+                                                         autoreverses:timing.repetition.autoreverses];
+      break;
+    case MDMMotionRepetitionTypeDuration:
+      repetition = [[MDMRepetitionOverTime alloc] initWithDuration:timing.repetition.amount
+                                                      autoreverses:timing.repetition.autoreverses];
+      break;
+  }
+  return [self initWithDelay:timing.delay
+                    duration:timing.duration
+                 timingCurve:timingCurve
+                  repetition:repetition];
+}
+
 #pragma mark - NSCopying
 
 - (id)copyWithZone:(NSZone *)zone {
diff --git a/tests/unit/MDMLegacyAPITests.swift b/tests/unit/MDMLegacyAPITests.swift
new file mode 100644
index 0000000..0aa3d67
--- /dev/null
+++ b/tests/unit/MDMLegacyAPITests.swift
@@ -0,0 +1,47 @@
+/*
+ Copyright 2017-present The Material Motion Authors. All Rights Reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+import XCTest
+import MotionInterchange
+
+class MDMLegacyAPITests: XCTestCase {
+
+  func testTimingValuesMatchTraitValues() {
+    let timing = MotionTiming(delay: 0.1,
+                                   duration: 0.2,
+                                   curve: MotionCurveMakeBezier(p1x: 0.3, p1y: 0.4, p2x: 0.5, p2y: 0.6),
+                                   repetition: .init(type: .duration, amount: 0.7, autoreverses: true))
+
+    let traits = MDMAnimationTraits(motionTiming: timing)
+
+    XCTAssertEqualWithAccuracy(traits.duration, timing.duration, accuracy: 0.001)
+    XCTAssertEqualWithAccuracy(traits.delay, timing.delay, accuracy: 0.001)
+    XCTAssertTrue(traits.timingCurve is CAMediaTimingFunction)
+    if let timingCurve = traits.timingCurve as? CAMediaTimingFunction {
+      XCTAssertEqualWithAccuracy(timingCurve.mdm_point1.x, timing.curve.data.0, accuracy: 0.001)
+      XCTAssertEqualWithAccuracy(timingCurve.mdm_point1.y, timing.curve.data.1, accuracy: 0.001)
+      XCTAssertEqualWithAccuracy(timingCurve.mdm_point2.x, timing.curve.data.2, accuracy: 0.001)
+      XCTAssertEqualWithAccuracy(timingCurve.mdm_point2.y, timing.curve.data.3, accuracy: 0.001)
+    }
+    XCTAssertTrue(traits.repetition is MDMRepetitionOverTime)
+    if let repetition = traits.repetition as? MDMRepetitionOverTime {
+      XCTAssertEqualWithAccuracy(repetition.duration, timing.repetition.amount, accuracy: 0.001)
+      XCTAssertEqual(repetition.autoreverses, timing.repetition.autoreverses.boolValue)
+    }
+  }
+
+}
+