diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9b3ef0f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +## `develop` + +Changes for users of the library currently on `develop`: + +_This space intentionally left blank._ + +## [1.0.0](https://github.com/NYTimes/ios-360-videos/releases/tag/1.0.0) + +Initial public release. + +- Optimize iOS version check in `NYT360PlayerScene`. + +## Older releases + +_Please see [the Releases page](https://github.com/NYTimes/ios-360-videos/releases)._ diff --git a/Documentation/Release Process.md b/Documentation/Release Process.md new file mode 100644 index 0000000..78d86ba --- /dev/null +++ b/Documentation/Release Process.md @@ -0,0 +1,21 @@ +_While tagging a new version of a library and pushing it to CocoaPods is conceptually simple, there are several fine points which must not be forgotten to produce a polished product._ + +# NYT360Video Release Process + +- Review [the diff between `master` and `develop`](https://github.com/NYTimes/ios-360-videos/compare/master...develop) to determine which part of the version number to increment, in accordance with [semantic versioning](http://semver.org/). +- Update [the Podspec](https://github.com/NYTimes/ios-360-videos/blob/develop/NYT360Video.podspec) and the framework targets in [the Xcode project](https://github.com/NYTimes/ios-360-videos/tree/develop/ios-360-videos.xcodeproj) to the new version number. + - Make this change via a pull request to trigger notifications for those who watch the repository. +- Using [the diff](https://github.com/NYTimes/ios-360-videos/compare/master...develop), [the commit history](https://github.com/NYTimes/ios-360-videos/commits/develop), and any notes about `develop` which may have been written in the `CHANGELOG`, update [`CHANGELOG.md`](https://github.com/NYTimes/ios-360-videos/blob/develop/CHANGELOG.md) with changes in the new version which may affect library users. + - When reviewing the commit history, searching for “Merge pull request” helps find changes which should appear in [the `CHANGELOG`](https://github.com/NYTimes/ios-360-videos/blob/develop/CHANGELOG.md). +- Update any other documentation which still needs to be updated, given those changes. +- Create a pull request merging [`develop` into `master`](https://github.com/NYTimes/ios-360-videos/compare/master...develop). Merge it yourself, immediately. +- Create a [Github release](https://github.com/NYTimes/ios-360-videos/releases), using the new version number (eg. `1.0.0`) as the tag, and the merge commit into `master` as the target. Copy [the `CHANGELOG`](https://raw.githubusercontent.com/NYTimes/ios-360-videos/develop/CHANGELOG.md)’s Markdown content for this release into the Github release. +- Push the new Podspec to Cocoapods Trunk: `pod trunk push NYT360Video.podspec` + - Ensure your local clone is up-to-date before this push. + +## CocoaPods Trunk Setup + +Before you can use this process to push a release to CocoaPods: + +- Set up a CocoaPods Trunk account on your machine: [Getting setup with Trunk/Getting started](https://guides.cocoapods.org/making/getting-setup-with-trunk.html#getting-started) +- Become an owner for [NYT360Video](https://cocoapods.org/pods/NYT360Video). A current owner will need to run `pod trunk add-owner NYT360Video your.email@nytimes.com` diff --git a/LICENSE.md b/LICENSE.md index 1b8afe2..b7e73dc 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,13 +1,9 @@ -Copyright (c) 2015-2016 The New York Times Company - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this library except in compliance with the License. +Copyright (c) 2016 The New York Times Company + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this library 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. + +[www.apache.org/licenses/LICENSE-2.0](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. diff --git a/NYT360Video.podspec b/NYT360Video.podspec index 0d3a285..14f133a 100644 --- a/NYT360Video.podspec +++ b/NYT360Video.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'NYT360Video' - s.version = '0.6.2' + s.version = '1.0.0' s.summary = 'NYT360Video plays 360º video streamed from an AVPlayer.' s.description = <<-DESC @@ -10,7 +10,7 @@ Pod::Spec.new do |s| DESC s.homepage = 'https://github.com/nytm/ios-360-videos/' - s.license = { :type => 'Apache', :file => 'LICENSE' } + s.license = { :type => 'Apache', :file => 'LICENSE.md' } s.author = 'The New York Times' s.source = { :git => 'git@github.com:nytm/ios-360-videos.git', :tag => s.version.to_s } diff --git a/NYT360VideoExample/ViewController.m b/NYT360VideoExample/ViewController.m index 3fbcbff..df3bc7c 100644 --- a/NYT360VideoExample/ViewController.m +++ b/NYT360VideoExample/ViewController.m @@ -22,20 +22,25 @@ @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; + self.view.backgroundColor = [UIColor blackColor]; - NSURL *videoURL = [[NSURL alloc] initWithString:@"https://vp.nyt.com/video/360/hls/video.m3u8"]; + // Create an AVPlayer for a 360º video: + NSURL * const videoURL = [[NSURL alloc] initWithString:@"https://vp.nyt.com/video/360/hls/video.m3u8"]; self.player = [[AVPlayer alloc] initWithURL:videoURL]; - self.view.backgroundColor = [UIColor blackColor]; - id manager = [NYT360MotionManager sharedManager]; + // Create a NYT360ViewController with the AVPlayer and our app's motion manager: + id const manager = [NYT360MotionManager sharedManager]; self.nyt360VC = [[NYT360ViewController alloc] initWithAVPlayer:self.player motionManager:manager]; + // Embed the player view controller in our UI, via view controller containment: [self addChildViewController:self.nyt360VC]; [self.view addSubview:self.nyt360VC.view]; [self.nyt360VC didMoveToParentViewController:self]; + // Begin playing the 360º video: [self.player play]; - + + // In this example, tapping the video will place the horizon in the middle of the screen: UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reorientVerticalCameraAngle:)]; [self.view addGestureRecognizer:tapRecognizer]; } diff --git a/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m b/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m index 4df5f5a..d0dfba4 100644 --- a/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m +++ b/NYT360VideoTests/NYT360EulerAngleCalculationsTests.m @@ -1,6 +1,6 @@ // // NYT360EulerAngleCalculationsTests.m -// NYT360Video +// NYT360VideoTests // // Created by Jared Sinclair on 7/27/16. // Copyright © 2016 The New York Times Company. All rights reserved. diff --git a/NYT360VideoTests/NYT360MotionManagerTests.m b/NYT360VideoTests/NYT360MotionManagerTests.m index e660c8a..c8a80d1 100644 --- a/NYT360VideoTests/NYT360MotionManagerTests.m +++ b/NYT360VideoTests/NYT360MotionManagerTests.m @@ -1,6 +1,6 @@ // // NYT360MotionManagerTests.m -// NYT360Video +// NYT360VideoTests // // Created by Jared Sinclair on 8/3/16. // Copyright © 2016 The New York Times Company. All rights reserved. diff --git a/README.md b/README.md index d2a3599..21a860b 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # NYT360Video -## 360º video playback from The New York Times - [![Version](https://img.shields.io/cocoapods/v/NYT360Video.svg?style=flat)](http://cocoapods.org/pods/NYT360Video) [![License](https://img.shields.io/cocoapods/l/NYT360Video.svg?style=flat)](http://cocoapods.org/pods/NYT360Video) [![Platform](https://img.shields.io/cocoapods/p/NYT360Video.svg?style=flat)](http://cocoapods.org/pods/NYT360Video) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) + +## 360º video playback from The New York Times NYT360Video is a framework allowing playback of a 360º video stream from an `AVPlayer`. @@ -11,17 +12,49 @@ It provides no control user interface; it is intended to be embedded in your own ## Usage -TKTK +[`NYT360ViewController`](https://github.com/NYTimes/ios-360-videos/blob/develop/Sources/NYT360ViewController.h) is the entry point for the library. [Initialize](https://github.com/NYTimes/ios-360-videos/blob/68c522d51d6c88ddd705e4febbb480de825cdc5d/Sources/NYT360ViewController.h#L67) it with an `AVPlayer` instance and your application’s motion manager. (Motion management is discussed in the next section.) + +Once it’s initialized, embed your `NYT360ViewController` instance in your view hierarchy via [view controller containment](https://www.objc.io/issues/1-view-controllers/containment-view-controller/). + +[The Example application demonstrates](https://github.com/NYTimes/ios-360-videos/blob/develop/NYT360VideoExample/ViewController.m) how to set this up. + +### Motion Management + +Apple’s documentation warns, + +> An app should create only a single instance of the `CMMotionManager` class. + +To cope with this limitation, NYT360Video doesn’t have to create its own `CMMotionManager` instance to receive device motion updates. Instead, you’ll inject a motion manager when you create a `NYT360ViewController`. + +The expectations for this motion manager are set by [the `NYT360MotionManagement` protocol](https://github.com/NYTimes/ios-360-videos/blob/develop/Sources/NYT360MotionManagement.h); see the header for a detailed description of those requirements. + +If your application doesn’t use `CMMotionManager` elsewhere, you can simply use [the `NYT360MotionManager` singleton provided with this library](https://github.com/NYTimes/ios-360-videos/blob/develop/Sources/NYT360MotionManager.h) to fulfill these requirements. + +Otherwise, if your app has a motion manager already, you’ll need to make it conform to [`NYT360MotionManagement`](https://github.com/NYTimes/ios-360-videos/blob/develop/Sources/NYT360MotionManagement.h) and use it when creating a `NYT360ViewController`. + +### Managing Gesture Interactions + +You may want to restrict the gesture-based interactions with `NYT360ViewController` in certain cases in your application — for example, when embedded in a vertically-scrolling view, `NYT360ViewController` should not intercept vertical pan gestures. There are a few ways to accomplish this with NYT360Video. + +First, [`NYT360ViewController` provides properties](https://github.com/NYTimes/ios-360-videos/blob/68c522d51d6c88ddd705e4febbb480de825cdc5d/Sources/NYT360ViewController.h#L111) to configure which axes of movement are allowed. (This would be the simplest way to solve the example problem set out above.) + +The library exposes its pan gesture recognizer as a property on `NYT360ViewController` for more advanced ingegration with other gesture recognizers. And finally, the type `NYT360CameraPanGestureRecognizer` is introduced so that host applications can more easily configure interaction with other gesture recognizers, without having to refer to specific instances of an NYT360Video gesture recognizer. ## Requirements -TKTK +NYT360Video works on iOS 8+. ## Installation ### Carthage -TKTK +NYT360Video may be fetched and built via [Carthage](https://github.com/Carthage/Carthage). To install it, simply add the following line to your `Cartfile`: + +``` +github "NYTimes/ios-360-videos" +``` + +Then, following the instructions for [integrating Carthage frameworks into your app](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos), link the `NYT360Video` framework into your project. ### CocoaPods @@ -33,17 +66,28 @@ pod 'NYT360Video' ## Known Issues -- **iOS 10 CoreAudio Crash** - On devices running iOS 10 (at least as of Beta 7), host applications will crash if the device is locked while an NYT360ViewController is visible (whether paused or not). The crash is caused by a CoreAudio exception. [An extended discussion of the issue can be found here](https://github.com/nytm/ios-360-videos/issues/37). A workaround that appears to work for some, though not all, apps is to enable the background audio capability in the host application's plist. +- **iOS 10 CoreAudio Crash** - On devices running iOS 10 (at least as of Beta 7), host applications will crash if the device is locked while an `NYT360ViewController` is visible (whether paused or not). The crash is caused by a CoreAudio exception. A workaround that appears to work for some, though not all, apps is to enable the background audio capability in the host application’s plist. [An extended discussion of the issue can be found in issue #37](https://github.com/nytimes/ios-360-videos/issues/37). + +_See also [this project’s issue tracker](https://github.com/NYTimes/ios-360-videos/issues)._ ## Contributing -1. Fork it +Contributions are welcomed via GitHub’s pull request system. As a note: + +- Contributions which include unit tests, where feasible, are likely to be merged more easily. +- Pull requests which add player UI are unlikely to be accepted. We consider that to be a separate responsibility; this library aims to provide only lower-level 360º-specific playback functionality. + +1. [Fork this repository](https://github.com/NYTimes/ios-360-videos/fork) 2. Create your feature branch: `git checkout -b my-awesome-new-feature` 3. Commit your changes: `git commit -m 'Add some awesome feature'` -4. Push to the branch: `git push origin my-awesome-new-feature` -5. Submit a pull request + _Please split your commits up logically, and be sure to write [good commit messages](https://www.dzombak.com/blog/2015/10/Writing-good-commit-messages.html)._ +4. Push to your branch: `git push origin my-awesome-new-feature` +5. Submit a [pull request](https://github.com/NYTimes/ios-360-videos/pulls) to this repository + +## Version History + +See [CHANGELOG.md](CHANGELOG.md). ## License -- This code is under [Apache 2.0 - license](https://github.com/NYTimes/ios-360-videos/blob/master/LICENSE.md). +NYT360Video is released under the [Apache 2.0 license](LICENSE.md). diff --git a/Sources/Info.plist b/Sources/Info.plist index 6f21e11..60b9c00 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.6.2 + 1.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/Sources/NYT360CameraPanGestureRecognizer.h b/Sources/NYT360CameraPanGestureRecognizer.h index 8efcc96..0b9b7cd 100644 --- a/Sources/NYT360CameraPanGestureRecognizer.h +++ b/Sources/NYT360CameraPanGestureRecognizer.h @@ -9,8 +9,9 @@ @import UIKit; /** - * An otherwise vanilla subclass of UIPanGestureRecognizer used by NYT360Video to enable manual camera panning. This class is exposed so that host applications can more easily configure interaction with other gesture recognizers without having to have references to specific instances of an NYT360Video pan recognizer. + * An otherwise vanilla subclass of UIPanGestureRecognizer used by NYT360Video to enable manual camera panning. + * + * This class is exposed so that host applications can more easily configure interaction with other gesture recognizers, without having to refer to specific instances of an NYT360Video gesture recognizer. */ @interface NYT360CameraPanGestureRecognizer : UIPanGestureRecognizer - @end diff --git a/Sources/NYT360CameraPanGestureRecognizer.m b/Sources/NYT360CameraPanGestureRecognizer.m index 7a2cf95..a21ae82 100644 --- a/Sources/NYT360CameraPanGestureRecognizer.m +++ b/Sources/NYT360CameraPanGestureRecognizer.m @@ -9,5 +9,4 @@ #import "NYT360CameraPanGestureRecognizer.h" @implementation NYT360CameraPanGestureRecognizer - @end diff --git a/Sources/NYT360MotionManagement.h b/Sources/NYT360MotionManagement.h index bbf40e5..32183d9 100644 --- a/Sources/NYT360MotionManagement.h +++ b/Sources/NYT360MotionManagement.h @@ -14,25 +14,28 @@ NS_ASSUME_NONNULL_BEGIN typedef id NYT360MotionManagementToken; /** + Expectations that must be fulfilled by an appliation-wide "wrapper" around + CMMotionManager for NYT360Video's use. + Per Apple's documentation, it is recommended that an application will have no more than one `CMMotionManager`, otherwise performance could degrade. The - challenge for the host application using NYT360Videos is that there may be - entities outside of NYT360Videos that also require device motion updates. It is + challenge for the host application using NYT360Video is that there may be + entities outside of NYT360Video that also require device motion updates. It is undesirable that all these entities would have direct access to the same shared manager, leading to misconfigurations or a premature call to `stopDeviceMotionUpdates`. To facilitate the use of a shared motion manager without exposing a surface area for misuse, the `NYT360MotionManagement` protocol defines a set of expectations for a shared "wrapper" around a shared - `CMMotionManager`. The conforming class will ensure that a shared + `CMMotionManager`. The conforming class must ensure that a shared `CMMotionManager` is kept private, properly configured, and activated or deactivated at the appropriate times. - + A host application is free to provide a custom class conforming to `NYT360MotionManagement`. If your application does not use a CMMotionManager outside of NYT360Videos, we recommend that you use the shared instance of `NYT360MotionManager`, a ready-made class that already conforms to `NYT360MotionManagement`. - + @seealso `NYT360MotionManager.h` */ @protocol NYT360MotionManagement diff --git a/Sources/NYT360MotionManager.h b/Sources/NYT360MotionManager.h index ac09076..5cb5056 100644 --- a/Sources/NYT360MotionManager.h +++ b/Sources/NYT360MotionManager.h @@ -12,7 +12,7 @@ #import "NYT360MotionManagement.h" /** - This is an example implementation of `NYT360MotionManagement`. Your application + A reference implementation of `NYT360MotionManagement`. Your host application can provide another implementation if so desired. @seealso `NYT360ViewController`. @@ -21,9 +21,13 @@ #pragma mark - Singleton +/** + The shared, app-wide `NYT360MotionManager`. + */ + (instancetype)sharedManager; #pragma mark - Internal +// The following internal state is exposed for testing. - (NSTimeInterval)resolvedUpdateInterval; - (NSUInteger)numberOfObservers; diff --git a/Sources/NYT360PlayerScene.h b/Sources/NYT360PlayerScene.h index eb43fe8..7e9c74d 100644 --- a/Sources/NYT360PlayerScene.h +++ b/Sources/NYT360PlayerScene.h @@ -10,6 +10,9 @@ NS_ASSUME_NONNULL_BEGIN @import SceneKit; +/** + A 3D video playback scene. + */ @interface NYT360PlayerScene : SCNScene @property (nonatomic, readonly) SCNCamera *camera; diff --git a/Sources/NYT360PlayerScene.m b/Sources/NYT360PlayerScene.m index 599be58..b0ea7dd 100644 --- a/Sources/NYT360PlayerScene.m +++ b/Sources/NYT360PlayerScene.m @@ -129,7 +129,7 @@ - (void)play { // See note in NYTSKVideoNode above. self.videoPlaybackIsPaused = NO; - if ([self isIOS10OrLater]) { + if ([self.class isIOS10OrLater]) { // On iOS 10, AVPlayer playback on a video node seems to work most // reliably by directly invoking `play` and `pause` on the player, // rather than the alternatives: calling the `play` and `pause` methods @@ -165,7 +165,7 @@ - (void)pause { // See note in NYTSKVideoNode above. self.videoPlaybackIsPaused = YES; - if ([self isIOS10OrLater]) { + if ([self.class isIOS10OrLater]) { // On iOS 10, AVPlayer playback on a video node seems to work most // reliably by directly invoking `play` and `pause` on the player, // rather than the alternatives: calling the `play` and `pause` methods @@ -218,12 +218,20 @@ - (BOOL)videoNodeShouldAllowPlaybackToBegin:(NYTSKVideoNode *)videoNode { #pragma mark - Convenience -- (BOOL)isIOS10OrLater { - NSOperatingSystemVersion ios10; - ios10.majorVersion = 10; - ios10.minorVersion = 0; - ios10.patchVersion = 0; - return [[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ios10]; ++ (BOOL)isIOS10OrLater { + static BOOL isIOS10OrLater; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSOperatingSystemVersion ios10; + ios10.majorVersion = 10; + ios10.minorVersion = 0; + ios10.patchVersion = 0; + + isIOS10OrLater = [[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ios10]; + }); + + return isIOS10OrLater; } @end diff --git a/Sources/NYT360ViewController.h b/Sources/NYT360ViewController.h index 2b4b76f..173ce80 100644 --- a/Sources/NYT360ViewController.h +++ b/Sources/NYT360ViewController.h @@ -43,20 +43,39 @@ NS_ASSUME_NONNULL_BEGIN @end +/** + * NYT360ViewController plays 360º video from an AVPlayer and handles user interaction to move the camera around the video. + * + * NYT360ViewController should be initialized and then embedded in your application UI via view controller containment. + * + * This class is the entry point for the NYT360Video library. + */ @interface NYT360ViewController : UIViewController /** * The delegate of the view controller. + * + * @seealso NYT360ViewControllerDelegate */ @property (nullable, nonatomic, weak) id delegate; #pragma mark - Initializers +/** + * Initialize a new 360 playback view controller, with the given AVPlayer instance and device motion manager. + */ - (id)initWithAVPlayer:(AVPlayer *)player motionManager:(id)motionManager; #pragma mark - Playback +/** + * Play the view controller's video. + */ - (void)play; + +/** + * Pause the view controller's video. + */ - (void)pause; #pragma mark - Camera Movement diff --git a/ios-360-videos.xcodeproj/project.pbxproj b/ios-360-videos.xcodeproj/project.pbxproj index b858f10..614a829 100644 --- a/ios-360-videos.xcodeproj/project.pbxproj +++ b/ios-360-videos.xcodeproj/project.pbxproj @@ -92,7 +92,7 @@ 934A49491D46B0A1001AD295 /* NYT360Video.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NYT360Video.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 934A494C1D46B0A1001AD295 /* NYT360Video.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NYT360Video.h; sourceTree = ""; }; 934A49531D46B119001AD295 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 934A49561D46B32D001AD295 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + 934A49561D46B32D001AD295 /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = ""; }; 934A49571D46B32D001AD295 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 934A495C1D46B3B5001AD295 /* NYT360VideoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NYT360VideoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 934A496B1D46B3FD001AD295 /* NYT360VideoExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NYT360VideoExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -180,7 +180,7 @@ children = ( 934A49961D46BBB6001AD295 /* NYT360Video.podspec */, 934A49571D46B32D001AD295 /* README.md */, - 934A49561D46B32D001AD295 /* LICENSE */, + 934A49561D46B32D001AD295 /* LICENSE.md */, ); name = Metadata; sourceTree = ""; @@ -203,11 +203,11 @@ children = ( 4736BBE21D5393E500262306 /* AppDelegate.h */, 4736BBE31D5393E500262306 /* AppDelegate.m */, + 4736BBE51D5393E500262306 /* ViewController.h */, + 4736BBE61D5393E500262306 /* ViewController.m */, 4736BBEE1D5393F600262306 /* LaunchScreen.storyboard */, 4736BBF01D5393F600262306 /* Main.storyboard */, 4736BBE41D5393E500262306 /* Assets.xcassets */, - 4736BBE51D5393E500262306 /* ViewController.h */, - 4736BBE61D5393E500262306 /* ViewController.m */, 4736BBEC1D5393EB00262306 /* Info.plist */, 934A496D1D46B3FD001AD295 /* Supporting Files */, );