diff --git a/.gitignore b/.gitignore index 3ef169c..7e83334 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,8 @@ Pods/ # Swift Playgrounds timeline.xctimeline + +# Swift Pacakge Manager +generated/ +.build/ +.swiftpm/ diff --git a/.travis.yml b/.travis.yml index 5417c46..e06ae3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,20 @@ language: objective-c jobs: - osx_image: xcode11.3 - env: DEST='platform=iOS Simulator,OS=13.3,name=iPhone 11 Pro' + env: ACTIONS="xcode,pod-lint";PLATFORM="iOS_13" - osx_image: xcode10.3 - env: DEST='platform=iOS Simulator,OS=12.4,name=iPhone Xs' + env: ACTIONS="xcode,pod-lint";PLATFORM="iOS_12" - osx_image: xcode10.3 - env: DEST='platform=iOS Simulator,OS=11.4,name=iPhone X' + env: ACTIONS="xcode,pod-lint";PLATFORM="iOS_11" - osx_image: xcode10.3 - env: DEST='platform=iOS Simulator,OS=10.3.1,name=iPhone 7' + env: ACTIONS="xcode,pod-lint";PLATFORM="iOS_10" + - osx_image: xcode11.3 + env: ACTIONS="spm";PLATFORM="iOS_13" install: - bundle install --gemfile=Example/Gemfile - bundle exec --gemfile=Example/Gemfile pod install --project-directory=Example script: - - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/Stagehand.xcworkspace -scheme "Stagehand Demo App" -sdk iphonesimulator -destination "$DEST" ONLY_ACTIVE_ARCH=NO | xcpretty - - bundle exec --gemfile=Example/Gemfile pod lib lint + - ./Scripts/ci.sh branches: only: - master diff --git a/Example/Podfile.lock b/Example/Podfile.lock index c88e25b..59da4db 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -25,8 +25,8 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: iOSSnapshotTestCase: 9ab44cb5aa62b84d31847f40680112e15ec579a6 - Stagehand: a4a339d2b1227bbffadd18877b2ae28cec416b1f - StagehandTesting: 410114f4d7583665f5e8498de1e4d631b0f49997 + Stagehand: 6d360781f1e1ad7c15a16253952164c0035010d1 + StagehandTesting: 941f1f1e381cf27e91a677895e249a3078e863e9 PODFILE CHECKSUM: e2058eb578e4d6fb432b015c43b4e221c1b70e87 diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..446aac7 --- /dev/null +++ b/Package.swift @@ -0,0 +1,42 @@ +// swift-tools-version:5.0.1 + +// +// Copyright 2020 Square Inc. +// +// 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 PackageDescription + +let package = Package( + name: "Stagehand", + platforms: [ + .iOS(.v10), + ], + products: [ + .library( + name: "Stagehand", + targets: ["Stagehand"] + ), + ], + targets: [ + .target( + name: "Stagehand", + dependencies: [], + path: "Sources/Stagehand" + ), + ], + swiftLanguageVersions: [.v5] +) + +let version = Version(2, 0, 3) diff --git a/README.md b/README.md index ff799ac..6333f70 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,30 @@ Stagehand provides a modern, type-safe API for building animations on iOS. Stage ## Installation -Stagehand is available through [CocoaPods](https://cocoapods.org). To install it, simply add the following line to your Podfile: +### CocoaPods + +To install Stagehand via [CocoaPods](https://cocoapods.org), simply add the following line to your `Podfile`: ```ruby pod 'Stagehand' ``` +To install StagehandTesting, the animation snapshot testing utilities, add the following line to your test target definition in your `Podfile`: + +```ruby +pod 'StagehandTesting' +``` + +### Swift Package Manager + +To install Stagehand via [Swift Package Manager](https://github.com/apple/swift-package-manager), add the following to your `Package.swift`: + +```swift +dependencies: [ + .package(url: "https://github.com/cashapp/stagehand", from: "2.0.3"), +], +``` + ## Getting Started with Stagehand An animation begins with the construction of an `Animation`. An `Animation` is generic over a type of element and acts as a definition of how that element should be animated. diff --git a/Scripts/build.swift b/Scripts/build.swift new file mode 100755 index 0000000..c1893f1 --- /dev/null +++ b/Scripts/build.swift @@ -0,0 +1,182 @@ +#!/usr/bin/env swift + +import Foundation + +// Usage: build.swift [] + +func execute(commandPath: String, arguments: [String], pipedTo pipeProcess: Process? = nil) throws { + let task = Process() + task.launchPath = commandPath + task.arguments = arguments + + let argumentsString = arguments + .map { argument in + if argument.contains(" ") { + return "\"\(argument)\"" + } else { + return argument + } + } + .joined(separator: " ") + + if let pipeProcess = pipeProcess, let pipePath = pipeProcess.launchPath { + let pipe = Pipe() + task.standardOutput = pipe + pipeProcess.standardInput = pipe + + print("Launching command: \(commandPath) \(argumentsString) | \(pipePath)") + + } else { + print("Launching command: \(commandPath) \(argumentsString)") + } + + task.launch() + + pipeProcess?.launch() + + task.waitUntilExit() + + guard task.terminationStatus == 0 else { + throw TaskError.code(task.terminationStatus) + } +} + +enum TaskError: Error { + case code(Int32) +} + +enum Platform: String, CustomStringConvertible { + case iOS_13 + case iOS_12 + case iOS_11 + case iOS_10 + + var destination: String { + switch self { + case .iOS_13: + return "platform=iOS Simulator,OS=13.3,name=iPhone 11 Pro" + case .iOS_12: + return "platform=iOS Simulator,OS=12.4,name=iPhone Xs" + case .iOS_11: + return "platform=iOS Simulator,OS=11.4,name=iPhone X" + case .iOS_10: + return "platform=iOS Simulator,OS=10.3.1,name=iPhone 7" + } + } + + var description: String { + return rawValue + } +} + +enum Task: String, CustomStringConvertible { + case spm + case xcode + + var workspace: String? { + switch self { + case .xcode: + return "Example/Stagehand.xcworkspace" + case .spm: + return nil + } + } + + var project: String? { + switch self { + case .spm: + return "generated/Stagehand.xcodeproj" + case .xcode: + return nil + } + } + + var scheme: String { + switch self { + case .xcode: + return "Stagehand Demo App" + case .spm: + return "Stagehand-Package" + } + } + + var shouldGenerateXcodeProject: Bool { + switch self { + case .spm: + return true + case .xcode: + return false + } + } + + var shouldRunTests: Bool { + switch self { + case .spm: + return false + case .xcode: + return true + } + } + + var description: String { + return rawValue + } +} + +guard CommandLine.arguments.count > 2 else { + print("Usage: build.swift [spm|xcode] ") + throw TaskError.code(1) +} + +let rawTask = CommandLine.arguments[1] +let rawPlatform = CommandLine.arguments[2] + +guard let task = Task(rawValue: rawTask) else { + print("Received unknown task \"\(rawTask)\"") + throw TaskError.code(1) +} + +if task.shouldGenerateXcodeProject { + try execute(commandPath: "/usr/bin/swift", arguments: ["package", "generate-xcodeproj", "--output=generated/"]) +} + +guard let platform = Platform(rawValue: rawPlatform) else { + print("Received unknown platform \"\(rawPlatform)\"") + throw TaskError.code(1) +} + +var xcodeBuildArguments: [String] = [] + +if let workspace = task.workspace { + xcodeBuildArguments.append("-workspace") + xcodeBuildArguments.append(workspace) +} else if let project = task.project { + xcodeBuildArguments.append("-project") + xcodeBuildArguments.append(project) +} + +xcodeBuildArguments.append( + contentsOf: [ + "-scheme", task.scheme, + "-sdk", "iphonesimulator", + "-PBXBuildsContinueAfterErrors=0", + "-destination", platform.destination, + "ONLY_ACTIVE_ARCH=NO", + ] +) + +xcodeBuildArguments.append("build") + +if task.shouldRunTests { + xcodeBuildArguments.append("test") +} + +let xcpretty: Process? +if CommandLine.arguments.count > 3 { + xcpretty = .init() + xcpretty?.launchPath = CommandLine.arguments[3] +} else { + xcpretty = nil +} + +try execute(commandPath: "/usr/bin/xcodebuild", arguments: xcodeBuildArguments, pipedTo: xcpretty) diff --git a/Scripts/ci.sh b/Scripts/ci.sh new file mode 100755 index 0000000..7f235e4 --- /dev/null +++ b/Scripts/ci.sh @@ -0,0 +1,22 @@ +#!/bin/bash -l +set -e + +# Find the directory in which this script resides. +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +actionstr=$(echo $ACTIONS | tr "," "\n") +for action in $actionstr ; do + case $action in + xcode) + $DIR/build.swift xcode $PLATFORM `which xcpretty` + ;; + + spm) + $DIR/build.swift spm $PLATFORM + ;; + + pod-lint) + bundle exec --gemfile=Example/Gemfile pod lib lint --verbose --fail-fast + ;; + esac +done diff --git a/Stagehand/Classes/Core/AnimatableProperty/AnimatableProperty+Color.swift b/Sources/Stagehand/AnimatableProperty/AnimatableProperty+Color.swift similarity index 100% rename from Stagehand/Classes/Core/AnimatableProperty/AnimatableProperty+Color.swift rename to Sources/Stagehand/AnimatableProperty/AnimatableProperty+Color.swift diff --git a/Stagehand/Classes/Core/AnimatableProperty/AnimatableProperty+CoreGraphics.swift b/Sources/Stagehand/AnimatableProperty/AnimatableProperty+CoreGraphics.swift similarity index 100% rename from Stagehand/Classes/Core/AnimatableProperty/AnimatableProperty+CoreGraphics.swift rename to Sources/Stagehand/AnimatableProperty/AnimatableProperty+CoreGraphics.swift diff --git a/Stagehand/Classes/Core/AnimatableProperty/AnimatableProperty+FloatingPoint.swift b/Sources/Stagehand/AnimatableProperty/AnimatableProperty+FloatingPoint.swift similarity index 100% rename from Stagehand/Classes/Core/AnimatableProperty/AnimatableProperty+FloatingPoint.swift rename to Sources/Stagehand/AnimatableProperty/AnimatableProperty+FloatingPoint.swift diff --git a/Stagehand/Classes/Core/AnimatableProperty/AnimatableProperty+Optional.swift b/Sources/Stagehand/AnimatableProperty/AnimatableProperty+Optional.swift similarity index 100% rename from Stagehand/Classes/Core/AnimatableProperty/AnimatableProperty+Optional.swift rename to Sources/Stagehand/AnimatableProperty/AnimatableProperty+Optional.swift diff --git a/Stagehand/Classes/Core/AnimatableProperty/AnimatableProperty.swift b/Sources/Stagehand/AnimatableProperty/AnimatableProperty.swift similarity index 100% rename from Stagehand/Classes/Core/AnimatableProperty/AnimatableProperty.swift rename to Sources/Stagehand/AnimatableProperty/AnimatableProperty.swift diff --git a/Stagehand/Classes/Core/Animation/Animation+Optimization.swift b/Sources/Stagehand/Animation/Animation+Optimization.swift similarity index 100% rename from Stagehand/Classes/Core/Animation/Animation+Optimization.swift rename to Sources/Stagehand/Animation/Animation+Optimization.swift diff --git a/Stagehand/Classes/Core/Animation/Animation.swift b/Sources/Stagehand/Animation/Animation.swift similarity index 100% rename from Stagehand/Classes/Core/Animation/Animation.swift rename to Sources/Stagehand/Animation/Animation.swift diff --git a/Stagehand/Classes/Core/AnimationCurve/AnimationCurve+Basic.swift b/Sources/Stagehand/AnimationCurve/AnimationCurve+Basic.swift similarity index 100% rename from Stagehand/Classes/Core/AnimationCurve/AnimationCurve+Basic.swift rename to Sources/Stagehand/AnimationCurve/AnimationCurve+Basic.swift diff --git a/Stagehand/Classes/Core/AnimationCurve/AnimationCurve+CubicBezier.swift b/Sources/Stagehand/AnimationCurve/AnimationCurve+CubicBezier.swift similarity index 100% rename from Stagehand/Classes/Core/AnimationCurve/AnimationCurve+CubicBezier.swift rename to Sources/Stagehand/AnimationCurve/AnimationCurve+CubicBezier.swift diff --git a/Stagehand/Classes/Core/AnimationCurve/AnimationCurve.swift b/Sources/Stagehand/AnimationCurve/AnimationCurve.swift similarity index 100% rename from Stagehand/Classes/Core/AnimationCurve/AnimationCurve.swift rename to Sources/Stagehand/AnimationCurve/AnimationCurve.swift diff --git a/Stagehand/Classes/Core/AnimationGroup.swift b/Sources/Stagehand/AnimationGroup.swift similarity index 100% rename from Stagehand/Classes/Core/AnimationGroup.swift rename to Sources/Stagehand/AnimationGroup.swift diff --git a/Stagehand/Classes/Core/AnimationInstance/AnimationInstance.swift b/Sources/Stagehand/AnimationInstance/AnimationInstance.swift similarity index 100% rename from Stagehand/Classes/Core/AnimationInstance/AnimationInstance.swift rename to Sources/Stagehand/AnimationInstance/AnimationInstance.swift diff --git a/Stagehand/Classes/Core/AnimationInstance/Renderer.swift b/Sources/Stagehand/AnimationInstance/Renderer.swift similarity index 100% rename from Stagehand/Classes/Core/AnimationInstance/Renderer.swift rename to Sources/Stagehand/AnimationInstance/Renderer.swift diff --git a/Stagehand/Classes/Core/AnimationQueue.swift b/Sources/Stagehand/AnimationQueue.swift similarity index 100% rename from Stagehand/Classes/Core/AnimationQueue.swift rename to Sources/Stagehand/AnimationQueue.swift diff --git a/Stagehand/Classes/Core/Driver/DisplayLinkDriver+Dependencies.swift b/Sources/Stagehand/Driver/DisplayLinkDriver+Dependencies.swift similarity index 100% rename from Stagehand/Classes/Core/Driver/DisplayLinkDriver+Dependencies.swift rename to Sources/Stagehand/Driver/DisplayLinkDriver+Dependencies.swift diff --git a/Stagehand/Classes/Core/Driver/DisplayLinkDriver.swift b/Sources/Stagehand/Driver/DisplayLinkDriver.swift similarity index 99% rename from Stagehand/Classes/Core/Driver/DisplayLinkDriver.swift rename to Sources/Stagehand/Driver/DisplayLinkDriver.swift index 66af47f..e1e1f53 100644 --- a/Stagehand/Classes/Core/Driver/DisplayLinkDriver.swift +++ b/Sources/Stagehand/Driver/DisplayLinkDriver.swift @@ -14,7 +14,7 @@ // limitations under the License. // -import Foundation +import QuartzCore /// An animation driver that is controlled by a `CADisplayLink`. This driver is intended for use with non-interactive /// animations that have a specified duration. diff --git a/Stagehand/Classes/Core/Driver/Driver.swift b/Sources/Stagehand/Driver/Driver.swift similarity index 100% rename from Stagehand/Classes/Core/Driver/Driver.swift rename to Sources/Stagehand/Driver/Driver.swift diff --git a/Stagehand/Classes/Core/Utilities/Comparable+Additions.swift b/Sources/Stagehand/Utilities/Comparable+Additions.swift similarity index 100% rename from Stagehand/Classes/Core/Utilities/Comparable+Additions.swift rename to Sources/Stagehand/Utilities/Comparable+Additions.swift diff --git a/Stagehand/Classes/Testing/FBSnapshotTestCase+Animation.swift b/Sources/StagehandTesting/FBSnapshotTestCase+Animation.swift similarity index 100% rename from Stagehand/Classes/Testing/FBSnapshotTestCase+Animation.swift rename to Sources/StagehandTesting/FBSnapshotTestCase+Animation.swift diff --git a/Stagehand/Classes/Testing/FBSnapshotTestCase+AnimationGIF.swift b/Sources/StagehandTesting/FBSnapshotTestCase+AnimationGIF.swift similarity index 100% rename from Stagehand/Classes/Testing/FBSnapshotTestCase+AnimationGIF.swift rename to Sources/StagehandTesting/FBSnapshotTestCase+AnimationGIF.swift diff --git a/Stagehand/Classes/Testing/SnapshotTestDriver.swift b/Sources/StagehandTesting/SnapshotTestDriver.swift similarity index 100% rename from Stagehand/Classes/Testing/SnapshotTestDriver.swift rename to Sources/StagehandTesting/SnapshotTestDriver.swift diff --git a/Stagehand.podspec b/Stagehand.podspec index 5461a7a..862d61f 100644 --- a/Stagehand.podspec +++ b/Stagehand.podspec @@ -11,7 +11,7 @@ Pod::Spec.new do |s| s.swift_version = '5.0.1' - s.source_files = 'Stagehand/Classes/Core/**/*' + s.source_files = 'Sources/Stagehand/**/*' s.frameworks = 'CoreGraphics', 'UIKit' diff --git a/StagehandTesting.podspec b/StagehandTesting.podspec index dd003a2..3c1ca04 100644 --- a/StagehandTesting.podspec +++ b/StagehandTesting.podspec @@ -11,7 +11,7 @@ Pod::Spec.new do |s| s.swift_version = '5.0.1' - s.source_files = 'Stagehand/Classes/Testing/**/*' + s.source_files = 'Sources/StagehandTesting/**/*' # The dependency on Stagehand is pinned to the same version as StagehandTesting. This is because # StagehandTesting depends on internal methods inside Stagehand, so the normal rules of semantic