├── .gitignore ├── .DS_Store ├── Replay_.png ├── Replay ├── .DS_Store ├── Replay │ ├── .DS_Store │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── App Icon & Top Shelf Image.brandassets │ │ │ ├── App Icon - Large.imagestack │ │ │ │ ├── Back.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ ├── Replay11_.png │ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Front.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ ├── Replay11_.png │ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Middle.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ ├── Replay11_.png │ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── App Icon - Small.imagestack │ │ │ │ ├── Back.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ ├── Replay11.png │ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Front.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ ├── Replay11.png │ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Middle.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ ├── Replay11.png │ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Top Shelf Image.imageset │ │ │ │ ├── AppleTV-TopShelf-1920@1x.png │ │ │ │ └── Contents.json │ │ │ ├── Top Shelf Image Wide.imageset │ │ │ │ ├── AppleTV-TopShelf-2320x.png │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ ├── ReplayLaunchImage.png │ │ │ └── Contents.json │ ├── BridgingHeader.h │ ├── Info.plist │ ├── AppDelegate.swift │ ├── Base.lproj │ │ └── Main.storyboard │ └── ViewController.swift ├── Replay.xcodeproj │ ├── project.xcworkspace │ │ ├── xcuserdata │ │ │ ├── rajivs.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ │ └── npitchandi.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── contents.xcworkspacedata │ ├── xcuserdata │ │ ├── rajivs.xcuserdatad │ │ │ └── xcschemes │ │ │ │ ├── xcschememanagement.plist │ │ │ │ └── Replay.xcscheme │ │ └── npitchandi.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ └── Replay.xcscheme │ └── project.pbxproj ├── ReplayTests │ ├── Info.plist │ └── ReplayTests.swift └── ReplayUITests │ ├── Info.plist │ └── ReplayUITests.swift ├── NOTICE.txt ├── LICENSE.txt ├── CONTRIBUTING.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/.DS_Store -------------------------------------------------------------------------------- /Replay_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay_.png -------------------------------------------------------------------------------- /Replay/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/.DS_Store -------------------------------------------------------------------------------- /Replay/Replay/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay/.DS_Store -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/LaunchImage.launchimage/ReplayLaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay/Assets.xcassets/LaunchImage.launchimage/ReplayLaunchImage.png -------------------------------------------------------------------------------- /Replay/Replay.xcodeproj/project.xcworkspace/xcuserdata/rajivs.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay.xcodeproj/project.xcworkspace/xcuserdata/rajivs.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Replay/Replay.xcodeproj/project.xcworkspace/xcuserdata/npitchandi.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay.xcodeproj/project.xcworkspace/xcuserdata/npitchandi.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Replay/Replay.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/AppleTV-TopShelf-1920@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/AppleTV-TopShelf-1920@1x.png -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/AppleTV-TopShelf-2320x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/AppleTV-TopShelf-2320x.png -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "filename" : "AppleTV-TopShelf-1920@1x.png", 6 | "scale" : "1x" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /Replay/Replay/BridgingHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // BridgingHeader.h 3 | // Replay 4 | // 5 | // Created by Rajiv Singh on 8/15/17. 6 | // Copyright © 2017 Rajiv Singh. All rights reserved. 7 | // 8 | 9 | #ifndef BridgingHeader_h 10 | #define BridgingHeader_h 11 | 12 | #import 13 | 14 | #endif /* BridgingHeader_h */ 15 | -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "filename" : "AppleTV-TopShelf-2320x.png", 6 | "scale" : "1x" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Replay11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Replay11.png -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Replay11_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Replay11_.png -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Replay11_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Replay11_.png -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Replay11_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Replay11_.png -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Replay11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Replay11.png -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Replay11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnissa-archive/replay-app-for-tvos/master/Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Replay11.png -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "filename" : "Replay11_.png", 6 | "scale" : "1x" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "filename" : "Replay11_.png", 6 | "scale" : "1x" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "filename" : "Replay11.png", 6 | "scale" : "1x" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "filename" : "Replay11.png", 6 | "scale" : "1x" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "filename" : "Replay11.png", 6 | "scale" : "1x" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "filename" : "Replay11_.png", 6 | "scale" : "1x" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "landscape", 5 | "idiom" : "tv", 6 | "filename" : "ReplayLaunchImage.png", 7 | "extent" : "full-screen", 8 | "minimum-system-version" : "9.0", 9 | "scale" : "1x" 10 | } 11 | ], 12 | "info" : { 13 | "version" : 1, 14 | "author" : "xcode" 15 | } 16 | } -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers" : [ 3 | { 4 | "filename" : "Front.imagestacklayer" 5 | }, 6 | { 7 | "filename" : "Middle.imagestacklayer" 8 | }, 9 | { 10 | "filename" : "Back.imagestacklayer" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers" : [ 3 | { 4 | "filename" : "Front.imagestacklayer" 5 | }, 6 | { 7 | "filename" : "Middle.imagestacklayer" 8 | }, 9 | { 10 | "filename" : "Back.imagestacklayer" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | replay-app-for-tvOS 2 | 3 | Copyright 2017 Omnissa, LLC. All Rights Reserved. 4 | 5 | This product is licensed to you under the BSD-2 license (the "License"). You may not use this product except in compliance with the BSD-2 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | 9 | -------------------------------------------------------------------------------- /Replay/ReplayTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Replay/ReplayUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Replay/Replay.xcodeproj/xcuserdata/rajivs.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Replay.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 44A51DEA1F434820008A1C22 16 | 17 | primary 18 | 19 | 20 | 44A51DFB1F434820008A1C22 21 | 22 | primary 23 | 24 | 25 | 44A51E061F434820008A1C22 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Replay/Replay/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "size" : "1280x768", 5 | "idiom" : "tv", 6 | "filename" : "App Icon - Large.imagestack", 7 | "role" : "primary-app-icon" 8 | }, 9 | { 10 | "size" : "400x240", 11 | "idiom" : "tv", 12 | "filename" : "App Icon - Small.imagestack", 13 | "role" : "primary-app-icon" 14 | }, 15 | { 16 | "size" : "2320x720", 17 | "idiom" : "tv", 18 | "filename" : "Top Shelf Image Wide.imageset", 19 | "role" : "top-shelf-image-wide" 20 | }, 21 | { 22 | "size" : "1920x720", 23 | "idiom" : "tv", 24 | "filename" : "Top Shelf Image.imageset", 25 | "role" : "top-shelf-image" 26 | } 27 | ], 28 | "info" : { 29 | "version" : 1, 30 | "author" : "xcode" 31 | } 32 | } -------------------------------------------------------------------------------- /Replay/Replay.xcodeproj/xcuserdata/npitchandi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Replay.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 44A51DEA1F434820008A1C22 16 | 17 | primary 18 | 19 | 20 | 44A51DFB1F434820008A1C22 21 | 22 | primary 23 | 24 | 25 | 44A51E061F434820008A1C22 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Replay/ReplayTests/ReplayTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReplayTests.swift 3 | // ReplayTests 4 | // 5 | // Created by Rajiv Singh on 8/15/17. 6 | // Copyright © 2017 Rajiv Singh. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Replay 11 | 12 | class ReplayTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Replay/Replay/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Replay 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | arm64 30 | 31 | UIUserInterfaceStyle 32 | Automatic 33 | NSAppTransportSecurity 34 | 35 | NSAllowsArbitraryLoads 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Replay/ReplayUITests/ReplayUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReplayUITests.swift 3 | // ReplayUITests 4 | // 5 | // Created by Rajiv Singh on 8/15/17. 6 | // Copyright © 2017 Rajiv Singh. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class ReplayUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | replay-app-for-tvOS 2 | 3 | Copyright 2017 Omnissa, LLC. All rights reserved 4 | 5 | The BSD-2 license (the "License") set forth below applies to all parts of the replay-app-for-tvOS 6 | project. You may not use this file except in compliance with the License.� 7 | 8 | BSD-2 License 9 | 10 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 11 | � Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 12 | � Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to replay-app-for-tvos 2 | 3 | The replay-app-for-tvos project team welcomes contributions from the community. 4 | 5 | ## Contribution Flow 6 | 7 | This is a rough outline of what a contributor's workflow looks like: 8 | 9 | - Create a topic branch from where you want to base your work 10 | - Make commits of logical units 11 | - Make sure your commit messages are in the proper format (see below) 12 | - Push your changes to a topic branch in your fork of the repository 13 | - Submit a pull request 14 | 15 | Example: 16 | 17 | ``` shell 18 | git remote add upstream https://github.com/omnissa-archive/replay-app-for-tvos.git 19 | git checkout -b my-new-feature master 20 | git commit -a 21 | git push origin my-new-feature 22 | ``` 23 | 24 | ### Staying In Sync With Upstream 25 | 26 | When your branch gets out of sync with the omnissa-archive/master branch, use the following to update: 27 | 28 | ``` shell 29 | git checkout my-new-feature 30 | git fetch -a 31 | git pull --rebase upstream master 32 | git push --force-with-lease origin my-new-feature 33 | ``` 34 | 35 | ### Updating pull requests 36 | 37 | If your PR fails to pass CI or needs changes based on code review, you'll most likely want to squash these changes into 38 | existing commits. 39 | 40 | If your pull request contains a single commit or your changes are related to the most recent commit, you can simply 41 | amend the commit. 42 | 43 | ``` shell 44 | git add . 45 | git commit --amend 46 | git push --force-with-lease origin my-new-feature 47 | ``` 48 | 49 | If you need to squash changes into an earlier commit, you can use: 50 | 51 | ``` shell 52 | git add . 53 | git commit --fixup 54 | git rebase -i --autosquash master 55 | git push --force-with-lease origin my-new-feature 56 | ``` 57 | 58 | Be sure to add a comment to the PR indicating your new changes are ready to review, as GitHub does not generate a 59 | notification when you git push. 60 | 61 | ### Code Style 62 | 63 | ### Formatting Commit Messages 64 | 65 | We follow the conventions on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/). 66 | 67 | Be sure to include any related GitHub issue references in the commit message. See 68 | [GFM syntax](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) for referencing issues 69 | and commits. 70 | 71 | ## Reporting Bugs and Creating Issues 72 | 73 | When opening a new issue, try to roughly follow the commit message format conventions above. 74 | 75 | -------------------------------------------------------------------------------- /Replay/Replay/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Replay 4 | // 5 | // Created by Rajiv Singh and Naveen Pitchandi on 8/24/17. 6 | // Copyright 2017 Omnissa, LLC. All Rights Reserved. 7 | 8 | //This product is licensed to you under the BSD-2 license (the "License"). You may not use this product except in compliance with the BSD-2 License. 9 | 10 | //This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 11 | 12 | 13 | import UIKit 14 | 15 | @UIApplicationMain 16 | class AppDelegate: UIResponder, UIApplicationDelegate { 17 | 18 | var window: UIWindow? 19 | 20 | 21 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 22 | // Override point for customization after application launch. 23 | return true 24 | } 25 | 26 | func applicationWillResignActive(_ application: UIApplication) { 27 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 28 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 29 | } 30 | 31 | func applicationDidEnterBackground(_ application: UIApplication) { 32 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | func applicationWillEnterForeground(_ application: UIApplication) { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | func applicationDidBecomeActive(_ application: UIApplication) { 41 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 42 | } 43 | 44 | func applicationWillTerminate(_ application: UIApplication) { 45 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 46 | } 47 | 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Replay/Replay/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Replay](./Replay_.png) 2 | 3 | # Replay 4 | 5 | tvOS application that converts an Apple TV into to a digital signage / kiosk 6 | 7 | ## Highlight Features 8 | 9 | - Plays video on launch and runs in a loop 10 | - Caches video for replay, conserving network bandwidth 11 | - Allows for airplay mirroring on top of the video and resumes once session is terminated 12 | - In conjunction with MDM, can be locked onto screen without any input from remote or remote app (on iPhone) to be a true Kiosk! 13 | 14 | 15 | ## Description 16 | 17 | The application is designed to start a video on launch and keeping running in an infinite loop. The video being played can be hosted in any public facing server. If you don't have any servers that you can host the videos on, a free AWS S3 account (Free storage up to 5GB) can be used. 18 | 19 | ## How does Replay cache videos? 20 | 21 | Videos are cached locally as application data with one complete run of the video (advise two just to be safe) 22 | 23 | ## Supported Video Formats 24 | 25 | - H.264 video up to 1080p, 30 frames per second, High or Main Profile level 4.0 or lower, Baseline profile level 3.0 or lower with AAC-LC audio up to 160 Kbps per channel, 48kHz, stereo audio in .m4v, .mp4, and .mov file formats 26 | - MPEG-4 video up to 2.5 Mbps, 640 by 480 pixels, 30 frames per second, Simple Profile with AAC-LC audio up to 160 Kbps, 48kHz, stereo audio in .m4v, .mp4, and .mov file formats 27 | - Motion JPEG (M-JPEG) up to 35 Mbps, 1280 by 720 pixels, 30 frames per second, audio in ulaw, PCM stereo audio in .avi file format 28 | 29 | ## Customization 30 | 31 | To customize the applcation for your needs, follow the steps below: 32 | 33 | STEP 1: Update the bundleID and link your Apple Developer Account 34 | 35 | STEP 2: Edit the URL for the video that you'd like to play 36 | 37 | ``` 38 | // URL of the Video 39 | let media = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" 40 | " 41 | ``` 42 | 43 | STEP 3 (Optional): Customize the Icon and Top Shelf images 44 | 45 | Here's a [link](https://developer.apple.com/design/resources/) to Apple's design resources for template 46 | For more information on icon and top shelf images for tvOS, Refer [this] (https://developer.apple.com/tvos/human-interface-guidelines/icons-and-images/) 47 | 48 | ## Deployment 49 | 50 | Apple has made significant improvements to management capabilities of Apple TVs on tvOS 10.2 with the introduction of [DEP](https://support.apple.com/en-us/HT204142) and Enterprise Application Management. 51 | 52 | ### Enrollment 53 | 54 | - DEP on tvOS allows for a true Zero-Touch deployment with the a mode known as "Auto Advance Setup" - which when configured in the DEP profile assigned to the Apple TV, allows the Apple TV to enroll into MDM and skip all the screens to go straight to the spring board within 30seconds of network active when connected to Ethernet. 55 | 56 | Essentially, the deployment steps would be 57 | 1) Connect Apple TV to power 58 | 2) Connect to Ethernet and wait 30 seconds and watch the rest unfold 59 | 60 | *NOTE:* With the "Auto Advance Setup", do not pair the remote / tap to setup during the first 30 seconds, the key is to power on Apple TV and just wait. 61 | 62 | ### Application Management 63 | 64 | - Enterprise developed applications can now be managed for tvOS and be downloaded and installed automatically on enrollment. Replay application can hence be effectively managed using Omnissa AirWatch to transform a display to true Digital Signage 65 | 66 | ### Configuration Policies 67 | 68 | - Once the application has been installed, a configuration policy known as 'Single App Mode' with the reference of the bundle ID of this application can be deployed to the devices to automatically launch the applicaiton and lock it to the screen 69 | - In addition to the 'Single App Mode' policy, there are few more restrictions that can be added on top to enforce security such as : 1) Force incoming Airplay Requests for pairing password 2) Restrict pairing Remote app (iPhone) 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Replay/Replay.xcodeproj/xcuserdata/rajivs.xcuserdatad/xcschemes/Replay.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Replay/Replay.xcodeproj/xcuserdata/npitchandi.xcuserdatad/xcschemes/Replay.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Replay/Replay/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Replay 4 | // 5 | // Created by Rajiv Singh and Naveen Pitchandi on 8/24/17. 6 | 7 | // Copyright 2017 Omnissa, LLC. All Rights Reserved. 8 | 9 | //This product is licensed to you under the BSD-2 license (the "License"). You may not use this product except in compliance with the BSD-2 License. 10 | 11 | //This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 12 | 13 | import UIKit 14 | import AVFoundation 15 | import AVKit 16 | 17 | enum PlaybackState { 18 | case unknown 19 | case initializing 20 | case initialized 21 | case playing 22 | case paused 23 | case stopped 24 | case unplayable 25 | } 26 | 27 | class ViewController: UIViewController { 28 | 29 | // URL of the Video 30 | var urlArray: [String] = ["http://techslides.com/demos/sample-videos/small.mp4", "https://media.w3.org/2010/05/sintel/trailer.mp4"] 31 | var player: AVQueuePlayer? = nil 32 | var playbackStatus : PlaybackState = .unknown 33 | 34 | @IBOutlet var statusLabel : UILabel? = nil 35 | 36 | lazy var spinner = UIActivityIndicatorView.init(activityIndicatorStyle: .whiteLarge) 37 | 38 | // MARK: 39 | // MARK: View life cycle 40 | 41 | override func viewDidLoad() { 42 | super.viewDidLoad() 43 | // Do any additional setup after loading the view, typically from a nib. 44 | registerNotifications() 45 | initAudioSession() 46 | } 47 | 48 | override func viewDidAppear(_ animated: Bool) { 49 | super.viewDidAppear(animated) 50 | 51 | if playbackStatus == .unknown { 52 | playbackStatus = .initializing 53 | initPlayer() 54 | }else if playbackStatus == .playing { 55 | self.statusLabel?.text = "Player interrupted" 56 | } 57 | } 58 | 59 | // MARK: 60 | // MARK: memory management 61 | 62 | deinit { 63 | deregisterNotifications() 64 | } 65 | 66 | override func didReceiveMemoryWarning() { 67 | super.didReceiveMemoryWarning() 68 | // Dispose of any resources that can be recreated. 69 | } 70 | 71 | // MARK: 72 | // MARK: Initializations 73 | 74 | func initAudioSession() -> Void { 75 | let audioSession = AVAudioSession.sharedInstance() 76 | do { 77 | try audioSession.setCategory(AVAudioSessionCategoryPlayback) 78 | } 79 | catch { 80 | print("Setting category to AVAudioSessionCategoryPlayback failed.") 81 | } 82 | } 83 | 84 | func initPlayer() -> Void { 85 | 86 | if urlArray.isEmpty { 87 | playbackStatus = .unplayable 88 | self.statusLabel?.text = "No media specified for playback" 89 | return 90 | } 91 | 92 | let asset = assets(forMediaURLs: urlArray) 93 | 94 | guard let currentAssetPlaying = asset.first else { 95 | playbackStatus = .unplayable 96 | self.statusLabel?.text = "Could not create Assets from the supplied media URL's" 97 | return 98 | } 99 | 100 | let playableKey = "playable" 101 | 102 | // In future, we should load all assets at once and add to player queue only ones which are playable. 103 | currentAssetPlaying.loadValuesAsynchronously(forKeys: [playableKey], completionHandler: { 104 | DispatchQueue.main.async { 105 | var error: NSError? = nil 106 | let status = currentAssetPlaying.statusOfValue(forKey: playableKey, error: &error) 107 | switch status { 108 | case .loaded: 109 | // Sucessfully loaded. Continue processing. 110 | self.statusLabel?.text = "Player initialized" 111 | self.playbackStatus = .initialized 112 | self.startPlayer(forAssets: asset) 113 | break 114 | case .failed: 115 | // Handle error 116 | self.playbackStatus = .unplayable 117 | self.statusLabel?.text = "Failed to initialize the player. Error: \(error?.localizedDescription ?? "")" 118 | break 119 | case .cancelled: 120 | // Terminate processing 121 | self.playbackStatus = .unplayable 122 | self.statusLabel?.text = "Initializing player was cancelled. Error: \(error?.localizedDescription ?? "")" 123 | break 124 | default: 125 | // Handle all other cases 126 | self.playbackStatus = .unplayable 127 | self.statusLabel?.text = "Unknown error while initilizing the player" 128 | break 129 | } 130 | } 131 | }) 132 | } 133 | 134 | // MARK: 135 | // MARK: Playback 136 | 137 | func startPlayer(forAssets asset: [AVURLAsset]) -> Void { 138 | 139 | if asset.isEmpty { 140 | self.playbackStatus = .unplayable 141 | self.statusLabel?.text = "Media asset not present" 142 | return 143 | } 144 | 145 | let playerItemArray = playerItemArrayCollection(forAsset: asset) 146 | if playerItemArray.isEmpty { 147 | self.playbackStatus = .unplayable 148 | self.statusLabel?.text = "Media asset not present" 149 | return 150 | } 151 | 152 | // Create a new AVPlayerViewController and pass it a reference to the player. 153 | self.player = AVQueuePlayer.init(items: playerItemArray) 154 | self.player?.actionAtItemEnd = .none 155 | if let controller = constructPlayerViewController(player: self.player) { 156 | // Modally present the player and call the player's play() method when complete. 157 | self.present(controller, animated: true) { 158 | self.playbackStatus = .playing 159 | controller.player?.play() 160 | } 161 | } 162 | } 163 | 164 | func resumePlayer(player: AVQueuePlayer?) -> Void { 165 | guard (self.playbackStatus == .playing || self.playbackStatus == .paused || self.playbackStatus == .stopped) else { 166 | return 167 | } 168 | 169 | guard player != nil else { 170 | return 171 | } 172 | 173 | if let playerViewController = self.presentedViewController as? AVPlayerViewController { 174 | playerViewController.player?.play() 175 | }else { 176 | if let controller = constructPlayerViewController(player: player) { 177 | // Modally present the player and call the player's play() method when complete. 178 | self.present(controller, animated: true) { 179 | self.playbackStatus = .playing 180 | controller.player?.play() 181 | } 182 | } 183 | } 184 | } 185 | 186 | func constructPlayerViewController(player: AVQueuePlayer?) -> AVPlayerViewController? { 187 | guard player != nil else { 188 | return nil 189 | } 190 | 191 | let controller = AVPlayerViewController() 192 | controller.showsPlaybackControls = false 193 | controller.delegate = self 194 | controller.player = player 195 | return controller 196 | } 197 | 198 | // MARK: 199 | // MARK: AVPlayer notifications 200 | 201 | func registerNotifications() -> Void { 202 | NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd(notification:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem) 203 | NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(notification:)), name: .UIApplicationDidBecomeActive, object: nil) 204 | } 205 | 206 | func deregisterNotifications() -> Void { 207 | NotificationCenter.default.removeObserver(self) 208 | } 209 | 210 | func playerItemDidReachEnd(notification: NSNotification) -> Void { 211 | 212 | guard notification.object as? AVPlayerItem == self.player?.currentItem else { 213 | return 214 | } 215 | 216 | guard let asset = self.player?.currentItem?.asset as? AVURLAsset else { 217 | return 218 | } 219 | 220 | if asset.isDownloaded() { 221 | // Asset was already downloaded. We play it again. 222 | self.player?.playNextItem() 223 | return 224 | } 225 | 226 | if asset.isExportable == false { 227 | self.player?.playNextItem() 228 | return 229 | } 230 | 231 | let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) 232 | 233 | exporter?.outputURL = asset.downloadPath(create: true) 234 | exporter?.outputFileType = AVFileTypeQuickTimeMovie 235 | exporter?.exportAsynchronously(completionHandler: { 236 | DispatchQueue.main.async { 237 | self.player?.playNextItem() 238 | return 239 | } 240 | }) 241 | } 242 | 243 | func applicationDidBecomeActive(notification: Notification) -> Void { 244 | self.resumePlayer(player: self.player) 245 | } 246 | 247 | // MARK: 248 | // MARK: Spinner 249 | 250 | func showSpinner(inView parentView: UIView) -> Void { 251 | spinner.center = parentView.center 252 | parentView.addSubview(spinner) 253 | spinner.startAnimating() 254 | } 255 | 256 | func hideSpinner() -> Void { 257 | spinner.stopAnimating() 258 | spinner.removeFromSuperview() 259 | } 260 | 261 | func assets(forMediaURLs mediaURLs: [String]) -> [AVURLAsset] { 262 | var assetArray = [AVURLAsset]() 263 | 264 | for media in urlArray { 265 | guard let url = URL.init(string: media) else { 266 | continue 267 | } 268 | 269 | var asset = AVURLAsset.init(url: url, options: [AVURLAssetAllowsCellularAccessKey : false]) 270 | if asset.isDownloaded() { 271 | // Asset was already downloaded before. So we recreate it from the local URL. 272 | if let localAssetURL = asset.downloadPath() { 273 | asset = AVURLAsset.init(url: localAssetURL) 274 | } 275 | }else { 276 | // Asset isn't downloaded yet. Set its resource loader so that we can export it later when its finished. 277 | asset.resourceLoader.setDelegate(self, queue: DispatchQueue.main) 278 | } 279 | assetArray.append(asset) 280 | } 281 | 282 | return assetArray 283 | } 284 | 285 | func playerItemArrayCollection(forAsset asset: [AVURLAsset]) -> [AVPlayerItem]{ 286 | var playerItemArray = [AVPlayerItem]() 287 | for mediaAsset in asset{ 288 | let playerItemTemp = AVPlayerItem.init(asset: mediaAsset) 289 | playerItemArray.append(playerItemTemp) 290 | } 291 | return playerItemArray 292 | } 293 | 294 | func isLastItemPlayed(asset: AVURLAsset) -> Bool { 295 | let lastURLOfMedia : String = self.urlArray.last! 296 | if(asset.url.absoluteString.range(of: lastURLOfMedia) != nil){ 297 | return true 298 | } 299 | else{ 300 | return false 301 | } 302 | } 303 | 304 | 305 | } 306 | 307 | // MARK: 308 | // MARK: Extensions 309 | 310 | extension AVQueuePlayer { 311 | func playNextItem(){ 312 | let currentItem = self.currentItem 313 | self.advanceToNextItem() 314 | 315 | if let item = currentItem { 316 | item.seek(to: kCMTimeZero) 317 | if self.canInsert(item, after: nil){ 318 | self.insert(item, after: nil) 319 | } 320 | } 321 | } 322 | } 323 | extension ViewController : AVAssetResourceLoaderDelegate { 324 | // We don't really have anything to do here. 325 | } 326 | 327 | extension ViewController : AVPlayerViewControllerDelegate { 328 | func playerViewController(_ playerViewController: AVPlayerViewController, shouldPresent proposal: AVContentProposal, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) { 329 | self.present(playerViewController, animated: true) { 330 | completionHandler(true) 331 | } 332 | } 333 | } 334 | 335 | extension AVPlayer { 336 | func restart() -> Void { 337 | self.pause() 338 | self.seek(to: kCMTimeZero) 339 | self.play() 340 | } 341 | } 342 | 343 | extension AVURLAsset { 344 | 345 | func isDownloaded() -> Bool { 346 | 347 | // First check if media is present at asset's URL. This could be the case if asset was created locally. 348 | let mediaExists = FileManager.init().fileExists(atPath: self.url.path) 349 | if mediaExists == false { 350 | // Media is not present at asset's URL. Derive the download path to check if its present there instead. 351 | if let downloadedAssetURL = self.downloadPath() { 352 | let mediaExists = FileManager.init().fileExists(atPath: downloadedAssetURL.path) 353 | return mediaExists 354 | } 355 | }else { 356 | // Media is present at asset's URL. This means asset was created out of the locally stored media. Thus, it is already downloaded. 357 | return true 358 | } 359 | 360 | return false 361 | } 362 | 363 | func downloadPath(create: Bool = false) -> URL? { 364 | 365 | let urlData = self.url.absoluteString.data(using: .utf8) 366 | let base64EncodedString = urlData?.base64EncodedString() 367 | 368 | guard let urlDirectory = base64EncodedString else { 369 | return nil 370 | } 371 | 372 | guard let documentsDirectory: URL = FileManager.init().urls(for: FileManager.SearchPathDirectory.cachesDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).last else { 373 | return nil 374 | } 375 | 376 | let directoryURL = documentsDirectory.appendingPathComponent(urlDirectory) 377 | 378 | if create { 379 | do { 380 | try FileManager.init().createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) 381 | }catch let error as NSError { 382 | print("Failed to create asset’s download path with error: \(error.localizedDescription)") 383 | } 384 | } 385 | let fileExtension = ".mov" 386 | let filename = "\(urlDirectory)\(fileExtension)" 387 | let mediaURL = directoryURL.appendingPathComponent(filename) 388 | return mediaURL 389 | } 390 | } 391 | 392 | extension String { 393 | func sha512() -> String? { 394 | if let stringData = self.data(using: String.Encoding.utf8) { 395 | if let hash = stringData.sha512() { 396 | return hash.base64EncodedString() 397 | } 398 | } 399 | return nil 400 | } 401 | } 402 | 403 | extension Data { 404 | func sha512() -> Data? { 405 | 406 | var hash = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH)) 407 | self.withUnsafeBytes { 408 | _ = CC_SHA512($0, CC_LONG(self.count), &hash) 409 | } 410 | return Data(bytes: hash) 411 | 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /Replay/Replay.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 01AB047F1F4F9F27007A4985 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 44A51DF51F434820008A1C22 /* Assets.xcassets */; }; 11 | 44A51DEF1F434820008A1C22 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A51DEE1F434820008A1C22 /* AppDelegate.swift */; }; 12 | 44A51DF11F434820008A1C22 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A51DF01F434820008A1C22 /* ViewController.swift */; }; 13 | 44A51DF41F434820008A1C22 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 44A51DF21F434820008A1C22 /* Main.storyboard */; }; 14 | 44A51E011F434820008A1C22 /* ReplayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A51E001F434820008A1C22 /* ReplayTests.swift */; }; 15 | 44A51E0C1F434820008A1C22 /* ReplayUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A51E0B1F434820008A1C22 /* ReplayUITests.swift */; }; 16 | 44A51E1B1F4348BC008A1C22 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 44A51E1A1F4348BC008A1C22 /* AVFoundation.framework */; }; 17 | 44A51E1D1F4348C2008A1C22 /* AVKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 44A51E1C1F4348C2008A1C22 /* AVKit.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 44A51DFD1F434820008A1C22 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 44A51DE31F434820008A1C22 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 44A51DEA1F434820008A1C22; 26 | remoteInfo = Replay; 27 | }; 28 | 44A51E081F434820008A1C22 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 44A51DE31F434820008A1C22 /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 44A51DEA1F434820008A1C22; 33 | remoteInfo = Replay; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 44A51DEB1F434820008A1C22 /* Replay.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Replay.app; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 44A51DEE1F434820008A1C22 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 40 | 44A51DF01F434820008A1C22 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 41 | 44A51DF31F434820008A1C22 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 44A51DF51F434820008A1C22 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 44A51DF71F434820008A1C22 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 44A51DFC1F434820008A1C22 /* ReplayTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReplayTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 44A51E001F434820008A1C22 /* ReplayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplayTests.swift; sourceTree = ""; }; 46 | 44A51E021F434820008A1C22 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | 44A51E071F434820008A1C22 /* ReplayUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReplayUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 44A51E0B1F434820008A1C22 /* ReplayUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplayUITests.swift; sourceTree = ""; }; 49 | 44A51E0D1F434820008A1C22 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 44A51E1A1F4348BC008A1C22 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 51 | 44A51E1C1F4348C2008A1C22 /* AVKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVKit.framework; path = System/Library/Frameworks/AVKit.framework; sourceTree = SDKROOT; }; 52 | 44A51E1E1F4349DD008A1C22 /* BridgingHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BridgingHeader.h; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 44A51DE81F434820008A1C22 /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 44A51E1D1F4348C2008A1C22 /* AVKit.framework in Frameworks */, 61 | 44A51E1B1F4348BC008A1C22 /* AVFoundation.framework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | 44A51DF91F434820008A1C22 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | 44A51E041F434820008A1C22 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | 44A51DE21F434820008A1C22 = { 83 | isa = PBXGroup; 84 | children = ( 85 | 44A51DED1F434820008A1C22 /* Replay */, 86 | 44A51DFF1F434820008A1C22 /* ReplayTests */, 87 | 44A51E0A1F434820008A1C22 /* ReplayUITests */, 88 | 44A51DEC1F434820008A1C22 /* Products */, 89 | 44A51E191F4348BC008A1C22 /* Frameworks */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | 44A51DEC1F434820008A1C22 /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 44A51DEB1F434820008A1C22 /* Replay.app */, 97 | 44A51DFC1F434820008A1C22 /* ReplayTests.xctest */, 98 | 44A51E071F434820008A1C22 /* ReplayUITests.xctest */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | 44A51DED1F434820008A1C22 /* Replay */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 44A51DEE1F434820008A1C22 /* AppDelegate.swift */, 107 | 44A51DF01F434820008A1C22 /* ViewController.swift */, 108 | 44A51DF21F434820008A1C22 /* Main.storyboard */, 109 | 44A51DF51F434820008A1C22 /* Assets.xcassets */, 110 | 44A51E1E1F4349DD008A1C22 /* BridgingHeader.h */, 111 | 44A51DF71F434820008A1C22 /* Info.plist */, 112 | ); 113 | path = Replay; 114 | sourceTree = ""; 115 | }; 116 | 44A51DFF1F434820008A1C22 /* ReplayTests */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 44A51E001F434820008A1C22 /* ReplayTests.swift */, 120 | 44A51E021F434820008A1C22 /* Info.plist */, 121 | ); 122 | path = ReplayTests; 123 | sourceTree = ""; 124 | }; 125 | 44A51E0A1F434820008A1C22 /* ReplayUITests */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 44A51E0B1F434820008A1C22 /* ReplayUITests.swift */, 129 | 44A51E0D1F434820008A1C22 /* Info.plist */, 130 | ); 131 | path = ReplayUITests; 132 | sourceTree = ""; 133 | }; 134 | 44A51E191F4348BC008A1C22 /* Frameworks */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 44A51E1C1F4348C2008A1C22 /* AVKit.framework */, 138 | 44A51E1A1F4348BC008A1C22 /* AVFoundation.framework */, 139 | ); 140 | name = Frameworks; 141 | sourceTree = ""; 142 | }; 143 | /* End PBXGroup section */ 144 | 145 | /* Begin PBXNativeTarget section */ 146 | 44A51DEA1F434820008A1C22 /* Replay */ = { 147 | isa = PBXNativeTarget; 148 | buildConfigurationList = 44A51E101F434820008A1C22 /* Build configuration list for PBXNativeTarget "Replay" */; 149 | buildPhases = ( 150 | 44A51DE71F434820008A1C22 /* Sources */, 151 | 44A51DE81F434820008A1C22 /* Frameworks */, 152 | 44A51DE91F434820008A1C22 /* Resources */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Replay; 159 | productName = Replay; 160 | productReference = 44A51DEB1F434820008A1C22 /* Replay.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | 44A51DFB1F434820008A1C22 /* ReplayTests */ = { 164 | isa = PBXNativeTarget; 165 | buildConfigurationList = 44A51E131F434820008A1C22 /* Build configuration list for PBXNativeTarget "ReplayTests" */; 166 | buildPhases = ( 167 | 44A51DF81F434820008A1C22 /* Sources */, 168 | 44A51DF91F434820008A1C22 /* Frameworks */, 169 | 44A51DFA1F434820008A1C22 /* Resources */, 170 | ); 171 | buildRules = ( 172 | ); 173 | dependencies = ( 174 | 44A51DFE1F434820008A1C22 /* PBXTargetDependency */, 175 | ); 176 | name = ReplayTests; 177 | productName = ReplayTests; 178 | productReference = 44A51DFC1F434820008A1C22 /* ReplayTests.xctest */; 179 | productType = "com.apple.product-type.bundle.unit-test"; 180 | }; 181 | 44A51E061F434820008A1C22 /* ReplayUITests */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = 44A51E161F434820008A1C22 /* Build configuration list for PBXNativeTarget "ReplayUITests" */; 184 | buildPhases = ( 185 | 44A51E031F434820008A1C22 /* Sources */, 186 | 44A51E041F434820008A1C22 /* Frameworks */, 187 | 44A51E051F434820008A1C22 /* Resources */, 188 | ); 189 | buildRules = ( 190 | ); 191 | dependencies = ( 192 | 44A51E091F434820008A1C22 /* PBXTargetDependency */, 193 | ); 194 | name = ReplayUITests; 195 | productName = ReplayUITests; 196 | productReference = 44A51E071F434820008A1C22 /* ReplayUITests.xctest */; 197 | productType = "com.apple.product-type.bundle.ui-testing"; 198 | }; 199 | /* End PBXNativeTarget section */ 200 | 201 | /* Begin PBXProject section */ 202 | 44A51DE31F434820008A1C22 /* Project object */ = { 203 | isa = PBXProject; 204 | attributes = { 205 | LastSwiftUpdateCheck = 0830; 206 | LastUpgradeCheck = 0830; 207 | ORGANIZATIONNAME = Omnissa; 208 | TargetAttributes = { 209 | 44A51DEA1F434820008A1C22 = { 210 | CreatedOnToolsVersion = 8.3.3; 211 | DevelopmentTeam = 7SUQPKEF96; 212 | ProvisioningStyle = Automatic; 213 | }; 214 | 44A51DFB1F434820008A1C22 = { 215 | CreatedOnToolsVersion = 8.3.3; 216 | DevelopmentTeam = S2ZMFGQM93; 217 | ProvisioningStyle = Automatic; 218 | TestTargetID = 44A51DEA1F434820008A1C22; 219 | }; 220 | 44A51E061F434820008A1C22 = { 221 | CreatedOnToolsVersion = 8.3.3; 222 | DevelopmentTeam = S2ZMFGQM93; 223 | ProvisioningStyle = Automatic; 224 | TestTargetID = 44A51DEA1F434820008A1C22; 225 | }; 226 | }; 227 | }; 228 | buildConfigurationList = 44A51DE61F434820008A1C22 /* Build configuration list for PBXProject "Replay" */; 229 | compatibilityVersion = "Xcode 3.2"; 230 | developmentRegion = English; 231 | hasScannedForEncodings = 0; 232 | knownRegions = ( 233 | en, 234 | Base, 235 | ); 236 | mainGroup = 44A51DE21F434820008A1C22; 237 | productRefGroup = 44A51DEC1F434820008A1C22 /* Products */; 238 | projectDirPath = ""; 239 | projectRoot = ""; 240 | targets = ( 241 | 44A51DEA1F434820008A1C22 /* Replay */, 242 | 44A51DFB1F434820008A1C22 /* ReplayTests */, 243 | 44A51E061F434820008A1C22 /* ReplayUITests */, 244 | ); 245 | }; 246 | /* End PBXProject section */ 247 | 248 | /* Begin PBXResourcesBuildPhase section */ 249 | 44A51DE91F434820008A1C22 /* Resources */ = { 250 | isa = PBXResourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | 01AB047F1F4F9F27007A4985 /* Assets.xcassets in Resources */, 254 | 44A51DF41F434820008A1C22 /* Main.storyboard in Resources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | 44A51DFA1F434820008A1C22 /* Resources */ = { 259 | isa = PBXResourcesBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | 44A51E051F434820008A1C22 /* Resources */ = { 266 | isa = PBXResourcesBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | }; 272 | /* End PBXResourcesBuildPhase section */ 273 | 274 | /* Begin PBXSourcesBuildPhase section */ 275 | 44A51DE71F434820008A1C22 /* Sources */ = { 276 | isa = PBXSourcesBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | 44A51DF11F434820008A1C22 /* ViewController.swift in Sources */, 280 | 44A51DEF1F434820008A1C22 /* AppDelegate.swift in Sources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | 44A51DF81F434820008A1C22 /* Sources */ = { 285 | isa = PBXSourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 44A51E011F434820008A1C22 /* ReplayTests.swift in Sources */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | 44A51E031F434820008A1C22 /* Sources */ = { 293 | isa = PBXSourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | 44A51E0C1F434820008A1C22 /* ReplayUITests.swift in Sources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXSourcesBuildPhase section */ 301 | 302 | /* Begin PBXTargetDependency section */ 303 | 44A51DFE1F434820008A1C22 /* PBXTargetDependency */ = { 304 | isa = PBXTargetDependency; 305 | target = 44A51DEA1F434820008A1C22 /* Replay */; 306 | targetProxy = 44A51DFD1F434820008A1C22 /* PBXContainerItemProxy */; 307 | }; 308 | 44A51E091F434820008A1C22 /* PBXTargetDependency */ = { 309 | isa = PBXTargetDependency; 310 | target = 44A51DEA1F434820008A1C22 /* Replay */; 311 | targetProxy = 44A51E081F434820008A1C22 /* PBXContainerItemProxy */; 312 | }; 313 | /* End PBXTargetDependency section */ 314 | 315 | /* Begin PBXVariantGroup section */ 316 | 44A51DF21F434820008A1C22 /* Main.storyboard */ = { 317 | isa = PBXVariantGroup; 318 | children = ( 319 | 44A51DF31F434820008A1C22 /* Base */, 320 | ); 321 | name = Main.storyboard; 322 | sourceTree = ""; 323 | }; 324 | /* End PBXVariantGroup section */ 325 | 326 | /* Begin XCBuildConfiguration section */ 327 | 44A51E0E1F434820008A1C22 /* Debug */ = { 328 | isa = XCBuildConfiguration; 329 | buildSettings = { 330 | ALWAYS_SEARCH_USER_PATHS = NO; 331 | CLANG_ANALYZER_NONNULL = YES; 332 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 333 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 334 | CLANG_CXX_LIBRARY = "libc++"; 335 | CLANG_ENABLE_MODULES = YES; 336 | CLANG_ENABLE_OBJC_ARC = YES; 337 | CLANG_WARN_BOOL_CONVERSION = YES; 338 | CLANG_WARN_CONSTANT_CONVERSION = YES; 339 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 340 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 341 | CLANG_WARN_EMPTY_BODY = YES; 342 | CLANG_WARN_ENUM_CONVERSION = YES; 343 | CLANG_WARN_INFINITE_RECURSION = YES; 344 | CLANG_WARN_INT_CONVERSION = YES; 345 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 346 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 347 | CLANG_WARN_UNREACHABLE_CODE = YES; 348 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 349 | COPY_PHASE_STRIP = NO; 350 | DEBUG_INFORMATION_FORMAT = dwarf; 351 | ENABLE_STRICT_OBJC_MSGSEND = YES; 352 | ENABLE_TESTABILITY = YES; 353 | GCC_C_LANGUAGE_STANDARD = gnu99; 354 | GCC_DYNAMIC_NO_PIC = NO; 355 | GCC_NO_COMMON_BLOCKS = YES; 356 | GCC_OPTIMIZATION_LEVEL = 0; 357 | GCC_PREPROCESSOR_DEFINITIONS = ( 358 | "DEBUG=1", 359 | "$(inherited)", 360 | ); 361 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 362 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 363 | GCC_WARN_UNDECLARED_SELECTOR = YES; 364 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 365 | GCC_WARN_UNUSED_FUNCTION = YES; 366 | GCC_WARN_UNUSED_VARIABLE = YES; 367 | MTL_ENABLE_DEBUG_INFO = YES; 368 | ONLY_ACTIVE_ARCH = YES; 369 | SDKROOT = appletvos; 370 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 371 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 372 | TARGETED_DEVICE_FAMILY = 3; 373 | TVOS_DEPLOYMENT_TARGET = 10.2; 374 | }; 375 | name = Debug; 376 | }; 377 | 44A51E0F1F434820008A1C22 /* Release */ = { 378 | isa = XCBuildConfiguration; 379 | buildSettings = { 380 | ALWAYS_SEARCH_USER_PATHS = NO; 381 | CLANG_ANALYZER_NONNULL = YES; 382 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 383 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 384 | CLANG_CXX_LIBRARY = "libc++"; 385 | CLANG_ENABLE_MODULES = YES; 386 | CLANG_ENABLE_OBJC_ARC = YES; 387 | CLANG_WARN_BOOL_CONVERSION = YES; 388 | CLANG_WARN_CONSTANT_CONVERSION = YES; 389 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 390 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 391 | CLANG_WARN_EMPTY_BODY = YES; 392 | CLANG_WARN_ENUM_CONVERSION = YES; 393 | CLANG_WARN_INFINITE_RECURSION = YES; 394 | CLANG_WARN_INT_CONVERSION = YES; 395 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 396 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 397 | CLANG_WARN_UNREACHABLE_CODE = YES; 398 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 399 | COPY_PHASE_STRIP = NO; 400 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 401 | ENABLE_NS_ASSERTIONS = NO; 402 | ENABLE_STRICT_OBJC_MSGSEND = YES; 403 | GCC_C_LANGUAGE_STANDARD = gnu99; 404 | GCC_NO_COMMON_BLOCKS = YES; 405 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 406 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 407 | GCC_WARN_UNDECLARED_SELECTOR = YES; 408 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 409 | GCC_WARN_UNUSED_FUNCTION = YES; 410 | GCC_WARN_UNUSED_VARIABLE = YES; 411 | MTL_ENABLE_DEBUG_INFO = NO; 412 | SDKROOT = appletvos; 413 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 414 | TARGETED_DEVICE_FAMILY = 3; 415 | TVOS_DEPLOYMENT_TARGET = 10.2; 416 | VALIDATE_PRODUCT = YES; 417 | }; 418 | name = Release; 419 | }; 420 | 44A51E111F434820008A1C22 /* Debug */ = { 421 | isa = XCBuildConfiguration; 422 | buildSettings = { 423 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; 424 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 425 | DEVELOPMENT_TEAM = 7SUQPKEF96; 426 | INFOPLIST_FILE = Replay/Info.plist; 427 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 428 | PRODUCT_BUNDLE_IDENTIFIER = "com.air-watch.Replay"; 429 | PRODUCT_NAME = "$(TARGET_NAME)"; 430 | SWIFT_OBJC_BRIDGING_HEADER = Replay/BridgingHeader.h; 431 | SWIFT_VERSION = 3.0; 432 | }; 433 | name = Debug; 434 | }; 435 | 44A51E121F434820008A1C22 /* Release */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; 439 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 440 | DEVELOPMENT_TEAM = 7SUQPKEF96; 441 | INFOPLIST_FILE = Replay/Info.plist; 442 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 443 | PRODUCT_BUNDLE_IDENTIFIER = "com.air-watch.Replay"; 444 | PRODUCT_NAME = "$(TARGET_NAME)"; 445 | SWIFT_OBJC_BRIDGING_HEADER = Replay/BridgingHeader.h; 446 | SWIFT_VERSION = 3.0; 447 | }; 448 | name = Release; 449 | }; 450 | 44A51E141F434820008A1C22 /* Debug */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 454 | BUNDLE_LOADER = "$(TEST_HOST)"; 455 | DEVELOPMENT_TEAM = S2ZMFGQM93; 456 | INFOPLIST_FILE = ReplayTests/Info.plist; 457 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 458 | PRODUCT_BUNDLE_IDENTIFIER = "com.air-watch.ReplayTests"; 459 | PRODUCT_NAME = "$(TARGET_NAME)"; 460 | SWIFT_VERSION = 3.0; 461 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Replay.app/Replay"; 462 | }; 463 | name = Debug; 464 | }; 465 | 44A51E151F434820008A1C22 /* Release */ = { 466 | isa = XCBuildConfiguration; 467 | buildSettings = { 468 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 469 | BUNDLE_LOADER = "$(TEST_HOST)"; 470 | DEVELOPMENT_TEAM = S2ZMFGQM93; 471 | INFOPLIST_FILE = ReplayTests/Info.plist; 472 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 473 | PRODUCT_BUNDLE_IDENTIFIER = "com.air-watch.ReplayTests"; 474 | PRODUCT_NAME = "$(TARGET_NAME)"; 475 | SWIFT_VERSION = 3.0; 476 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Replay.app/Replay"; 477 | }; 478 | name = Release; 479 | }; 480 | 44A51E171F434820008A1C22 /* Debug */ = { 481 | isa = XCBuildConfiguration; 482 | buildSettings = { 483 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 484 | DEVELOPMENT_TEAM = S2ZMFGQM93; 485 | INFOPLIST_FILE = ReplayUITests/Info.plist; 486 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 487 | PRODUCT_BUNDLE_IDENTIFIER = "com.air-watch.ReplayUITests"; 488 | PRODUCT_NAME = "$(TARGET_NAME)"; 489 | SWIFT_VERSION = 3.0; 490 | TEST_TARGET_NAME = Replay; 491 | }; 492 | name = Debug; 493 | }; 494 | 44A51E181F434820008A1C22 /* Release */ = { 495 | isa = XCBuildConfiguration; 496 | buildSettings = { 497 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 498 | DEVELOPMENT_TEAM = S2ZMFGQM93; 499 | INFOPLIST_FILE = ReplayUITests/Info.plist; 500 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 501 | PRODUCT_BUNDLE_IDENTIFIER = "com.air-watch.ReplayUITests"; 502 | PRODUCT_NAME = "$(TARGET_NAME)"; 503 | SWIFT_VERSION = 3.0; 504 | TEST_TARGET_NAME = Replay; 505 | }; 506 | name = Release; 507 | }; 508 | /* End XCBuildConfiguration section */ 509 | 510 | /* Begin XCConfigurationList section */ 511 | 44A51DE61F434820008A1C22 /* Build configuration list for PBXProject "Replay" */ = { 512 | isa = XCConfigurationList; 513 | buildConfigurations = ( 514 | 44A51E0E1F434820008A1C22 /* Debug */, 515 | 44A51E0F1F434820008A1C22 /* Release */, 516 | ); 517 | defaultConfigurationIsVisible = 0; 518 | defaultConfigurationName = Release; 519 | }; 520 | 44A51E101F434820008A1C22 /* Build configuration list for PBXNativeTarget "Replay" */ = { 521 | isa = XCConfigurationList; 522 | buildConfigurations = ( 523 | 44A51E111F434820008A1C22 /* Debug */, 524 | 44A51E121F434820008A1C22 /* Release */, 525 | ); 526 | defaultConfigurationIsVisible = 0; 527 | defaultConfigurationName = Release; 528 | }; 529 | 44A51E131F434820008A1C22 /* Build configuration list for PBXNativeTarget "ReplayTests" */ = { 530 | isa = XCConfigurationList; 531 | buildConfigurations = ( 532 | 44A51E141F434820008A1C22 /* Debug */, 533 | 44A51E151F434820008A1C22 /* Release */, 534 | ); 535 | defaultConfigurationIsVisible = 0; 536 | defaultConfigurationName = Release; 537 | }; 538 | 44A51E161F434820008A1C22 /* Build configuration list for PBXNativeTarget "ReplayUITests" */ = { 539 | isa = XCConfigurationList; 540 | buildConfigurations = ( 541 | 44A51E171F434820008A1C22 /* Debug */, 542 | 44A51E181F434820008A1C22 /* Release */, 543 | ); 544 | defaultConfigurationIsVisible = 0; 545 | defaultConfigurationName = Release; 546 | }; 547 | /* End XCConfigurationList section */ 548 | }; 549 | rootObject = 44A51DE31F434820008A1C22 /* Project object */; 550 | } 551 | --------------------------------------------------------------------------------