├── .DS_Store ├── promo.jpg ├── apnspushsimulate ├── Assets.xcassets │ ├── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── apnspushsimulate.entitlements ├── SceneDelegate.swift ├── ViewController.swift ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── AppDelegate.swift └── Info.plist ├── Pods ├── Target Support Files │ ├── Cosmos │ │ ├── Cosmos.modulemap │ │ ├── Cosmos-dummy.m │ │ ├── Cosmos-prefix.pch │ │ ├── Cosmos-umbrella.h │ │ ├── Cosmos.xcconfig │ │ └── Cosmos-Info.plist │ ├── Pods-test │ │ ├── Pods-test.modulemap │ │ ├── Pods-test-dummy.m │ │ ├── Pods-test-umbrella.h │ │ ├── Pods-test-Info.plist │ │ ├── Pods-test.debug.xcconfig │ │ ├── Pods-test.release.xcconfig │ │ ├── Pods-test-acknowledgements.markdown │ │ └── Pods-test-acknowledgements.plist │ ├── XCDYouTubeKit │ │ ├── XCDYouTubeKit.modulemap │ │ ├── XCDYouTubeKit-dummy.m │ │ ├── XCDYouTubeKit-prefix.pch │ │ ├── XCDYouTubeKit.xcconfig │ │ ├── XCDYouTubeKit-umbrella.h │ │ └── XCDYouTubeKit-Info.plist │ └── Pods-apnspushsimulate │ │ ├── Pods-apnspushsimulate.modulemap │ │ ├── Pods-apnspushsimulate-dummy.m │ │ ├── Pods-apnspushsimulate-frameworks-Debug-output-files.xcfilelist │ │ ├── Pods-apnspushsimulate-frameworks-Release-output-files.xcfilelist │ │ ├── Pods-apnspushsimulate-frameworks-Debug-input-files.xcfilelist │ │ ├── Pods-apnspushsimulate-frameworks-Release-input-files.xcfilelist │ │ ├── Pods-apnspushsimulate-umbrella.h │ │ ├── Pods-apnspushsimulate-Info.plist │ │ ├── Pods-apnspushsimulate.debug.xcconfig │ │ ├── Pods-apnspushsimulate.release.xcconfig │ │ ├── Pods-apnspushsimulate-acknowledgements.markdown │ │ ├── Pods-apnspushsimulate-acknowledgements.plist │ │ └── Pods-apnspushsimulate-frameworks.sh ├── Cosmos │ ├── Cosmos │ │ ├── Helpers │ │ │ ├── RightToLeft.swift │ │ │ └── CosmosTouchTarget.swift │ │ ├── StarFillMode.swift │ │ ├── CosmosSize.swift │ │ ├── CosmosText.swift │ │ ├── CosmosLayerHelper.swift │ │ ├── CosmosTouch.swift │ │ ├── CosmosLocalizedRating.swift │ │ ├── CosmosAccessibility.swift │ │ ├── CosmosDefaultSettings.swift │ │ ├── CosmosRating.swift │ │ ├── CosmosSettings.swift │ │ ├── CosmosLayers.swift │ │ └── StarLayer.swift │ ├── LICENSE │ └── README.md ├── Manifest.lock ├── XCDYouTubeKit │ ├── XCDYouTubeKit │ │ ├── XCDYouTubePlayerScript.h │ │ ├── XCDYouTubeDashManifestXML.h │ │ ├── XCDYouTubeKit.h │ │ ├── XCDYouTubeOperation.h │ │ ├── XCDYouTubeVideoWebpage.h │ │ ├── XCDYouTubeVideo+Private.h │ │ ├── XCDYouTubeLogger+Private.h │ │ ├── XCDYouTubeError.h │ │ ├── XCDYouTubeLogger.m │ │ ├── XCDYouTubeClient.m │ │ ├── XCDYouTubeDashManifestXML.m │ │ ├── XCDYouTubeVideoOperation.h │ │ ├── XCDYouTubeLogger.h │ │ ├── XCDYouTubeVideoPlayerViewController.h │ │ ├── XCDYouTubeVideo.h │ │ ├── XCDYouTubeVideoWebpage.m │ │ ├── XCDYouTubePlayerScript.m │ │ ├── XCDYouTubeClient.h │ │ └── XCDYouTubeVideoPlayerViewController.m │ ├── LICENSE │ └── README.md └── Pods.xcodeproj │ └── xcuserdata │ └── alfianlosari.xcuserdatad │ └── xcschemes │ ├── xcschememanagement.plist │ ├── Pods-test.xcscheme │ ├── Pods-apnspushsimulate.xcscheme │ ├── Cosmos.xcscheme │ └── XCDYouTubeKit.xcscheme ├── apnspushsimulate.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── alfianlosari.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── alfianlosari.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist └── xcshareddata │ └── xcschemes │ ├── apnspushsimulate.xcscheme │ └── test.xcscheme ├── apnspushsimulate.xcworkspace ├── xcuserdata │ └── alfianlosari.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── UserInterfaceState.xcuserstate ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── f9.apns ├── ff7r.apns ├── Podfile.lock ├── Podfile ├── README.md └── test ├── Info.plist └── NotificationViewController.swift /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfianlosari/iOSCustomInteractivePushNotification/HEAD/.DS_Store -------------------------------------------------------------------------------- /promo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfianlosari/iOSCustomInteractivePushNotification/HEAD/promo.jpg -------------------------------------------------------------------------------- /apnspushsimulate/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Cosmos/Cosmos.modulemap: -------------------------------------------------------------------------------- 1 | framework module Cosmos { 2 | umbrella header "Cosmos-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Cosmos/Cosmos-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Cosmos : NSObject 3 | @end 4 | @implementation PodsDummy_Cosmos 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-test/Pods-test.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_test { 2 | umbrella header "Pods-test-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-test/Pods-test-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_test : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_test 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit.modulemap: -------------------------------------------------------------------------------- 1 | framework module XCDYouTubeKit { 2 | umbrella header "XCDYouTubeKit-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_XCDYouTubeKit : NSObject 3 | @end 4 | @implementation PodsDummy_XCDYouTubeKit 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_apnspushsimulate { 2 | umbrella header "Pods-apnspushsimulate-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_apnspushsimulate : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_apnspushsimulate 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cosmos.framework 2 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/XCDYouTubeKit.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cosmos.framework 2 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/XCDYouTubeKit.framework -------------------------------------------------------------------------------- /apnspushsimulate.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /apnspushsimulate.xcodeproj/xcuserdata/alfianlosari.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /apnspushsimulate.xcworkspace/xcuserdata/alfianlosari.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /apnspushsimulate.xcworkspace/xcuserdata/alfianlosari.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfianlosari/iOSCustomInteractivePushNotification/HEAD/apnspushsimulate.xcworkspace/xcuserdata/alfianlosari.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Pods/Target Support Files/Cosmos/Cosmos-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /apnspushsimulate.xcodeproj/project.xcworkspace/xcuserdata/alfianlosari.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfianlosari/iOSCustomInteractivePushNotification/HEAD/apnspushsimulate.xcodeproj/project.xcworkspace/xcuserdata/alfianlosari.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Cosmos/Cosmos.framework 3 | ${BUILT_PRODUCTS_DIR}/XCDYouTubeKit/XCDYouTubeKit.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/Cosmos/Cosmos.framework 3 | ${BUILT_PRODUCTS_DIR}/XCDYouTubeKit/XCDYouTubeKit.framework -------------------------------------------------------------------------------- /Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /apnspushsimulate/apnspushsimulate.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | 8 | 9 | -------------------------------------------------------------------------------- /apnspushsimulate.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /apnspushsimulate.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /apnspushsimulate.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Cosmos/Cosmos-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double CosmosVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char CosmosVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-test/Pods-test-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_testVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_testVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/Helpers/RightToLeft.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** 4 | 5 | Helper functions for dealing with right-to-left languages. 6 | 7 | */ 8 | struct RightToLeft { 9 | static func isRightToLeft(_ view: UIView) -> Bool { 10 | if #available(iOS 9.0, *) { 11 | return UIView.userInterfaceLayoutDirection( 12 | for: view.semanticContentAttribute) == .rightToLeft 13 | } else { 14 | return false 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /f9.apns: -------------------------------------------------------------------------------- 1 | { 2 | "aps" : { 3 | "alert" : { 4 | "title" : "A new trailer has arrived for you", 5 | "body" : "Fast and Furious F9 Official Trailer" 6 | }, 7 | "category" : "testNotificationCategory", 8 | "sound": "bingbong.aiff", 9 | "badge": 3, 10 | }, 11 | "videoId" : "Kopyc23VfSw", 12 | "description": "Release date: 05-21-2020", 13 | "Simulator Target Bundle": "com.alfianlosari.apnspushsimulate" 14 | } -------------------------------------------------------------------------------- /ff7r.apns: -------------------------------------------------------------------------------- 1 | { 2 | "aps" : { 3 | "alert" : { 4 | "title" : "A new trailer has arrived for you", 5 | "body" : "Final Fantasy VII Remake Official Trailer" 6 | }, 7 | "category" : "testNotificationCategory", 8 | "sound": "bingbong.aiff", 9 | "badge": 3, 10 | }, 11 | "videoId" : "tEPb8uQ27BI", 12 | "description": "Release date: 04-10-2020", 13 | "Simulator Target Bundle": "com.alfianlosari.apnspushsimulate" 14 | } -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Cosmos (21.0.0) 3 | - XCDYouTubeKit (2.9.0) 4 | 5 | DEPENDENCIES: 6 | - Cosmos (~> 21.0) 7 | - XCDYouTubeKit (~> 2.9) 8 | 9 | SPEC REPOS: 10 | trunk: 11 | - Cosmos 12 | - XCDYouTubeKit 13 | 14 | SPEC CHECKSUMS: 15 | Cosmos: 3dfb6e4ba48bd48b8c9aa2a77048ae11ed114f96 16 | XCDYouTubeKit: 65b8c362ad9e8b69a7f6bba4e4fa3b26dc0c29f3 17 | 18 | PODFILE CHECKSUM: c265a8257f84a736330badc7223979a0d392995c 19 | 20 | COCOAPODS: 1.8.4 21 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Cosmos (21.0.0) 3 | - XCDYouTubeKit (2.9.0) 4 | 5 | DEPENDENCIES: 6 | - Cosmos (~> 21.0) 7 | - XCDYouTubeKit (~> 2.9) 8 | 9 | SPEC REPOS: 10 | trunk: 11 | - Cosmos 12 | - XCDYouTubeKit 13 | 14 | SPEC CHECKSUMS: 15 | Cosmos: 3dfb6e4ba48bd48b8c9aa2a77048ae11ed114f96 16 | XCDYouTubeKit: 65b8c362ad9e8b69a7f6bba4e4fa3b26dc0c29f3 17 | 18 | PODFILE CHECKSUM: c265a8257f84a736330badc7223979a0d392995c 19 | 20 | COCOAPODS: 1.8.4 21 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_apnspushsimulateVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_apnspushsimulateVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import 6 | 7 | __attribute__((visibility("hidden"))) 8 | @interface XCDYouTubePlayerScript : NSObject 9 | 10 | - (instancetype) initWithString:(NSString *)string customPatterns:(NSArray *)customPatterns; 11 | 12 | - (NSString *) unscrambleSignature:(NSString *)scrambledSignature; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h: -------------------------------------------------------------------------------- 1 | // 2 | // XCDYouTubeDashManifestXML.h 3 | // XCDYouTubeKit 4 | // 5 | // Created by Soneé John on 10/24/17. 6 | // Copyright © 2017 Cédric Luthi. All rights reserved. 7 | // 8 | 9 | #import 10 | __attribute__((visibility("hidden"))) 11 | @interface XCDYouTubeDashManifestXML : NSObject 12 | 13 | - (instancetype)initWithXMLString:(NSString *)XMLString; 14 | 15 | - (NSDictionary *)streamURLs; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import 6 | 7 | #import "XCDYouTubeClient.h" 8 | #import "XCDYouTubeError.h" 9 | #import "XCDYouTubeLogger.h" 10 | #import "XCDYouTubeOperation.h" 11 | #import "XCDYouTubeVideo.h" 12 | #import "XCDYouTubeVideoOperation.h" 13 | 14 | 15 | #if TARGET_OS_IOS || (!defined(TARGET_OS_IOS) && TARGET_OS_IPHONE) 16 | #import "XCDYouTubeVideoPlayerViewController.h" 17 | #endif 18 | -------------------------------------------------------------------------------- /apnspushsimulate/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // apnspushsimulate 4 | // 5 | // Created by Alfian Losari on 10/02/20. 6 | // Copyright © 2020 alfianlosari. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | guard let _ = (scene as? UIWindowScene) else { return } 17 | } 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'apnspushsimulate' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for apnspushsimulate 9 | pod "XCDYouTubeKit", "~> 2.9" 10 | pod 'Cosmos', '~> 21.0' 11 | 12 | end 13 | 14 | target 'test' do 15 | # Comment the next line if you don't want to use dynamic frameworks 16 | use_frameworks! 17 | 18 | # Pods for test 19 | pod "XCDYouTubeKit", "~> 2.9" 20 | pod 'Cosmos', '~> 21.0' 21 | 22 | end 23 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Cosmos/Cosmos.xcconfig: -------------------------------------------------------------------------------- 1 | APPLICATION_EXTENSION_API_ONLY = YES 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Cosmos 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Cosmos 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import 6 | 7 | /** 8 | * The `XCDYouTubeOperation` protocol is adopted by opaque objects returned by the `<-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]>` method. 9 | */ 10 | @protocol XCDYouTubeOperation 11 | 12 | /** 13 | * --------------- 14 | * @name Canceling 15 | * --------------- 16 | */ 17 | 18 | /** 19 | * Cancels the operation. If the operation is already finished, does nothing. 20 | */ 21 | - (void) cancel; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/StarFillMode.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /** 4 | 5 | Defines how the star is filled when the rating is not an integer number. For example, if rating is 4.6 and the fill more is Half, the star will appear to be half filled. 6 | 7 | */ 8 | public enum StarFillMode: Int { 9 | /// Show only fully filled stars. For example, fourth star will be empty for 3.2. 10 | case full = 0 11 | 12 | /// Show fully filled and half-filled stars. For example, fourth star will be half filled for 3.6. 13 | case half = 1 14 | 15 | /// Fill star according to decimal rating. For example, fourth star will be 20% filled for 3.2. 16 | case precise = 2 17 | } 18 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit.xcconfig: -------------------------------------------------------------------------------- 1 | APPLICATION_EXTENSION_API_ONLY = YES 2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | OTHER_LDFLAGS = $(inherited) -framework "JavaScriptCore" -framework "MediaPlayer" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/XCDYouTubeKit 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /apnspushsimulate/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // apnspushsimulate 4 | // 5 | // Created by Alfian Losari on 10/02/20. 6 | // Copyright © 2020 alfianlosari. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet weak var textLabel: UILabel! 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | textLabel.text = "Drag & Drop valid APNS file to the target simulator to begin (2 of sample APNS file is in the project directory). Make sure to accept the push notification permission and put the app in background before" 18 | } 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import 6 | 7 | __attribute__((visibility("hidden"))) 8 | @interface XCDYouTubeVideoWebpage : NSObject 9 | 10 | - (instancetype) initWithHTMLString:(NSString *)html; 11 | 12 | @property (nonatomic, readonly) NSDictionary *playerConfiguration; 13 | @property (nonatomic, readonly) NSDictionary *videoInfo; 14 | @property (nonatomic, readonly) NSString *sts; 15 | @property (nonatomic, readonly) NSURL *javaScriptPlayerURL; 16 | @property (nonatomic, readonly) BOOL isAgeRestricted; 17 | @property (nonatomic, readonly) NSSet *regionsAllowed; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "XCDYouTubeClient.h" 14 | #import "XCDYouTubeError.h" 15 | #import "XCDYouTubeKit.h" 16 | #import "XCDYouTubeLogger.h" 17 | #import "XCDYouTubeOperation.h" 18 | #import "XCDYouTubeVideo.h" 19 | #import "XCDYouTubeVideoOperation.h" 20 | #import "XCDYouTubeVideoPlayerViewController.h" 21 | 22 | FOUNDATION_EXPORT double XCDYouTubeKitVersionNumber; 23 | FOUNDATION_EXPORT const unsigned char XCDYouTubeKitVersionString[]; 24 | 25 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/CosmosSize.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** 4 | 5 | Helper class for calculating size for the cosmos view. 6 | 7 | */ 8 | class CosmosSize { 9 | /** 10 | 11 | Calculates the size of the cosmos view. It goes through all the star and text layers and makes size the view size is large enough to show all of them. 12 | 13 | */ 14 | class func calculateSizeToFitLayers(_ layers: [CALayer]) -> CGSize { 15 | var size = CGSize() 16 | 17 | for layer in layers { 18 | if layer.frame.maxX > size.width { 19 | size.width = layer.frame.maxX 20 | } 21 | 22 | if layer.frame.maxY > size.height { 23 | size.height = layer.frame.maxY 24 | } 25 | } 26 | 27 | return size 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/CosmosText.swift: -------------------------------------------------------------------------------- 1 | 2 | 3 | import UIKit 4 | 5 | /** 6 | 7 | Positions the text layer to the right of the stars. 8 | 9 | */ 10 | class CosmosText { 11 | /** 12 | 13 | Positions the text layer to the right from the stars. Text is aligned to the center of the star superview vertically. 14 | 15 | - parameter layer: The text layer to be positioned. 16 | - parameter starsSize: The size of the star superview. 17 | - parameter textMargin: The distance between the stars and the text. 18 | 19 | */ 20 | class func position(_ layer: CALayer, starsSize: CGSize, textMargin: Double) { 21 | layer.position.x = starsSize.width + CGFloat(textMargin) 22 | let yOffset = (starsSize.height - layer.bounds.height) / 2 23 | layer.position.y = yOffset 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/alfianlosari.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Cosmos.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-apnspushsimulate.xcscheme 13 | 14 | isShown 15 | 16 | 17 | Pods-test.xcscheme 18 | 19 | isShown 20 | 21 | 22 | XCDYouTubeKit.xcscheme 23 | 24 | isShown 25 | 26 | 27 | 28 | SuppressBuildableAutocreation 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/Helpers/CosmosTouchTarget.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** 4 | 5 | Helper function to make sure bounds are big enought to be used as touch target. 6 | The function is used in pointInside(point: CGPoint, withEvent event: UIEvent?) of UIImageView. 7 | 8 | */ 9 | struct CosmosTouchTarget { 10 | static func optimize(_ bounds: CGRect) -> CGRect { 11 | let recommendedHitSize: CGFloat = 44 12 | 13 | var hitWidthIncrease:CGFloat = recommendedHitSize - bounds.width 14 | var hitHeightIncrease:CGFloat = recommendedHitSize - bounds.height 15 | 16 | if hitWidthIncrease < 0 { hitWidthIncrease = 0 } 17 | if hitHeightIncrease < 0 { hitHeightIncrease = 0 } 18 | 19 | let extendedBounds: CGRect = bounds.insetBy(dx: -hitWidthIncrease / 2, 20 | dy: -hitHeightIncrease / 2) 21 | 22 | return extendedBounds 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apnspushsimulate.xcodeproj/xcuserdata/alfianlosari.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | apnspushsimulate.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | test.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 8BB5DCFD23F10F50009B2A18 21 | 22 | primary 23 | 24 | 25 | 8BB5DD1823F13082009B2A18 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Cosmos/Cosmos-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 | FMWK 17 | CFBundleShortVersionString 18 | 21.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-test/Pods-test-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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-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 | FMWK 17 | CFBundleShortVersionString 18 | 2.9.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-test/Pods-test.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Cosmos" "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Cosmos/Cosmos.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit/XCDYouTubeKit.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' '@executable_path/../../Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -framework "Cosmos" -framework "JavaScriptCore" -framework "MediaPlayer" -framework "XCDYouTubeKit" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-test/Pods-test.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Cosmos" "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Cosmos/Cosmos.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit/XCDYouTubeKit.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' '@executable_path/../../Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -framework "Cosmos" -framework "JavaScriptCore" -framework "MediaPlayer" -framework "XCDYouTubeKit" 6 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 7 | PODS_BUILD_DIR = ${BUILD_DIR} 8 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 9 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Cosmos" "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Cosmos/Cosmos.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit/XCDYouTubeKit.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Cosmos" -framework "JavaScriptCore" -framework "MediaPlayer" -framework "XCDYouTubeKit" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Cosmos" "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Cosmos/Cosmos.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit/XCDYouTubeKit.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Cosmos" -framework "JavaScriptCore" -framework "MediaPlayer" -framework "XCDYouTubeKit" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/CosmosLayerHelper.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// Helper class for creating CALayer objects. 4 | class CosmosLayerHelper { 5 | /** 6 | 7 | Creates a text layer for the given text string and font. 8 | 9 | - parameter text: The text shown in the layer. 10 | - parameter font: The text font. It is also used to calculate the layer bounds. 11 | - parameter color: Text color. 12 | 13 | - returns: New text layer. 14 | 15 | */ 16 | class func createTextLayer(_ text: String, font: UIFont, color: UIColor) -> CATextLayer { 17 | let size = NSString(string: text).size(withAttributes: [NSAttributedString.Key.font: font]) 18 | 19 | let layer = CATextLayer() 20 | layer.bounds = CGRect(origin: CGPoint(), size: size) 21 | layer.anchorPoint = CGPoint() 22 | 23 | layer.string = text 24 | layer.font = CGFont(font.fontName as CFString) 25 | layer.fontSize = font.pointSize 26 | layer.foregroundColor = color.cgColor 27 | layer.contentsScale = UIScreen.main.scale 28 | 29 | return layer 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Pods/Cosmos/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015 Evgenii Neumerzhitckii 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2016 Cédric Luthi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import "XCDYouTubeVideo.h" 6 | 7 | #import "XCDYouTubePlayerScript.h" 8 | 9 | #define XCDYouTubeErrorUseCipherSignature -1000 10 | 11 | extern NSString *const XCDYouTubeNoStreamVideoUserInfoKey; 12 | 13 | extern NSDictionary *XCDDictionaryWithQueryString(NSString *string); 14 | extern NSString *XCDQueryStringWithDictionary(NSDictionary *dictionary); 15 | extern NSArray *XCDCaptionArrayWithString(NSString *string); 16 | extern NSArray *XCDThumnailArrayWithString(NSString *string); 17 | extern NSString *XCDHTTPLiveStreamingStringWithString(NSString *string); 18 | extern NSDictionary *XCDDictionaryWithString(NSString *string); 19 | extern NSDictionary *XCDStreamingDataWithString(NSString *string); 20 | 21 | @interface XCDYouTubeVideo () 22 | 23 | - (instancetype) initWithIdentifier:(NSString *)identifier info:(NSDictionary *)info playerScript:(XCDYouTubePlayerScript *)playerScript response:(NSURLResponse *)response error:(NSError **)error; 24 | 25 | - (void) mergeVideo:(XCDYouTubeVideo *)video; 26 | - (void) mergeDashManifestStreamURLs:(NSDictionary *)dashManifestStreamURLs; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import 6 | 7 | #import "XCDYouTubeLogger.h" 8 | 9 | @interface XCDYouTubeLogger () 10 | 11 | + (void) logMessage:(NSString * (^)(void))message level:(XCDLogLevel)level file:(const char *)file function:(const char *)function line:(NSUInteger)line; 12 | 13 | @end 14 | 15 | #define XCDYouTubeLog(_level, _message) [XCDYouTubeLogger logMessage:(_message) level:(_level) file:__FILE__ function:__PRETTY_FUNCTION__ line:__LINE__] 16 | 17 | #define XCDYouTubeLogError(format, ...) XCDYouTubeLog(XCDLogLevelError, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) 18 | #define XCDYouTubeLogWarning(format, ...) XCDYouTubeLog(XCDLogLevelWarning, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) 19 | #define XCDYouTubeLogInfo(format, ...) XCDYouTubeLog(XCDLogLevelInfo, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) 20 | #define XCDYouTubeLogDebug(format, ...) XCDYouTubeLog(XCDLogLevelDebug, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) 21 | #define XCDYouTubeLogVerbose(format, ...) XCDYouTubeLog(XCDLogLevelVerbose, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Interactive Custom Push Notification UI with Simulated Remote Push Notification 2 | 3 | ![Alt text](./promo.jpg?raw=true "Custom Push notification UI") 4 | 5 | 6 | A simple demo showcasing interactive custom push notification UI that has several custom UI: 7 | - Video Player using XCDYoutubeKit 8 | - Star Rating control using Cosmos 9 | - A plain UITextView 10 | - Several UIButton 11 | 12 | ## Requirement 13 | 14 | - Xcode 11.4 15 | 16 | 17 | ## Installation 18 | 19 | - Clone and run the project from simulator 20 | - Use the provided APNS file in the project to drag and drop to target simulator 21 | - Replace the app id using your own app id 22 | - Set the value of `Simulator Target Bundler` key in the apns file to your app id 23 | - Open the notification preview 24 | 25 | 26 | ## Sample APS Payload 27 | 28 | Make sure the extension of the file is apns (not .JSON) 29 | 30 | ``` 31 | { 32 | "aps" : { 33 | "alert" : { 34 | "title" : "A new trailer has arrived for you", 35 | "body" : "Fast and Furious F9 Official Trailer" 36 | }, 37 | "category" : "testNotificationCategory", 38 | "sound": "bingbong.aiff", 39 | "badge": 3, 40 | }, 41 | "videoId" : "Kopyc23VfSw", 42 | "description": "Release date: 05-21-2020", 43 | "Simulator Target Bundle": "com.alfianlosari.apnspushsimulate" 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /test/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | test 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | UNNotificationExtensionDefaultContentHidden 28 | 29 | UNNotificationExtensionUserInteractionEnabled 30 | 31 | UNNotificationExtensionCategory 32 | testNotificationCategory 33 | UNNotificationExtensionInitialContentSizeRatio 34 | 0.12 35 | 36 | NSExtensionMainStoryboard 37 | MainInterface 38 | NSExtensionPointIdentifier 39 | com.apple.usernotifications.content-extension 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /apnspushsimulate/Base.lproj/LaunchScreen.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 | 25 | 26 | -------------------------------------------------------------------------------- /apnspushsimulate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // apnspushsimulate 4 | // 5 | // Created by Alfian Losari on 10/02/20. 6 | // Copyright © 2020 alfianlosari. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import UserNotifications 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | 17 | let center = UNUserNotificationCenter.current() 18 | center.requestAuthorization(options: [.alert, .sound]) { _, _ in } 19 | 20 | let testNotificationCategory = UNNotificationCategory(identifier: "testNotificationCategory", actions: [], intentIdentifiers: [], options: []) 21 | UNUserNotificationCenter.current().setNotificationCategories([testNotificationCategory]) 22 | return true 23 | } 24 | 25 | // MARK: UISceneSession Lifecycle 26 | 27 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 28 | // Called when a new scene session is being created. 29 | // Use this method to select a configuration to create the new scene with. 30 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 31 | } 32 | 33 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 34 | // Called when the user discards a scene session. 35 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 36 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 37 | } 38 | 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /apnspushsimulate/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import 6 | 7 | /** 8 | * The error domain used throughout XCDYouTubeKit. 9 | */ 10 | extern NSString *const XCDYouTubeVideoErrorDomain; 11 | 12 | /** 13 | * A key that may be present in the error's userInfo dictionary when the error code is XCDYouTubeErrorNoStreamAvailable. 14 | * The object for that key is a NSSet instance containing localized country names. 15 | */ 16 | extern NSString *const XCDYouTubeAllowedCountriesUserInfoKey; 17 | 18 | /** 19 | * These values are returned as the error code property of an NSError object with the domain `XCDYouTubeVideoErrorDomain`. 20 | */ 21 | typedef NS_ENUM(NSInteger, XCDYouTubeErrorCode) { 22 | /** 23 | * Returned when no suitable video stream is available. This can occur due to various reason such as: 24 | * * The video is not playable because of legal reasons or when the video is private. 25 | * * The given video identifier string is invalid. 26 | * * The video was removed as a violation of YouTube's policy or when the video did not exist. 27 | */ 28 | XCDYouTubeErrorNoStreamAvailable = -2, 29 | 30 | /** 31 | * Returned when a network error occurs. See `NSUnderlyingErrorKey` in the userInfo dictionary for more information. 32 | */ 33 | XCDYouTubeErrorNetwork = -1, 34 | 35 | /** 36 | * Previously returned when the given video identifier string is invalid. 37 | * Use `XCDYouTubeErrorNoStreamAvailable` instead. 38 | */ 39 | XCDYouTubeErrorInvalidVideoIdentifier DEPRECATED_MSG_ATTRIBUTE("YouTube has stopped using error code 2.") = 2, 40 | 41 | /** 42 | * Previously returned when the video was removed as a violation of YouTube's policy or when the video did not exist. 43 | */ 44 | XCDYouTubeErrorRemovedVideo DEPRECATED_MSG_ATTRIBUTE("YouTube has stopped using error code 100.") = 100, 45 | 46 | /** 47 | * Previously returned when the video is not playable because of legal reasons or when the video is private. 48 | * Use `XCDYouTubeErrorNoStreamAvailable` instead. 49 | */ 50 | XCDYouTubeErrorRestrictedPlayback DEPRECATED_MSG_ATTRIBUTE("YouTube has stopped using error code 150.") = 150 51 | }; 52 | -------------------------------------------------------------------------------- /apnspushsimulate/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/alfianlosari.xcuserdatad/xcschemes/Pods-test.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/alfianlosari.xcuserdatad/xcschemes/Pods-apnspushsimulate.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/alfianlosari.xcuserdatad/xcschemes/Cosmos.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/alfianlosari.xcuserdatad/xcschemes/XCDYouTubeKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-test/Pods-test-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Cosmos 5 | 6 | The MIT License 7 | 8 | Copyright (c) 2015 Evgenii Neumerzhitckii 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | 28 | ## XCDYouTubeKit 29 | 30 | The MIT License (MIT) 31 | 32 | Copyright (c) 2013-2016 Cédric Luthi 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in 42 | all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 50 | THE SOFTWARE. 51 | 52 | 53 | Generated by CocoaPods - https://cocoapods.org 54 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Cosmos 5 | 6 | The MIT License 7 | 8 | Copyright (c) 2015 Evgenii Neumerzhitckii 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | 28 | ## XCDYouTubeKit 29 | 30 | The MIT License (MIT) 31 | 32 | Copyright (c) 2013-2016 Cédric Luthi 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in 42 | all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 50 | THE SOFTWARE. 51 | 52 | 53 | Generated by CocoaPods - https://cocoapods.org 54 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/CosmosTouch.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** 4 | 5 | Functions for working with touch input. 6 | 7 | */ 8 | struct CosmosTouch { 9 | /** 10 | 11 | Calculates the rating based on the touch location. 12 | 13 | - parameter position: The horizontal location of the touch relative to the width of the stars. 14 | 15 | - returns: The rating representing the touch location. 16 | 17 | */ 18 | static func touchRating(_ position: CGFloat, settings: CosmosSettings) -> Double { 19 | var rating = preciseRating( 20 | position: Double(position), 21 | numberOfStars: settings.totalStars, 22 | starSize: settings.starSize, 23 | starMargin: settings.starMargin) 24 | 25 | if settings.fillMode == .half { 26 | rating += 0.20 27 | } 28 | 29 | if settings.fillMode == .full { 30 | rating += 0.45 31 | } 32 | 33 | rating = CosmosRating.displayedRatingFromPreciseRating(rating, 34 | fillMode: settings.fillMode, totalStars: settings.totalStars) 35 | 36 | rating = max(settings.minTouchRating, rating) // Can't be less than min rating 37 | 38 | return rating 39 | } 40 | 41 | 42 | /** 43 | 44 | Returns the precise rating based on the touch position. 45 | 46 | - parameter position: The horizontal location of the touch relative to the width of the stars. 47 | - parameter numberOfStars: Total number of stars, filled and full. 48 | - parameter starSize: The width of a star. 49 | - parameter starSize: Margin between stars. 50 | - returns: The precise rating. 51 | 52 | */ 53 | static func preciseRating(position: Double, numberOfStars: Int, 54 | starSize: Double, starMargin: Double) -> Double { 55 | 56 | if position < 0 { return 0 } 57 | var positionRemainder = position; 58 | 59 | // Calculate the number of times the star with a margin fits the position 60 | // This will be the whole part of the rating 61 | var rating: Double = Double(Int(position / (starSize + starMargin))) 62 | 63 | // If rating is grater than total number of stars - return maximum rating 64 | if Int(rating) > numberOfStars { return Double(numberOfStars) } 65 | 66 | // Calculate what portion of the last star does the position correspond to 67 | // This will be the added partial part of the rating 68 | 69 | positionRemainder -= rating * (starSize + starMargin) 70 | 71 | if positionRemainder > starSize 72 | { 73 | rating += 1 74 | } else { 75 | rating += positionRemainder / starSize 76 | } 77 | 78 | return rating 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import "XCDYouTubeLogger.h" 6 | 7 | #import 8 | 9 | const NSInteger XCDYouTubeKitLumberjackContext = (NSInteger)0xced70676; 10 | 11 | @protocol XCDYouTubeLogger_DDLog 12 | // Copied from CocoaLumberjack's DDLog interface 13 | + (void) log:(BOOL)asynchronous message:(NSString *)message level:(NSUInteger)level flag:(NSUInteger)flag context:(NSInteger)context file:(const char *)file function:(const char *)function line:(NSUInteger)line tag:(id)tag; 14 | @end 15 | 16 | static Class DDLogClass = Nil; 17 | 18 | static void (^const CocoaLumberjackLogHandler)(NSString * (^)(void), XCDLogLevel, const char *, const char *, NSUInteger) = ^(NSString *(^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line) 19 | { 20 | // The `XCDLogLevel` enum was carefully crafted to match the `DDLogFlag` options from DDLog.h 21 | [DDLogClass log:YES message:message() level:NSUIntegerMax flag:(1 << level) context:XCDYouTubeKitLumberjackContext file:file function:function line:line tag:nil]; 22 | }; 23 | 24 | static void (^LogHandler)(NSString * (^)(void), XCDLogLevel, const char *, const char *, NSUInteger) = ^(NSString *(^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line) 25 | { 26 | char *logLevelString = getenv("XCDYouTubeKitLogLevel"); 27 | NSUInteger logLevelMask = logLevelString ? strtoul(logLevelString, NULL, 0) : (1 << XCDLogLevelError) | (1 << XCDLogLevelWarning); 28 | if ((1 << level) & logLevelMask) 29 | NSLog(@"[XCDYouTubeKit] %@", message()); 30 | }; 31 | 32 | @implementation XCDYouTubeLogger 33 | 34 | + (void) initialize 35 | { 36 | static dispatch_once_t once; 37 | dispatch_once(&once, ^{ 38 | DDLogClass = objc_lookUpClass("DDLog"); 39 | if (DDLogClass) 40 | { 41 | const SEL logSeletor = @selector(log:message:level:flag:context:file:function:line:tag:); 42 | const char *typeEncoding = method_getTypeEncoding((Method)class_getClassMethod(DDLogClass, logSeletor)); 43 | const char *expectedTypeEncoding = protocol_getMethodDescription(@protocol(XCDYouTubeLogger_DDLog), logSeletor, /* isRequiredMethod: */ YES, /* isInstanceMethod: */ NO).types; 44 | if (typeEncoding && expectedTypeEncoding && strcmp(typeEncoding, expectedTypeEncoding) == 0) 45 | LogHandler = CocoaLumberjackLogHandler; 46 | else 47 | NSLog(@"[XCDYouTubeKit] Incompatible CocoaLumberjack version. Expected \"%@\", got \"%@\".", expectedTypeEncoding ? @(expectedTypeEncoding) : @"", typeEncoding ? @(typeEncoding) : @""); 48 | } 49 | }); 50 | } 51 | 52 | + (void) setLogHandler:(void (^)(NSString * (^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line))logHandler 53 | { 54 | LogHandler = logHandler; 55 | } 56 | 57 | + (void) logMessage:(NSString * (^)(void))message level:(XCDLogLevel)level file:(const char *)file function:(const char *)function line:(NSUInteger)line 58 | { 59 | if (LogHandler) 60 | LogHandler(message, level, file, function, line); 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /apnspushsimulate.xcodeproj/xcshareddata/xcschemes/apnspushsimulate.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /apnspushsimulate/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/CosmosLocalizedRating.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /** 4 | 5 | Returns the word "Rating" in user's language. It is used for voice-over in accessibility mode. 6 | 7 | */ 8 | struct CosmosLocalizedRating { 9 | static var defaultText = "Rating" 10 | 11 | static var localizedRatings = [ 12 | "ar": "تصنيف", 13 | "bg": "Рейтинг", 14 | "cy": "Sgôr", 15 | "da": "Rating", 16 | "de": "Bewertung", 17 | "el": "Βαθμολογία", 18 | "en": defaultText, 19 | "es": "Valorar", 20 | "et": "Reiting", 21 | "fi": "Luokitus", 22 | "fr": "De note", 23 | "he": "דירוג", 24 | "hi": "रेटिंग", 25 | "hr": "Ocjena", 26 | "hu": "Értékelés", 27 | "id": "Peringkat", 28 | "it": "Voto", 29 | "ko": "등급", 30 | "lt": "Reitingas", 31 | "lv": "Vērtējums", 32 | "nl": "Rating", 33 | "no": "Vurdering", 34 | "pl": "Ocena", 35 | "pt": "Classificação", 36 | "ro": "Evaluare", 37 | "ru": "Рейтинг", 38 | "sk": "Hodnotenie", 39 | "sl": "Ocena", 40 | "sr": "Рејтинг", 41 | "sw": "Rating", 42 | "th": "การจัดอันดับ", 43 | "tr": "Oy verin", 44 | "cs": "Hodnocení", 45 | "uk": "Рейтинг", 46 | "vi": "Đánh giá", 47 | "zh": "评分" 48 | ] 49 | 50 | static var ratingTranslation: String { 51 | let languages = preferredLanguages(Locale.preferredLanguages) 52 | return ratingInPreferredLanguage(languages) 53 | } 54 | 55 | /** 56 | 57 | Returns the word "Rating" in user's language. 58 | 59 | - parameter language: ISO 639-1 language code. Example: 'en'. 60 | 61 | */ 62 | static func translation(_ language: String) -> String? { 63 | return localizedRatings[language] 64 | } 65 | 66 | /** 67 | 68 | Returns translation using the preferred language. 69 | 70 | - parameter preferredLanguages: Array of preferred language codes (ISO 639-1). The first element is most preferred. 71 | 72 | - parameter localizedText: Dictionary with translations for the languages. The keys are ISO 639-1 language codes and values are the text. 73 | 74 | - parameter fallbackTranslation: The translation text used if no translation found for the preferred languages. 75 | 76 | - returns: Translation for the preferred language. 77 | 78 | */ 79 | static func translationInPreferredLanguage(_ preferredLanguages: [String], 80 | localizedText: [String: String], 81 | fallbackTranslation: String) -> String { 82 | 83 | for language in preferredLanguages { 84 | if let translatedText = translation(language) { 85 | return translatedText 86 | } 87 | } 88 | 89 | return fallbackTranslation 90 | } 91 | 92 | static func ratingInPreferredLanguage(_ preferredLanguages: [String]) -> String { 93 | return translationInPreferredLanguage(preferredLanguages, 94 | localizedText: localizedRatings, 95 | fallbackTranslation: defaultText) 96 | } 97 | 98 | static func preferredLanguages(_ preferredLocales: [String]) -> [String] { 99 | return preferredLocales.map { element in 100 | 101 | let dashSeparated = element.components(separatedBy: "-") 102 | if dashSeparated.count > 1 { return dashSeparated[0] } 103 | 104 | let underscoreSeparated = element.components(separatedBy: "_") 105 | if underscoreSeparated.count > 1 { return underscoreSeparated[0] } 106 | 107 | return element 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/CosmosAccessibility.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** 4 | 5 | Functions for making cosmos view accessible. 6 | 7 | */ 8 | struct CosmosAccessibility { 9 | /** 10 | 11 | Makes the view accesible by settings its label and using rating as value. 12 | 13 | */ 14 | 15 | static func update(_ view: UIView, rating: Double, text: String?, settings: CosmosSettings) { 16 | view.isAccessibilityElement = true 17 | 18 | view.accessibilityTraits = settings.updateOnTouch ? 19 | UIAccessibilityTraits.adjustable :UIAccessibilityTraits.none 20 | 21 | var accessibilityLabel = CosmosLocalizedRating.ratingTranslation 22 | 23 | if let text = text, text != "" { 24 | accessibilityLabel += " \(text)" 25 | } 26 | 27 | view.accessibilityLabel = accessibilityLabel 28 | 29 | view.accessibilityValue = accessibilityValue(view, rating: rating, settings: settings) 30 | } 31 | 32 | /** 33 | 34 | Returns the rating that is used as accessibility value. 35 | The accessibility value depends on the star fill mode. 36 | 37 | For example, if rating is 4.6 and fill mode is .half the value will be 4.5. And if the fill mode 38 | if .full the value will be 5. 39 | 40 | */ 41 | static func accessibilityValue(_ view: UIView, rating: Double, settings: CosmosSettings) -> String { 42 | let accessibilityRating = CosmosRating.displayedRatingFromPreciseRating(rating, 43 | fillMode: settings.fillMode, totalStars: settings.totalStars) 44 | 45 | // Omit decimals if the value is an integer 46 | let isInteger = (accessibilityRating * 10).truncatingRemainder(dividingBy: 10) == 0 47 | 48 | if isInteger { 49 | return "\(Int(accessibilityRating))" 50 | } else { 51 | // Only show a single decimal place 52 | let roundedToFirstDecimalPlace = Double( round(10 * accessibilityRating) / 10 ) 53 | return "\(roundedToFirstDecimalPlace)" 54 | } 55 | } 56 | 57 | /** 58 | 59 | Returns the amount of increment for the rating. When .half and .precise fill modes are used the 60 | rating is incremented by 0.5. 61 | 62 | */ 63 | static func accessibilityIncrement(_ rating: Double, settings: CosmosSettings) -> Double { 64 | var increment: Double = 0 65 | 66 | switch settings.fillMode { 67 | case .full: 68 | increment = ceil(rating) - rating 69 | if increment == 0 { increment = 1 } 70 | 71 | case .half, .precise: 72 | increment = (ceil(rating * 2) - rating * 2) / 2 73 | if increment == 0 { increment = 0.5 } 74 | } 75 | 76 | if rating >= Double(settings.totalStars) { increment = 0 } 77 | 78 | let roundedToFirstDecimalPlace = Double( round(10 * increment) / 10 ) 79 | return roundedToFirstDecimalPlace 80 | } 81 | 82 | static func accessibilityDecrement(_ rating: Double, settings: CosmosSettings) -> Double { 83 | var increment: Double = 0 84 | 85 | switch settings.fillMode { 86 | case .full: 87 | increment = rating - floor(rating) 88 | if increment == 0 { increment = 1 } 89 | 90 | case .half, .precise: 91 | increment = (rating * 2 - floor(rating * 2)) / 2 92 | if increment == 0 { increment = 0.5 } 93 | } 94 | 95 | if rating <= settings.minTouchRating { increment = 0 } 96 | 97 | let roundedToFirstDecimalPlace = Double( round(10 * increment) / 10 ) 98 | return roundedToFirstDecimalPlace 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import "XCDYouTubeClient.h" 6 | 7 | #import "XCDYouTubeVideoOperation.h" 8 | 9 | @interface XCDYouTubeClient () 10 | @property (nonatomic, strong) NSOperationQueue *queue; 11 | @end 12 | 13 | @implementation XCDYouTubeClient 14 | 15 | @synthesize languageIdentifier = _languageIdentifier; 16 | 17 | + (instancetype) defaultClient 18 | { 19 | static XCDYouTubeClient *defaultClient; 20 | static dispatch_once_t once; 21 | dispatch_once(&once, ^{ 22 | defaultClient = [self new]; 23 | }); 24 | return defaultClient; 25 | } 26 | 27 | - (instancetype) init 28 | { 29 | return [self initWithLanguageIdentifier:nil]; 30 | } 31 | 32 | - (instancetype) initWithLanguageIdentifier:(NSString *)languageIdentifier 33 | { 34 | if (!(self = [super init])) 35 | return nil; // LCOV_EXCL_LINE 36 | 37 | _languageIdentifier = ^{ 38 | return languageIdentifier ?: ^{ 39 | NSArray *preferredLocalizations = [[NSBundle mainBundle] preferredLocalizations]; 40 | NSString *preferredLocalization = preferredLocalizations.firstObject ?: @"en"; 41 | return [NSLocale canonicalLanguageIdentifierFromString:preferredLocalization] ?: @"en"; 42 | }(); 43 | }(); 44 | 45 | _queue = [NSOperationQueue new]; 46 | _queue.maxConcurrentOperationCount = 6; // paul_irish: Chrome re-confirmed that the 6 connections-per-host limit is the right magic number: https://code.google.com/p/chromium/issues/detail?id=285567#c14 [https://twitter.com/paul_irish/status/422808635698212864] 47 | _queue.name = NSStringFromClass(self.class); 48 | 49 | return self; 50 | } 51 | 52 | - (id) getVideoWithIdentifier:(NSString *)videoIdentifier cookies:(NSArray *)cookies customPatterns:(NSArray *)customPatterns completionHandler:(void (^)(XCDYouTubeVideo * _Nullable, NSError * _Nullable))completionHandler 53 | { 54 | if (!completionHandler) 55 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"The `completionHandler` argument must not be nil." userInfo:nil]; 56 | 57 | XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:videoIdentifier languageIdentifier:self.languageIdentifier cookies:cookies customPatterns:customPatterns]; 58 | operation.completionBlock = ^{ 59 | [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 60 | #pragma clang diagnostic push 61 | #pragma clang diagnostic ignored "-Warc-retain-cycles" 62 | if (operation.video || operation.error) 63 | { 64 | NSAssert(!(operation.video && operation.error), @"One of `video` or `error` must be nil."); 65 | completionHandler(operation.video, operation.error); 66 | } 67 | else 68 | { 69 | NSAssert(operation.isCancelled, @"Both `video` and `error` can not be nil if the operation was not canceled."); 70 | } 71 | operation.completionBlock = nil; 72 | #pragma clang diagnostic pop 73 | }]; 74 | }; 75 | [self.queue addOperation:operation]; 76 | return operation; 77 | } 78 | 79 | - (id) getVideoWithIdentifier:(NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler 80 | { 81 | return [self getVideoWithIdentifier:videoIdentifier cookies:nil customPatterns:nil completionHandler:completionHandler]; 82 | } 83 | 84 | - (id) getVideoWithIdentifier:(NSString *)videoIdentifier cookies:(NSArray *)cookies completionHandler:(void (^)(XCDYouTubeVideo * _Nullable, NSError * _Nullable))completionHandler 85 | { 86 | return [self getVideoWithIdentifier:videoIdentifier cookies:cookies customPatterns:nil completionHandler:completionHandler]; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-test/Pods-test-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License 18 | 19 | Copyright (c) 2015 Evgenii Neumerzhitckii 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in 29 | all copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 37 | THE SOFTWARE. 38 | License 39 | MIT 40 | Title 41 | Cosmos 42 | Type 43 | PSGroupSpecifier 44 | 45 | 46 | FooterText 47 | The MIT License (MIT) 48 | 49 | Copyright (c) 2013-2016 Cédric Luthi 50 | 51 | Permission is hereby granted, free of charge, to any person obtaining a copy 52 | of this software and associated documentation files (the "Software"), to deal 53 | in the Software without restriction, including without limitation the rights 54 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 55 | copies of the Software, and to permit persons to whom the Software is 56 | furnished to do so, subject to the following conditions: 57 | 58 | The above copyright notice and this permission notice shall be included in 59 | all copies or substantial portions of the Software. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 63 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 64 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 65 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 67 | THE SOFTWARE. 68 | 69 | 70 | License 71 | MIT 72 | Title 73 | XCDYouTubeKit 74 | Type 75 | PSGroupSpecifier 76 | 77 | 78 | FooterText 79 | Generated by CocoaPods - https://cocoapods.org 80 | Title 81 | 82 | Type 83 | PSGroupSpecifier 84 | 85 | 86 | StringsTable 87 | Acknowledgements 88 | Title 89 | Acknowledgements 90 | 91 | 92 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/CosmosDefaultSettings.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** 4 | 5 | Defaults setting values. 6 | 7 | */ 8 | struct CosmosDefaultSettings { 9 | init() {} 10 | 11 | static let defaultColor = UIColor(red: 1, green: 149/255, blue: 0, alpha: 1) 12 | 13 | 14 | // MARK: - Star settings 15 | // ----------------------------- 16 | 17 | /// Border color of an empty star. 18 | static let emptyBorderColor = defaultColor 19 | 20 | /// Width of the border for the empty star. 21 | static let emptyBorderWidth: Double = 1 / Double(UIScreen.main.scale) 22 | 23 | /// Border color of a filled star. 24 | static let filledBorderColor = defaultColor 25 | 26 | /// Width of the border for a filled star. 27 | static let filledBorderWidth: Double = 1 / Double(UIScreen.main.scale) 28 | 29 | /// Background color of an empty star. 30 | static let emptyColor = UIColor.clear 31 | 32 | /// Background color of a filled star. 33 | static let filledColor = defaultColor 34 | 35 | /** 36 | 37 | Defines how the star is filled when the rating value is not an integer value. It can either show full stars, half stars or stars partially filled according to the rating value. 38 | 39 | */ 40 | static let fillMode = StarFillMode.full 41 | 42 | /// Rating value that is shown in the storyboard by default. 43 | static let rating: Double = 2.718281828 44 | 45 | /// Distance between stars. 46 | static let starMargin: Double = 5 47 | 48 | /** 49 | 50 | Array of points for drawing the star with size of 100 by 100 pixels. Supply your points if you need to draw a different shape. 51 | 52 | */ 53 | static let starPoints: [CGPoint] = [ 54 | CGPoint(x: 49.5, y: 0.0), 55 | CGPoint(x: 60.5, y: 35.0), 56 | CGPoint(x: 99.0, y: 35.0), 57 | CGPoint(x: 67.5, y: 58.0), 58 | CGPoint(x: 78.5, y: 92.0), 59 | CGPoint(x: 49.5, y: 71.0), 60 | CGPoint(x: 20.5, y: 92.0), 61 | CGPoint(x: 31.5, y: 58.0), 62 | CGPoint(x: 0.0, y: 35.0), 63 | CGPoint(x: 38.5, y: 35.0) 64 | ] 65 | 66 | /// Size of a single star. 67 | static var starSize: Double = 20 68 | 69 | /// The total number of stars to be shown. 70 | static let totalStars = 5 71 | 72 | 73 | // MARK: - Text settings 74 | // ----------------------------- 75 | 76 | 77 | /// Color of the text. 78 | static let textColor = UIColor(red: 127/255, green: 127/255, blue: 127/255, alpha: 1) 79 | 80 | /// Font for the text. 81 | static let textFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.footnote) 82 | 83 | /// Distance between the text and the stars. 84 | static let textMargin: Double = 5 85 | 86 | /// Calculates the size of the default text font. It is used for making the text size configurable from the storyboard. 87 | static var textSize: Double { 88 | get { 89 | return Double(textFont.pointSize) 90 | } 91 | } 92 | 93 | 94 | // MARK: - Touch settings 95 | // ----------------------------- 96 | 97 | /// The lowest rating that user can set by touching the stars. 98 | static let minTouchRating: Double = 1 99 | 100 | /// Set to `false` if you don't want to pass touches to superview (can be useful in a table view). 101 | static let passTouchesToSuperview = true 102 | 103 | /// When `true` the star fill level is updated when user touches the cosmos view. When `false` the Cosmos view only shows the rating and does not act as the input control. 104 | static let updateOnTouch = true 105 | 106 | /// Set to `true` if you want to ignore pan gestures (can be useful when presented modally with a `presentationStyle` of `pageSheet` to avoid competing with the dismiss gesture) 107 | static let disablePanGestures = false 108 | } 109 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License 18 | 19 | Copyright (c) 2015 Evgenii Neumerzhitckii 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in 29 | all copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 37 | THE SOFTWARE. 38 | License 39 | MIT 40 | Title 41 | Cosmos 42 | Type 43 | PSGroupSpecifier 44 | 45 | 46 | FooterText 47 | The MIT License (MIT) 48 | 49 | Copyright (c) 2013-2016 Cédric Luthi 50 | 51 | Permission is hereby granted, free of charge, to any person obtaining a copy 52 | of this software and associated documentation files (the "Software"), to deal 53 | in the Software without restriction, including without limitation the rights 54 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 55 | copies of the Software, and to permit persons to whom the Software is 56 | furnished to do so, subject to the following conditions: 57 | 58 | The above copyright notice and this permission notice shall be included in 59 | all copies or substantial portions of the Software. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 62 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 63 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 64 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 65 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 67 | THE SOFTWARE. 68 | 69 | 70 | License 71 | MIT 72 | Title 73 | XCDYouTubeKit 74 | Type 75 | PSGroupSpecifier 76 | 77 | 78 | FooterText 79 | Generated by CocoaPods - https://cocoapods.org 80 | Title 81 | 82 | Type 83 | PSGroupSpecifier 84 | 85 | 86 | StringsTable 87 | Acknowledgements 88 | Title 89 | Acknowledgements 90 | 91 | 92 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.m: -------------------------------------------------------------------------------- 1 | // 2 | // XCDYouTubeDashManifestXML.m 3 | // XCDYouTubeKit 4 | // 5 | // Created by Soneé John on 10/24/17. 6 | // Copyright © 2017 Cédric Luthi. All rights reserved. 7 | // 8 | 9 | #import "XCDYouTubeDashManifestXML.h" 10 | 11 | @interface XCDYouTubeDashManifestXML() 12 | @property (nonatomic, readonly) NSString *XMLString; 13 | @end 14 | 15 | 16 | @implementation XCDYouTubeDashManifestXML 17 | 18 | - (instancetype)initWithXMLString:(NSString *)XMLString 19 | { 20 | if (!(self = [super init])) 21 | return nil; // LCOV_EXCL_LINE 22 | 23 | _XMLString = XMLString; 24 | 25 | return self; 26 | } 27 | 28 | - (NSDictionary *)streamURLs 29 | { 30 | 31 | //Catch the type 32 | NSError *xmlTypeRegexError = NULL; 33 | NSRegularExpression *xmlTypeRegex = [NSRegularExpression regularExpressionWithPattern:@"(?<=(type=\"))(\\w|\\d|\\n|[().,\\-:;@#$%^&*\\[\\]\"'+–/\\/®°⁰!?{}|`~]| )+?(?=(\"))" options:NSRegularExpressionAnchorsMatchLines error:&xmlTypeRegexError]; 34 | if (xmlTypeRegexError) 35 | return nil; 36 | NSTextCheckingResult *xmlTypeRegexCheckingResult = [xmlTypeRegex firstMatchInString:self.XMLString options:0 range:NSMakeRange(0, self.XMLString.length)]; 37 | NSString *xmlType = [self.XMLString substringWithRange:xmlTypeRegexCheckingResult.range]; 38 | 39 | NSRange staticRange = [xmlType rangeOfString:@"static" options:0]; 40 | if (staticRange.location == NSNotFound) 41 | return nil; 42 | 43 | //Do not process manifests that have DRM protection 44 | NSRange contentProtectionRange = [self.XMLString rangeOfString:@"ContentProtection" options:0]; 45 | NSRange mp4ProtectionRange = [self.XMLString rangeOfString:@"mp4protection" options:0]; 46 | if (contentProtectionRange.location != NSNotFound || mp4ProtectionRange.location != NSNotFound) 47 | return nil; 48 | 49 | //Catch all URLs 50 | NSError *error = nil; 51 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=())(\\w|\\d|\\n|[().,\\-:;@#$%^&*\\[\\]\"'+–/\\/®°⁰!?{}|`~]| )+?(?=())" options:0 error:&error]; 52 | 53 | if (error) 54 | return nil; 55 | 56 | NSArray *checkingResults = [regex matchesInString:self.XMLString options:0 range:NSMakeRange(0, [self.XMLString length])]; 57 | 58 | if (checkingResults.count == 0 || checkingResults == nil) 59 | return nil; 60 | 61 | NSMutableArray *URLs = [NSMutableArray new]; 62 | NSMutableDictionary *streamURLs = [NSMutableDictionary new]; 63 | 64 | for (NSTextCheckingResult *checkingResult in checkingResults) 65 | { 66 | NSString* substringForMatch = [self.XMLString substringWithRange:checkingResult.range]; 67 | NSURL *url = [NSURL URLWithString:substringForMatch]; 68 | 69 | NSRange youtubeRange = [url.absoluteString rangeOfString:@"youtube" options:0]; 70 | NSRange itagnRange = [url.absoluteString rangeOfString:@"itag" options:0]; 71 | 72 | if (youtubeRange.location != NSNotFound && itagnRange.location != NSNotFound ) 73 | { 74 | [URLs addObject:url]; 75 | } 76 | } 77 | 78 | for (NSURL *url in URLs) 79 | { 80 | NSError *itagRegexError = nil; 81 | NSRegularExpression *itagRegex = [NSRegularExpression regularExpressionWithPattern:@"(?<=(/itag/))(\\w|\\d|\\n|[().,\\-:;@#$%^&*\\[\\]\"'+–/\\/®°⁰!?{}|`~]| )+?(?=(/))" options:NSRegularExpressionAnchorsMatchLines error:&itagRegexError]; 82 | 83 | if (itagRegexError) 84 | continue; 85 | 86 | NSTextCheckingResult *itagCheckingResult = [itagRegex firstMatchInString:(NSString *_Nonnull)url.absoluteString options:0 range:NSMakeRange(0, url.absoluteString.length)]; 87 | 88 | NSString *itag = [url.absoluteString substringWithRange:itagCheckingResult.range]; 89 | streamURLs[@(itag.integerValue)] = url; 90 | } 91 | 92 | if (streamURLs.count == 0) 93 | return nil; 94 | 95 | return streamURLs; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/CosmosRating.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** 4 | 5 | Helper functions for calculating rating. 6 | 7 | */ 8 | struct CosmosRating { 9 | 10 | /** 11 | 12 | Returns a decimal number between 0 and 1 describing the star fill level. 13 | 14 | - parameter ratingRemainder: This value is passed from the loop that creates star layers. The value starts with the rating value and decremented by 1 when each star is created. For example, suppose we want to display rating of 3.5. When the first star is created the ratingRemainder parameter will be 3.5. For the second star it will be 2.5. Third: 1.5. Fourth: 0.5. Fifth: -0.5. 15 | 16 | - parameter fillMode: Describe how stars should be filled: full, half or precise. 17 | 18 | - returns: Decimal value between 0 and 1 describing the star fill level. 1 is a fully filled star. 0 is an empty star. 0.5 is a half-star. 19 | 20 | */ 21 | static func starFillLevel(ratingRemainder: Double, fillMode: StarFillMode) -> Double { 22 | 23 | var result = ratingRemainder 24 | 25 | if result > 1 { result = 1 } 26 | if result < 0 { result = 0 } 27 | 28 | return roundFillLevel(result, fillMode: fillMode) 29 | } 30 | 31 | /** 32 | 33 | Rounds a single star's fill level according to the fill mode. "Full" mode returns 0 or 1 by using the standard decimal rounding. "Half" mode returns 0, 0.5 or 1 by rounding the decimal to closest of 3 values. "Precise" mode will return the fill level unchanged. 34 | 35 | - parameter starFillLevel: Decimal number between 0 and 1 describing the star fill level. 36 | 37 | - parameter fillMode: Fill mode that is used to round the fill level value. 38 | 39 | - returns: The rounded fill level. 40 | 41 | */ 42 | static func roundFillLevel(_ starFillLevel: Double, fillMode: StarFillMode) -> Double { 43 | switch fillMode { 44 | case .full: 45 | return Double(round(starFillLevel)) 46 | case .half: 47 | return Double(round(starFillLevel * 2) / 2) 48 | case .precise : 49 | return starFillLevel 50 | } 51 | } 52 | 53 | 54 | /** 55 | 56 | Helper function for calculating the rating that is displayed to the user 57 | taking into account the star fill mode. For example, if the fill mode is .half and precise rating is 4.6, the displayed rating will be 4.5. And if the fill mode is .full the displayed rating will be 5. 58 | 59 | - parameter preciseRating: Precise rating value, like 4.8237 60 | 61 | - parameter fillMode: Describe how stars should be filled: full, half or precise. 62 | 63 | - parameter totalStars: Total number of stars. 64 | 65 | - returns: Returns rating that is displayed to the user taking into account the star fill mode. 66 | 67 | */ 68 | static func displayedRatingFromPreciseRating(_ preciseRating: Double, 69 | fillMode: StarFillMode, totalStars: Int) -> Double { 70 | 71 | let starFloorNumber = floor(preciseRating) 72 | let singleStarRemainder = preciseRating - starFloorNumber 73 | 74 | var displayedRating = starFloorNumber + starFillLevel( 75 | ratingRemainder: singleStarRemainder, fillMode: fillMode) 76 | 77 | displayedRating = min(Double(totalStars), displayedRating) // Can't go bigger than number of stars 78 | displayedRating = max(0, displayedRating) // Can't be less than zero 79 | 80 | return displayedRating 81 | } 82 | 83 | /** 84 | 85 | Returns the number of filled stars for given rating. 86 | 87 | - parameter rating: The rating to be displayed. 88 | - parameter totalNumberOfStars: Total number of stars. 89 | - returns: Number of filled stars. If rating is biggen than the total number of stars (usually 5) it returns the maximum number of stars. 90 | 91 | */ 92 | static func numberOfFilledStars(_ rating: Double, totalNumberOfStars: Int) -> Double { 93 | if rating > Double(totalNumberOfStars) { return Double(totalNumberOfStars) } 94 | if rating < 0 { return 0 } 95 | 96 | return rating 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /apnspushsimulate.xcodeproj/xcshareddata/xcschemes/test.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 60 | 62 | 68 | 69 | 70 | 71 | 78 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/CosmosSettings.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** 4 | 5 | Settings that define the appearance of the star rating views. 6 | 7 | */ 8 | public struct CosmosSettings { 9 | 10 | /// Returns default set of settings for CosmosView 11 | public static var `default`: CosmosSettings { 12 | return CosmosSettings() 13 | } 14 | 15 | public init() {} 16 | 17 | // MARK: - Star settings 18 | // ----------------------------- 19 | 20 | /// Border color of an empty star. 21 | public var emptyBorderColor = CosmosDefaultSettings.emptyBorderColor 22 | 23 | /// Width of the border for empty star. 24 | public var emptyBorderWidth: Double = CosmosDefaultSettings.emptyBorderWidth 25 | 26 | /// Border color of a filled star. 27 | public var filledBorderColor = CosmosDefaultSettings.filledBorderColor 28 | 29 | /// Width of the border for a filled star. 30 | public var filledBorderWidth: Double = CosmosDefaultSettings.filledBorderWidth 31 | 32 | /// Background color of an empty star. 33 | public var emptyColor = CosmosDefaultSettings.emptyColor 34 | 35 | /// Background color of a filled star. 36 | public var filledColor = CosmosDefaultSettings.filledColor 37 | 38 | /** 39 | 40 | Defines how the star is filled when the rating value is not a whole integer. It can either show full stars, half stars or stars partially filled according to the rating value. 41 | 42 | */ 43 | public var fillMode = CosmosDefaultSettings.fillMode 44 | 45 | /// Distance between stars. 46 | public var starMargin: Double = CosmosDefaultSettings.starMargin 47 | 48 | /** 49 | 50 | Array of points for drawing the star with size of 100 by 100 pixels. Supply your points if you need to draw a different shape. 51 | 52 | */ 53 | public var starPoints: [CGPoint] = CosmosDefaultSettings.starPoints 54 | 55 | /// Size of a single star. 56 | public var starSize: Double = CosmosDefaultSettings.starSize 57 | 58 | /// The maximum number of stars to be shown. 59 | public var totalStars = CosmosDefaultSettings.totalStars 60 | 61 | // MARK: - Star image settings 62 | // ----------------------------- 63 | 64 | /** 65 | 66 | Image used for the filled portion of the star. By default the star is drawn from the array of points unless an image is supplied. 67 | 68 | */ 69 | public var filledImage: UIImage? = nil 70 | 71 | /** 72 | 73 | Image used for the empty portion of the star. By default the star is drawn from the array of points unless an image is supplied. 74 | 75 | */ 76 | public var emptyImage: UIImage? = nil 77 | 78 | // MARK: - Text settings 79 | // ----------------------------- 80 | 81 | /// Color of the text. 82 | public var textColor = CosmosDefaultSettings.textColor 83 | 84 | /// Font for the text. 85 | public var textFont = CosmosDefaultSettings.textFont 86 | 87 | /// Distance between the text and the stars. 88 | public var textMargin: Double = CosmosDefaultSettings.textMargin 89 | 90 | 91 | // MARK: - Touch settings 92 | // ----------------------------- 93 | 94 | /// The lowest rating that user can set by touching the stars. 95 | public var minTouchRating: Double = CosmosDefaultSettings.minTouchRating 96 | 97 | /// Set to `false` if you don't want to pass touches to superview (can be useful in a table view). 98 | public var passTouchesToSuperview = CosmosDefaultSettings.passTouchesToSuperview 99 | 100 | /// When `true` the star fill level is updated when user touches the cosmos view. When `false` the Cosmos view only shows the rating and does not act as the input control. 101 | public var updateOnTouch = CosmosDefaultSettings.updateOnTouch 102 | 103 | /// Set to `true` if you want to ignore pan gestures (can be useful when presented modally with a `presentationStyle` of `pageSheet` to avoid competing with the dismiss gesture) 104 | public var disablePanGestures = CosmosDefaultSettings.disablePanGestures 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #if !__has_feature(nullability) 6 | #define NS_ASSUME_NONNULL_BEGIN 7 | #define NS_ASSUME_NONNULL_END 8 | #define nullable 9 | #endif 10 | 11 | #import 12 | 13 | #import "XCDYouTubeOperation.h" 14 | #import "XCDYouTubeVideo.h" 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | /** 19 | * XCDYouTubeVideoOperation is a subclass of `NSOperation` that connects to the YouTube API and parse the response. 20 | * 21 | * You should probably use the higher level class ``. Use this class only if you are very familiar with `NSOperation` and need to manage dependencies between operations. 22 | */ 23 | @interface XCDYouTubeVideoOperation : NSOperation 24 | 25 | /** 26 | * ------------------ 27 | * @name Initializing 28 | * ------------------ 29 | */ 30 | 31 | /** 32 | * Initializes a video operation with the specified video identifier and language identifier. 33 | * 34 | * @param videoIdentifier A 11 characters YouTube video identifier. 35 | * @param languageIdentifier An [ISO 639-1 two-letter language code](http://www.loc.gov/standards/iso639-2/php/code_list.php) used for error localization. If you pass a nil language identifier then English (`en`) is used. 36 | * 37 | * @return An initialized `XCDYouTubeVideoOperation` object. 38 | */ 39 | - (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(nullable NSString *)languageIdentifier; 40 | 41 | /** 42 | * Initializes a video operation with the specified video identifier and language identifier and cookies. 43 | * 44 | * @param videoIdentifier A 11 characters YouTube video identifier. 45 | * @param languageIdentifier An [ISO 639-1 two-letter language code](http://www.loc.gov/standards/iso639-2/php/code_list.php) used for error localization. If you pass a nil language identifier then English (`en`) is used. 46 | * @param cookies An array of `NSHTTPCookie` objects, can be nil. These cookies can be used for certain videos that require a login. 47 | * 48 | * @return An initialized `XCDYouTubeVideoOperation` object. 49 | */ 50 | - (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(NSString *)languageIdentifier cookies:(nullable NSArray *)cookies; 51 | 52 | /** 53 | * Initializes a video operation with the specified video identifier and language identifier and cookies and additional patterns. 54 | * 55 | * @param videoIdentifier A 11 characters YouTube video identifier. 56 | * @param languageIdentifier An [ISO 639-1 two-letter language code](http://www.loc.gov/standards/iso639-2/php/code_list.php) used for error localization. If you pass a nil language identifier then English (`en`) is used. 57 | * @param cookies An array of `NSHTTPCookie` objects, can be nil. These cookies can be used for certain videos that require a login. 58 | * @param customPatterns An array of `NSString` objects, can be nil. These patterns can be used to create custom regular expression objects in favor of the internal hard-coded patterns for video parsing. If none of these patterns produces a valid `NSRegularExpression` object then the internal hard-coded regular expression objects are used. Typically, you do not need to use this parameter, however, it can be used a way to use update patterns when needed (i.e to adapt to YouTube API changes). See https://github.com/0xced/XCDYouTubeKit/blob/master/REGULAR_EXPRESSION.md for more info. 59 | * 60 | * @return An initialized `XCDYouTubeVideoOperation` object. 61 | */ 62 | - (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(nullable NSString *)languageIdentifier cookies:(nullable NSArray *)cookies customPatterns:(nullable NSArray *)customPatterns NS_DESIGNATED_INITIALIZER; 63 | 64 | /** 65 | * -------------------------------- 66 | * @name Accessing operation result 67 | * -------------------------------- 68 | */ 69 | 70 | /** 71 | * Returns an error of the `XCDYouTubeVideoErrorDomain` domain if the operation failed or nil if it succeeded. 72 | * 73 | * Returns nil if the operation is not yet finished or if it was canceled. 74 | */ 75 | @property (atomic, readonly, nullable) NSError *error; 76 | /** 77 | * Returns a video object if the operation succeeded or nil if it failed. 78 | * 79 | * Returns nil if the operation is not yet finished or if it was canceled. 80 | */ 81 | @property (atomic, readonly, nullable) XCDYouTubeVideo *video; 82 | 83 | @end 84 | 85 | NS_ASSUME_NONNULL_END 86 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import 6 | 7 | /** 8 | * The [context][1] used when logging with CocoaLumberjack. 9 | * 10 | * [1]: https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/CustomContext.md 11 | */ 12 | extern const NSInteger XCDYouTubeKitLumberjackContext; 13 | 14 | /** 15 | * The log levels, closely mirroring the log levels of CocoaLumberjack. 16 | */ 17 | typedef NS_ENUM(NSUInteger, XCDLogLevel) { 18 | /** 19 | * Used when an error is produced, e.g. when a `` finishes with an error. 20 | */ 21 | XCDLogLevelError = 0, 22 | 23 | /** 24 | * Used on unusual conditions that may eventually lead to an error. 25 | */ 26 | XCDLogLevelWarning = 1, 27 | 28 | /** 29 | * Used when logging normal operational information, e.g. when a `` starts, is cancelled or finishes. 30 | */ 31 | XCDLogLevelInfo = 2, 32 | 33 | /** 34 | * Used throughout a `` for debugging purpose, e.g. for HTTP requests. 35 | */ 36 | XCDLogLevelDebug = 3, 37 | 38 | /** 39 | * Used to report large amount of information, e.g. full HTTP responses. 40 | */ 41 | XCDLogLevelVerbose = 4, 42 | }; 43 | 44 | /** 45 | * You can use the `XCDYouTubeLogger` class to configure how the XCDYouTubeKit framework emits logs. 46 | * 47 | * By default, logs are emitted through CocoaLumberjack if it is available, i.e. if the `DDLog` class is found at runtime. 48 | * The [context][1] used for CocoaLumberjack is the `XCDYouTubeKitLumberjackContext` constant whose value is `(NSInteger)0xced70676`. 49 | * 50 | * If CocoaLumberjack is not available, logs are emitted with `NSLog`, prefixed with the `[XCDYouTubeKit]` string. 51 | * 52 | * ## Controlling log levels 53 | * 54 | * If you are using CocoaLumberjack, you are responsible for controlling the log levels with the CocoaLumberjack APIs. 55 | * 56 | * If you are not using CocoaLumberjack, you can control the log levels with the `XCDYouTubeKitLogLevel` environment variable. See also the `` enum. 57 | * 58 | * Level | Value | Mask 59 | * --------|-------|------ 60 | * Error | 0 | 0x01 61 | * Warning | 1 | 0x02 62 | * Info | 2 | 0x04 63 | * Debug | 3 | 0x08 64 | * Verbose | 4 | 0x10 65 | * 66 | * Use the corresponding bitmask to combine levels. For example, if you want to log *error*, *warning* and *info* levels, set the `XCDYouTubeKitLogLevel` environment variable to `0x7` (0x01 | 0x02 | 0x04). 67 | * 68 | * If you do not set the `XCDYouTubeKitLogLevel` environment variable, only warning and error levels are logged. 69 | * 70 | * [1]: https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/CustomContext.md 71 | */ 72 | @interface XCDYouTubeLogger : NSObject 73 | 74 | /** 75 | * ------------------- 76 | * @name Custom Logger 77 | * ------------------- 78 | */ 79 | 80 | /** 81 | * If you prefer not to use CocoaLumberjack and want something more advanced than the default `NSLog` implementation, you can use this method to write your own logger. 82 | * 83 | * @param logHandler The block called when a log is emitted by the XCDYouTubeKit framework. If you set the log handler to nil, logging will be completely disabled. 84 | * 85 | * @discussion Here is a description of the log handler parameters. 86 | * 87 | * - The `message` parameter is a block returning a string that you must call to evaluate the log message. 88 | * - The `level` parameter is the log level of the message, see ``. 89 | * - The `file` parameter is the full path of the file, captured with the `__FILE__` macro where the log is emitted. 90 | * - The `function` parameter is the function name, captured with the `__PRETTY_FUNCTION__` macro where the log is emitted. 91 | * - The `line` parameter is the line number, captured with the `__LINE__` macro where the log is emitted. 92 | * 93 | * Here is how you could implement a custom log handler with [NSLogger](https://github.com/fpillet/NSLogger): 94 | * 95 | * ``` 96 | * [XCDYouTubeLogger setLogHandler:^(NSString * (^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line) { 97 | * LogMessageRawF(file, (int)line, function, @"XCDYouTubeKit", (int)level, message()); 98 | * }]; 99 | * ``` 100 | */ 101 | + (void) setLogHandler:(void (^)(NSString * (^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line))logHandler; 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /test/NotificationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationViewController.swift 3 | // test 4 | // 5 | // Created by Alfian Losari on 10/02/20. 6 | // Copyright © 2020 alfianlosari. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVKit 11 | import UserNotifications 12 | import UserNotificationsUI 13 | import XCDYouTubeKit 14 | import Cosmos 15 | 16 | class NotificationViewController: UIViewController, UNNotificationContentExtension { 17 | 18 | @IBOutlet weak var playerView: UIView! 19 | @IBOutlet weak var reviewStackView: UIStackView! 20 | @IBOutlet weak var reviewButton: UIButton! 21 | @IBOutlet weak var videoTitleLabel: UILabel! 22 | @IBOutlet weak var videoDescriptionLabel: UILabel! 23 | @IBOutlet weak var submitLabel: UILabel! 24 | @IBOutlet weak var subscribeButton: UIButton! 25 | @IBOutlet weak var favoriteButton: UIButton! 26 | 27 | var playerController: AVPlayerViewController! 28 | let standardHeight: CGFloat = 432 29 | let reviewHeight: CGFloat = 658 30 | 31 | var isSubscribed = false { 32 | didSet { 33 | self.subscribeButton.tintColor = self.isSubscribed ? UIColor.systemGray : UIColor.systemBlue 34 | self.subscribeButton.setTitle(self.isSubscribed ? " Added" : " Add", for: .normal) 35 | } 36 | } 37 | 38 | var isFavorited = false { 39 | didSet { 40 | self.favoriteButton.tintColor = self.isFavorited ? UIColor.systemGray : UIColor.systemBlue 41 | self.favoriteButton.setTitle(self.isFavorited ? " Favorited" : " Favorite", for: .normal) 42 | } 43 | } 44 | 45 | override func viewDidLoad() { 46 | super.viewDidLoad() 47 | subscribeButton.setImage(UIImage(systemName: "calendar"), for: .normal) 48 | favoriteButton.setImage(UIImage(systemName: "star"), for: .normal) 49 | reviewButton.setImage(UIImage(systemName: "pencil"), for: .normal) 50 | 51 | reviewStackView.isHidden = true 52 | submitLabel.isHidden = true 53 | } 54 | 55 | func didReceive(_ notification: UNNotification) { 56 | playerController = AVPlayerViewController() 57 | preferredContentSize.height = standardHeight 58 | videoTitleLabel.text = notification.request.content.body 59 | videoDescriptionLabel.text = notification.request.content.userInfo["description"] as? String ?? "" 60 | 61 | guard let videoId = notification.request.content.userInfo["videoId"] as? String else { 62 | self.preferredContentSize.height = 100 63 | return 64 | } 65 | 66 | XCDYouTubeClient.default().getVideoWithIdentifier(videoId) { [weak self] (video, error) in 67 | guard let self = self else { return } 68 | 69 | if let error = error { 70 | print(error.localizedDescription) 71 | return 72 | } 73 | 74 | guard let video = video else { 75 | return 76 | } 77 | 78 | let streamURLS = video.streamURLs 79 | if let url = streamURLS[XCDYouTubeVideoQuality.medium360] ?? streamURLS[XCDYouTubeVideoQuality.small240] ?? streamURLS[XCDYouTubeVideoQuality.HD720] ?? streamURLS[18] { 80 | self.setupPlayer(with: url) 81 | } 82 | } 83 | } 84 | 85 | private func setupPlayer(with url: URL) { 86 | guard let playerController = self.playerController else { 87 | return 88 | } 89 | 90 | let player = AVPlayer(url: url) 91 | playerController.player = player 92 | playerController.view.frame = self.playerView.bounds 93 | playerView.addSubview(playerController.view) 94 | addChild(playerController) 95 | playerController.didMove(toParent: self) 96 | player.play() 97 | } 98 | 99 | @IBAction func submitTapped(_ sender: Any) { 100 | UIView.animate(withDuration: 0.3) { 101 | self.reviewStackView.isHidden = true 102 | self.submitLabel.isHidden = false 103 | self.preferredContentSize.height = self.standardHeight 104 | } 105 | } 106 | 107 | @IBAction func reviewTapped() { 108 | UIView.animate(withDuration: 0.3) { 109 | self.preferredContentSize.height = self.reviewHeight 110 | self.reviewStackView.isHidden = false 111 | self.reviewButton.isHidden = true 112 | } 113 | } 114 | 115 | @IBAction func subscribeTapped(_ sender: Any) { 116 | self.isSubscribed.toggle() 117 | } 118 | 119 | @IBAction func favoriteTapped(_ sender: Any) { 120 | self.isFavorited.toggle() 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/CosmosLayers.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | 4 | /** 5 | 6 | Colection of helper functions for creating star layers. 7 | 8 | */ 9 | class CosmosLayers { 10 | /** 11 | 12 | Creates the layers for the stars. 13 | 14 | - parameter rating: The decimal number representing the rating. Usually a number between 1 and 5 15 | - parameter settings: Star view settings. 16 | - returns: Array of star layers. 17 | 18 | */ 19 | class func createStarLayers(_ rating: Double, settings: CosmosSettings, isRightToLeft: Bool) -> [CALayer] { 20 | 21 | var ratingRemander = CosmosRating.numberOfFilledStars(rating, 22 | totalNumberOfStars: settings.totalStars) 23 | 24 | var starLayers = [CALayer]() 25 | 26 | for _ in (0.. CALayer { 54 | 55 | if starFillLevel >= 1 { 56 | return createStarLayer(true, settings: settings) 57 | } 58 | 59 | if starFillLevel == 0 { 60 | return createStarLayer(false, settings: settings) 61 | } 62 | 63 | return createPartialStar(starFillLevel, settings: settings, isRightToLeft: isRightToLeft) 64 | } 65 | 66 | /** 67 | 68 | Creates a partially filled star layer with two sub-layers: 69 | 70 | 1. The layer for the filled star on top. The fill level parameter determines the width of this layer. 71 | 2. The layer for the empty star below. 72 | 73 | - parameter starFillLevel: Decimal number between 0 and 1 describing the star fill level. 74 | - parameter settings: Star view settings. 75 | 76 | - returns: Layer that contains the partially filled star. 77 | 78 | */ 79 | class func createPartialStar(_ starFillLevel: Double, settings: CosmosSettings, isRightToLeft: Bool) -> CALayer { 80 | let filledStar = createStarLayer(true, settings: settings) 81 | let emptyStar = createStarLayer(false, settings: settings) 82 | 83 | 84 | let parentLayer = CALayer() 85 | parentLayer.contentsScale = UIScreen.main.scale 86 | parentLayer.bounds = CGRect(origin: CGPoint(), size: filledStar.bounds.size) 87 | parentLayer.anchorPoint = CGPoint() 88 | parentLayer.addSublayer(emptyStar) 89 | parentLayer.addSublayer(filledStar) 90 | 91 | if isRightToLeft { 92 | // Flip the star horizontally for a right-to-left language 93 | let rotation = CATransform3DMakeRotation(CGFloat(Double.pi), 0, 1, 0) 94 | filledStar.transform = CATransform3DTranslate(rotation, -filledStar.bounds.size.width, 0, 0) 95 | } 96 | 97 | // Make filled layer width smaller according to the fill level 98 | filledStar.bounds.size.width *= CGFloat(starFillLevel) 99 | 100 | return parentLayer 101 | } 102 | 103 | private class func createStarLayer(_ isFilled: Bool, settings: CosmosSettings) -> CALayer { 104 | if let image = isFilled ? settings.filledImage : settings.emptyImage { 105 | // Create a layer that shows a star from an image 106 | return StarLayer.create(image: image, size: settings.starSize) 107 | } 108 | 109 | // Create a layer that draws a star from an array of points 110 | 111 | let fillColor = isFilled ? settings.filledColor : settings.emptyColor 112 | let strokeColor = isFilled ? settings.filledBorderColor : settings.emptyBorderColor 113 | 114 | return StarLayer.create(settings.starPoints, 115 | size: settings.starSize, 116 | lineWidth: isFilled ? settings.filledBorderWidth : settings.emptyBorderWidth, 117 | fillColor: fillColor, 118 | strokeColor: strokeColor) 119 | } 120 | 121 | /** 122 | 123 | Positions the star layers one after another with a margin in between. 124 | 125 | - parameter layers: The star layers array. 126 | - parameter starMargin: Margin between stars. 127 | 128 | */ 129 | class func positionStarLayers(_ layers: [CALayer], starMargin: Double) { 130 | var positionX:CGFloat = 0 131 | 132 | for layer in layers { 133 | layer.position.x = positionX 134 | positionX += layer.bounds.width + CGFloat(starMargin) 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Pods/Cosmos/Cosmos/StarLayer.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** 4 | 5 | Creates a layer with a single star in it. 6 | 7 | */ 8 | struct StarLayer { 9 | /** 10 | 11 | Creates a square layer with given size and draws the star shape in it. 12 | 13 | - parameter starPoints: Array of points for drawing a closed shape. The size of enclosing rectangle is 100 by 100. 14 | 15 | - parameter size: The width and height of the layer. The star shape is scaled to fill the size of the layer. 16 | 17 | - parameter lineWidth: The width of the star stroke. 18 | 19 | - parameter fillColor: Star shape fill color. Fill color is invisible if it is a clear color. 20 | 21 | - parameter strokeColor: Star shape stroke color. Stroke is invisible if it is a clear color. 22 | 23 | - returns: New layer containing the star shape. 24 | 25 | */ 26 | static func create(_ starPoints: [CGPoint], size: Double, 27 | lineWidth: Double, fillColor: UIColor, strokeColor: UIColor) -> CALayer { 28 | 29 | let containerLayer = createContainerLayer(size) 30 | let path = createStarPath(starPoints, size: size, lineWidth: lineWidth) 31 | 32 | let shapeLayer = createShapeLayer(path.cgPath, lineWidth: lineWidth, 33 | fillColor: fillColor, strokeColor: strokeColor, size: size) 34 | 35 | containerLayer.addSublayer(shapeLayer) 36 | 37 | return containerLayer 38 | } 39 | 40 | /** 41 | 42 | Creates the star layer from an image 43 | 44 | - parameter image: a star image to be shown. 45 | 46 | - parameter size: The width and height of the layer. The image is scaled to fit the layer. 47 | 48 | */ 49 | static func create(image: UIImage, size: Double) -> CALayer { 50 | let containerLayer = createContainerLayer(size) 51 | let imageLayer = createContainerLayer(size) 52 | 53 | containerLayer.addSublayer(imageLayer) 54 | imageLayer.contents = image.cgImage 55 | imageLayer.contentsGravity = CALayerContentsGravity.resizeAspect 56 | 57 | return containerLayer 58 | } 59 | 60 | /** 61 | 62 | Creates the star shape layer. 63 | 64 | - parameter path: The star shape path. 65 | 66 | - parameter lineWidth: The width of the star stroke. 67 | 68 | - parameter fillColor: Star shape fill color. Fill color is invisible if it is a clear color. 69 | 70 | - parameter strokeColor: Star shape stroke color. Stroke is invisible if it is a clear color. 71 | 72 | - returns: New shape layer. 73 | 74 | */ 75 | static func createShapeLayer(_ path: CGPath, lineWidth: Double, fillColor: UIColor, 76 | strokeColor: UIColor, size: Double) -> CALayer { 77 | 78 | let layer = CAShapeLayer() 79 | layer.anchorPoint = CGPoint() 80 | layer.contentsScale = UIScreen.main.scale 81 | layer.strokeColor = strokeColor.cgColor 82 | layer.fillColor = fillColor.cgColor 83 | layer.lineWidth = CGFloat(lineWidth) 84 | layer.bounds.size = CGSize(width: size, height: size) 85 | layer.masksToBounds = true 86 | layer.path = path 87 | layer.isOpaque = true 88 | return layer 89 | } 90 | 91 | /** 92 | 93 | Creates a layer that will contain the shape layer. 94 | 95 | - returns: New container layer. 96 | 97 | */ 98 | static func createContainerLayer(_ size: Double) -> CALayer { 99 | let layer = CALayer() 100 | layer.contentsScale = UIScreen.main.scale 101 | layer.anchorPoint = CGPoint() 102 | layer.masksToBounds = true 103 | layer.bounds.size = CGSize(width: size, height: size) 104 | layer.isOpaque = true 105 | return layer 106 | } 107 | 108 | /** 109 | 110 | Creates a path for the given star points and size. The star points specify a shape of size 100 by 100. The star shape will be scaled if the size parameter is not 100. For exampe, if size parameter is 200 the shape will be scaled by 2. 111 | 112 | - parameter starPoints: Array of points for drawing a closed shape. The size of enclosing rectangle is 100 by 100. 113 | 114 | - parameter size: Specifies the size of the shape to return. 115 | 116 | - returns: New shape path. 117 | 118 | */ 119 | static func createStarPath(_ starPoints: [CGPoint], size: Double, 120 | lineWidth: Double) -> UIBezierPath { 121 | 122 | let lineWidthLocal = lineWidth + ceil(lineWidth * 0.3) 123 | let sizeWithoutLineWidth = size - lineWidthLocal * 2 124 | 125 | let points = scaleStar(starPoints, factor: sizeWithoutLineWidth / 100, 126 | lineWidth: lineWidthLocal) 127 | 128 | let path = UIBezierPath() 129 | path.move(to: points[0]) 130 | let remainingPoints = Array(points[1.. [CGPoint] { 152 | return starPoints.map { point in 153 | return CGPoint( 154 | x: point.x * CGFloat(factor) + CGFloat(lineWidth), 155 | y: point.y * CGFloat(factor) + CGFloat(lineWidth) 156 | ) 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #if !__has_feature(nullability) 6 | #define NS_ASSUME_NONNULL_BEGIN 7 | #define NS_ASSUME_NONNULL_END 8 | #define nullable 9 | #define null_resettable 10 | #endif 11 | 12 | #import 13 | 14 | #if TARGET_OS_IOS 15 | 16 | #import 17 | 18 | NS_ASSUME_NONNULL_BEGIN 19 | 20 | /** 21 | * ------------------- 22 | * @name Notifications 23 | * ------------------- 24 | */ 25 | 26 | /** 27 | * NSError key for the `MPMoviePlayerPlaybackDidFinishNotification` userInfo dictionary. 28 | * 29 | * Ideally, there should be a `MPMoviePlayerPlaybackDidFinishErrorUserInfoKey` declared near to `MPMoviePlayerPlaybackDidFinishReasonUserInfoKey` in MPMoviePlayerController.h but since it doesn't exist, here is a convenient constant key. 30 | */ 31 | MP_EXTERN NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey; 32 | 33 | /** 34 | * Posted when the video player has received the video information. The `object` of the notification is the `XCDYouTubeVideoPlayerViewController` instance. The `userInfo` dictionary contains the `XCDYouTubeVideo` object. 35 | */ 36 | MP_EXTERN NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification; 37 | /** 38 | * The key for the `XCDYouTubeVideo` object in the user info dictionary of `XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification`. 39 | */ 40 | MP_EXTERN NSString *const XCDYouTubeVideoUserInfoKey; 41 | 42 | /** 43 | * A subclass of `MPMoviePlayerViewController` for playing YouTube videos. 44 | * 45 | * Use UIViewController’s `presentMoviePlayerViewControllerAnimated:` method to play a YouTube video fullscreen. 46 | * 47 | * Use the `` method to play a YouTube video inline. 48 | */ 49 | #pragma clang diagnostic push 50 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 51 | DEPRECATED_MSG_ATTRIBUTE("Use `AVFoundation` or `AVKit` APIs instead.") 52 | @interface XCDYouTubeVideoPlayerViewController : MPMoviePlayerViewController 53 | #pragma clang diagnostic pop 54 | 55 | /** 56 | * ------------------ 57 | * @name Initializing 58 | * ------------------ 59 | */ 60 | 61 | /** 62 | * Initializes a YouTube video player view controller 63 | * 64 | * @param videoIdentifier A 11 characters YouTube video identifier. If the video identifier is invalid the `MPMoviePlayerPlaybackDidFinishNotification` will be posted with a `MPMovieFinishReasonPlaybackError` reason. 65 | * 66 | * @return An initialized YouTube video player view controller with the specified video identifier. 67 | * 68 | * @discussion You can pass a nil *videoIdentifier* (or use the standard `init` method instead) and set the `` property later. 69 | */ 70 | - (instancetype) initWithVideoIdentifier:(nullable NSString *)videoIdentifier NS_DESIGNATED_INITIALIZER; 71 | 72 | /** 73 | * ------------------------------------ 74 | * @name Accessing the video identifier 75 | * ------------------------------------ 76 | */ 77 | 78 | /** 79 | * The 11 characters YouTube video identifier. 80 | */ 81 | @property (nonatomic, copy, nullable) NSString *videoIdentifier; 82 | 83 | /** 84 | * ------------------------------------------ 85 | * @name Defining the preferred video quality 86 | * ------------------------------------------ 87 | */ 88 | 89 | /** 90 | * The preferred order for the quality of the video to play. Plays the first match when multiple video streams are available. 91 | * 92 | * Defaults to @[ XCDYouTubeVideoQualityHTTPLiveStreaming, @(XCDYouTubeVideoQualityHD720), @(XCDYouTubeVideoQualityMedium360), @(XCDYouTubeVideoQualitySmall240) ] 93 | * 94 | * You should set this property right after calling the `` method. Setting this property to nil restores its default values. 95 | * 96 | * @see XCDYouTubeVideoQuality 97 | */ 98 | @property (nonatomic, copy, null_resettable) NSArray *preferredVideoQualities; 99 | 100 | /** 101 | * ------------------------ 102 | * @name Presenting a video 103 | * ------------------------ 104 | */ 105 | 106 | /** 107 | * Present the video inside a view. 108 | * 109 | * @param view The view inside which you want to present the video. 110 | * 111 | * @discussion The video view is added as a subview of the specified view. The video does not start playing immediately, you have to call `[videoPlayerViewController.moviePlayer play]` for playback to start. See `MPMoviePlayerController` documentation for more information. 112 | * 113 | * Ownership of the XCDYouTubeVideoPlayerViewController instance is transferred to the view. 114 | */ 115 | - (void) presentInView:(UIView *)view; 116 | 117 | @end 118 | 119 | /** 120 | * ------------------------------ 121 | * @name Deprecated notifications 122 | * ------------------------------ 123 | */ 124 | MP_EXTERN NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification instead."); 125 | MP_EXTERN NSString *const XCDMetadataKeyTitle DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); 126 | MP_EXTERN NSString *const XCDMetadataKeySmallThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); 127 | MP_EXTERN NSString *const XCDMetadataKeyMediumThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); 128 | MP_EXTERN NSString *const XCDMetadataKeyLargeThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); 129 | 130 | NS_ASSUME_NONNULL_END 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #if !__has_feature(nullability) 6 | #define NS_ASSUME_NONNULL_BEGIN 7 | #define NS_ASSUME_NONNULL_END 8 | #define nullable 9 | #endif 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | /** 16 | * The quality of YouTube videos. These values are used as keys in the `<[XCDYouTubeVideo streamURLs]>` property. 17 | * 18 | * The constant numbers are the YouTube [itag](https://en.wikipedia.org/wiki/Talk:YouTube/Archive_22#Moved_from_YouTube#Quality_formats) values. 19 | */ 20 | typedef NS_ENUM(NSUInteger, XCDYouTubeVideoQuality) { 21 | /** 22 | * Video: 240p MPEG-4 Visual | 0.175 Mbit/s 23 | * Audio: AAC | 36 kbit/s 24 | */ 25 | XCDYouTubeVideoQualitySmall240 = 36, 26 | 27 | /** 28 | * Video: 360p H.264 | 0.5 Mbit/s 29 | * Audio: AAC | 96 kbit/s 30 | */ 31 | XCDYouTubeVideoQualityMedium360 = 18, 32 | 33 | /** 34 | * Video: 720p H.264 | 2-3 Mbit/s 35 | * Audio: AAC | 192 kbit/s 36 | */ 37 | XCDYouTubeVideoQualityHD720 = 22, 38 | 39 | /** 40 | * Video: 1080p H.264 | 3–5.9 Mbit/s 41 | * Audio: AAC | 192 kbit/s 42 | * 43 | * @deprecated YouTube has removed 1080p mp4 videos. 44 | */ 45 | XCDYouTubeVideoQualityHD1080 DEPRECATED_MSG_ATTRIBUTE("YouTube has removed 1080p mp4 videos.") = 37, 46 | }; 47 | 48 | /** 49 | * Used as a key in the `streamURLs` property of the `XCDYouTubeVideo` class for live videos. 50 | */ 51 | extern NSString *const XCDYouTubeVideoQualityHTTPLiveStreaming; 52 | 53 | /** 54 | * Represents a YouTube video. Use the `<-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]>` method to obtain a `XCDYouTubeVideo` object. 55 | */ 56 | @interface XCDYouTubeVideo : NSObject 57 | 58 | /** 59 | * -------------------------------- 60 | * @name Accessing video properties 61 | * -------------------------------- 62 | */ 63 | 64 | /** 65 | * The 11 characters YouTube video identifier. 66 | */ 67 | @property (nonatomic, readonly) NSString *identifier; 68 | /** 69 | * The title of the video. 70 | */ 71 | @property (nonatomic, readonly) NSString *title; 72 | /** 73 | * The duration of the video in seconds. 74 | */ 75 | @property (nonatomic, readonly) NSTimeInterval duration; 76 | /** 77 | * The views count of the video. 78 | */ 79 | @property (nonatomic, readonly) NSInteger viewCount; 80 | /** 81 | * A thumbnail URL for an image of small size, i.e. 120×90. May be nil. 82 | */ 83 | @property (nonatomic, readonly, nullable) NSURL *thumbnailURL; 84 | /** 85 | * A thumbnail URL for an image of small size, i.e. 120×90. May be nil. 86 | */ 87 | @property (nonatomic, readonly, nullable) NSURL *smallThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Renamed. Use `thumbnailURL` instead."); 88 | /** 89 | * A thumbnail URL for an image of medium size, i.e. 320×180, 480×360 or 640×480. May be nil. 90 | */ 91 | @property (nonatomic, readonly, nullable) NSURL *mediumThumbnailURL DEPRECATED_MSG_ATTRIBUTE("No longer available. Use `thumbnailURL` instead."); 92 | /** 93 | * A thumbnail URL for an image of large size, i.e. 1'280×720 or 1'980×1'080. May be nil. 94 | */ 95 | @property (nonatomic, readonly, nullable) NSURL *largeThumbnailURL DEPRECATED_MSG_ATTRIBUTE("No longer available. Use `thumbnailURL` instead."); 96 | 97 | /** 98 | * A dictionary of video stream URLs. 99 | * 100 | * The keys are the YouTube [itag](https://en.wikipedia.org/wiki/YouTube#Quality_and_formats) values as `NSNumber` objects. The values are the video URLs as `NSURL` objects. There is also the special `XCDYouTubeVideoQualityHTTPLiveStreaming` key for live videos. 101 | * 102 | * You should not store the URLs for later use since they have a limited lifetime and are bound to an IP address. 103 | * 104 | * @see XCDYouTubeVideoQuality 105 | * @see expirationDate 106 | */ 107 | #if __has_feature(objc_generics) 108 | @property (nonatomic, readonly) NSDictionary *streamURLs; 109 | #else 110 | @property (nonatomic, readonly) NSDictionary *streamURLs; 111 | #endif 112 | 113 | /** 114 | 115 | * A streamURL that is compatible on Apple devices. 116 | * The `streamURLs` may contain both video and audio streams, some video streams do not contain any audio. This property will return a video stream that contains both audio and video with a maximum video quality of 720p in the case of videos that aren't live. Also, this properly will return the URL to a live stream in the case of live videos. 117 | */ 118 | @property (nonatomic, readonly) NSURL *streamURL; 119 | 120 | /** 121 | 122 | * A dictionary of caption URLs (XML). 123 | * 124 | * The keys are the [language codes](https://www.loc.gov/standards/iso639-2/php/code_list.php) values as `NSString` objects. The values are the caption URLs as `NSURL` objects. 125 | * 126 | * You should not store the URLs for later use since they have a limited lifetime. 127 | */ 128 | #if __has_feature(objc_generics) 129 | @property (nonatomic, readonly, nullable) NSDictionary *captionURLs; 130 | #else 131 | @property (nonatomic, readonly, nullable) NSDictionary *captionURLs; 132 | #endif 133 | 134 | /** 135 | 136 | * A dictionary of auto generated caption URLs (XML). 137 | * 138 | * The keys are the [language codes](https://www.loc.gov/standards/iso639-2/php/code_list.php) values as `NSString` objects. The values are the caption URLs as `NSURL` objects. These URLs are the ones that were automatically generated by YouTube. 139 | * 140 | * You should not store the URLs for later use since they have a limited lifetime. 141 | */ 142 | 143 | #if __has_feature(objc_generics) 144 | @property (nonatomic, readonly, nullable) NSDictionary *autoGeneratedCaptionURLs; 145 | #else 146 | @property (nonatomic, readonly, nullable) NSDictionary *autoGeneratedCaptionURLs; 147 | #endif 148 | 149 | /** 150 | * The expiration date of the video. 151 | * 152 | * After this date, the stream URLs will not be playable. May be nil if it can not be determined, for example in live videos. 153 | */ 154 | @property (nonatomic, readonly, nullable) NSDate *expirationDate; 155 | 156 | @end 157 | 158 | NS_ASSUME_NONNULL_END 159 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import "XCDYouTubeVideoWebpage.h" 6 | 7 | @interface XCDYouTubeVideoWebpage () 8 | @property (nonatomic, readonly) NSString *html; 9 | @end 10 | 11 | @implementation XCDYouTubeVideoWebpage 12 | 13 | @synthesize playerConfiguration = _playerConfiguration; 14 | @synthesize videoInfo = _videoInfo; 15 | @synthesize sts = _sts; 16 | @synthesize javaScriptPlayerURL = _javaScriptPlayerURL; 17 | @synthesize isAgeRestricted = _isAgeRestricted; 18 | @synthesize regionsAllowed = _regionsAllowed; 19 | 20 | - (instancetype) initWithHTMLString:(NSString *)html 21 | { 22 | if (!(self = [super init])) 23 | return nil; // LCOV_EXCL_LINE 24 | 25 | _html = html; 26 | 27 | return self; 28 | } 29 | 30 | - (NSDictionary *) playerConfiguration 31 | { 32 | if (!_playerConfiguration) 33 | { 34 | __block NSDictionary *playerConfigurationDictionary; 35 | NSRegularExpression *playerConfigRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"ytplayer.config\\s*=\\s*(\\{.*?\\});|[\\({]\\s*'PLAYER_CONFIG'[,:]\\s*(\\{.*?\\})\\s*(?:,'|\\))" options:NSRegularExpressionCaseInsensitive error:NULL]; 36 | [playerConfigRegularExpression enumerateMatchesInString:self.html options:(NSMatchingOptions)0 range:NSMakeRange(0, self.html.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) 37 | { 38 | for (NSUInteger i = 1; i < result.numberOfRanges; i++) 39 | { 40 | NSRange range = [result rangeAtIndex:i]; 41 | if (range.length == 0) 42 | continue; 43 | 44 | NSString *configString = [self.html substringWithRange:range]; 45 | NSData *configData = [configString dataUsingEncoding:NSUTF8StringEncoding]; 46 | NSDictionary *playerConfiguration = [NSJSONSerialization JSONObjectWithData:configData ?: [NSData new] options:(NSJSONReadingOptions)0 error:NULL]; 47 | if ([playerConfiguration isKindOfClass:[NSDictionary class]]) 48 | { 49 | playerConfigurationDictionary = playerConfiguration; 50 | *stop = YES; 51 | } 52 | } 53 | }]; 54 | _playerConfiguration = playerConfigurationDictionary; 55 | } 56 | return _playerConfiguration; 57 | } 58 | 59 | - (NSDictionary *) videoInfo 60 | { 61 | if (!_videoInfo) 62 | { 63 | NSDictionary *args = self.playerConfiguration[@"args"]; 64 | if ([args isKindOfClass:[NSDictionary class]]) 65 | { 66 | NSMutableDictionary *info = [NSMutableDictionary new]; 67 | [args enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) 68 | { 69 | if ([(NSObject *)value isKindOfClass:[NSString class]] || [(NSObject *)value isKindOfClass:[NSNumber class]]) 70 | info[key] = [(NSObject *)value description]; 71 | }]; 72 | _videoInfo = [info copy]; 73 | } 74 | } 75 | return _videoInfo; 76 | } 77 | 78 | - (NSString *)sts 79 | { 80 | if (!_sts) 81 | { 82 | NSString *sts = [(NSString *)self.playerConfiguration[@"sts"] description]; 83 | if (sts != nil) { 84 | _sts = sts; 85 | return _sts; 86 | } else { 87 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\"sts\"\\s*:\\s*(\\d+)" options:0 error:nil]; 88 | NSTextCheckingResult *result = [regex firstMatchInString:self.html options:(NSMatchingOptions)0 range:NSMakeRange(0, self.html.length)]; 89 | if (result.numberOfRanges < 2) 90 | return _sts; 91 | 92 | NSRange range = [result rangeAtIndex:1]; 93 | if (range.length == 0) 94 | return _sts; 95 | 96 | _sts = [self.html substringWithRange:range]; 97 | } 98 | } 99 | 100 | return _sts; 101 | } 102 | - (NSURL *) javaScriptPlayerURL 103 | { 104 | if (!_javaScriptPlayerURL) 105 | { 106 | NSString *jsAssets = [self.playerConfiguration valueForKeyPath:@"assets.js"]; 107 | if ([jsAssets isKindOfClass:[NSString class]]) 108 | { 109 | NSString *javaScriptPlayerURLString = jsAssets; 110 | if ([jsAssets hasPrefix:@"//"]) 111 | javaScriptPlayerURLString = [@"https:" stringByAppendingString:jsAssets]; 112 | else if ([jsAssets hasPrefix:@"/"]) 113 | javaScriptPlayerURLString = [@"https://www.youtube.com" stringByAppendingString:jsAssets]; 114 | 115 | _javaScriptPlayerURL = [NSURL URLWithString:javaScriptPlayerURLString]; 116 | } 117 | else 118 | { 119 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\"assets\":.+?\"js\":\\s*(\"[^\"]+\")" options:0 error:nil]; 120 | NSTextCheckingResult *result = [regex firstMatchInString:self.html options:(NSMatchingOptions)0 range:NSMakeRange(0, self.html.length)]; 121 | if (result.numberOfRanges < 2) 122 | return _javaScriptPlayerURL; 123 | 124 | NSRange range = [result rangeAtIndex:1]; 125 | if (range.length == 0) 126 | return _javaScriptPlayerURL; 127 | 128 | 129 | NSString *baseJSURLPathString = [[[self.html substringWithRange:range] stringByReplacingOccurrencesOfString:@"\\" withString:@""] stringByReplacingOccurrencesOfString:@"\"" withString:@""]; 130 | 131 | NSURLComponents *components = [NSURLComponents componentsWithString:baseJSURLPathString]; 132 | if (components == nil) 133 | return _javaScriptPlayerURL; 134 | 135 | if (components.scheme == nil || components.scheme.length == 0) 136 | components.scheme = @"https"; 137 | 138 | if (components.host == nil || components.host.length == 0) 139 | components.host = @"www.youtube.com"; 140 | 141 | if (components.URL == nil) 142 | return _javaScriptPlayerURL; 143 | 144 | _javaScriptPlayerURL = components.URL; 145 | } 146 | } 147 | return _javaScriptPlayerURL; 148 | } 149 | 150 | - (BOOL) isAgeRestricted 151 | { 152 | if (!_isAgeRestricted) 153 | { 154 | NSStringCompareOptions options = (NSStringCompareOptions)0; 155 | NSRange range = NSMakeRange(0, self.html.length); 156 | _isAgeRestricted = [self.html rangeOfString:@"og:restrictions:age" options:options range:range].location != NSNotFound || [self.html rangeOfString:@"player-age-gate-content" options:options range:range].location != NSNotFound; 157 | 158 | } 159 | return _isAgeRestricted; 160 | } 161 | 162 | - (NSSet *) regionsAllowed 163 | { 164 | if (!_regionsAllowed) 165 | { 166 | _regionsAllowed = [NSSet set]; 167 | NSRegularExpression *regionsAllowedRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"meta\\s+itemprop=\"regionsAllowed\"\\s+content=\"(.*)\"" options:(NSRegularExpressionOptions)0 error:NULL]; 168 | NSTextCheckingResult *regionsAllowedResult = [regionsAllowedRegularExpression firstMatchInString:self.html options:(NSMatchingOptions)0 range:NSMakeRange(0, self.html.length)]; 169 | if (regionsAllowedResult.numberOfRanges > 1) 170 | { 171 | NSString *regionsAllowed = [self.html substringWithRange:[regionsAllowedResult rangeAtIndex:1]]; 172 | if (regionsAllowed.length > 0) 173 | _regionsAllowed = [NSSet setWithArray:[regionsAllowed componentsSeparatedByString:@","]]; 174 | } 175 | } 176 | return _regionsAllowed; 177 | } 178 | 179 | @end 180 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import "XCDYouTubePlayerScript.h" 6 | 7 | #import 8 | 9 | #import "XCDYouTubeLogger+Private.h" 10 | 11 | @interface XCDYouTubePlayerScript () 12 | @property (nonatomic, strong) JSContext *context; 13 | @property (nonatomic, strong) JSValue *signatureFunction; 14 | @end 15 | 16 | @implementation XCDYouTubePlayerScript 17 | 18 | - (instancetype) initWithString:(NSString *)string customPatterns:(NSArray *)customPatterns 19 | { 20 | if (!(self = [super init])) 21 | return nil; // LCOV_EXCL_LINE 22 | 23 | _context = [JSContext new]; 24 | 25 | _context.exceptionHandler = ^(JSContext *context, JSValue *exception) { 26 | XCDYouTubeLogWarning(@"JavaScript exception: %@", exception); 27 | }; 28 | 29 | NSDictionary *environment = @{ 30 | @"document": @{ 31 | @"documentElement": @{} 32 | }, 33 | @"location": @{ 34 | @"hash": @"" 35 | }, 36 | @"navigator": @{ 37 | @"userAgent": @"" 38 | }, 39 | }; 40 | _context[@"window"] = @{}; 41 | _context [@"XMLHttpRequest"] = @{}; 42 | 43 | for (NSString *propertyName in environment) 44 | { 45 | JSValue *value = [JSValue valueWithObject:environment[propertyName] inContext:_context]; 46 | _context[propertyName] = value; 47 | _context[@"window"][propertyName] = value; 48 | } 49 | 50 | NSString *matchMediaJsFunction = @"var matchMediaWindow=this;matchMediaWindow.matchMedia=function(a){return false;};"; 51 | NSString *script = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 52 | script=[matchMediaJsFunction stringByAppendingString:(script)]; 53 | [_context evaluateScript:script]; 54 | 55 | NSRegularExpression *anonymousFunctionRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"\\(function\\(([^)]*)\\)\\{(.*)\\}\\)\\(([^)]*)\\)" options:NSRegularExpressionDotMatchesLineSeparators error:NULL]; 56 | NSTextCheckingResult *anonymousFunctionResult = [anonymousFunctionRegularExpression firstMatchInString:script options:(NSMatchingOptions)0 range:NSMakeRange(0, script.length)]; 57 | if (anonymousFunctionResult.numberOfRanges > 3) 58 | { 59 | NSArray *parameters = [[script substringWithRange:[anonymousFunctionResult rangeAtIndex:1]] componentsSeparatedByString:@","]; 60 | NSArray *arguments = [[script substringWithRange:[anonymousFunctionResult rangeAtIndex:3]] componentsSeparatedByString:@","]; 61 | if (parameters.count == arguments.count) 62 | { 63 | for (NSUInteger i = 0; i < parameters.count; i++) 64 | { 65 | _context[parameters[i]] = _context[arguments[i]]; 66 | } 67 | } 68 | NSString *anonymousFunctionBody = [script substringWithRange:[anonymousFunctionResult rangeAtIndex:2]]; 69 | [_context evaluateScript:anonymousFunctionBody]; 70 | } 71 | else 72 | { 73 | XCDYouTubeLogWarning(@"Unexpected player script (no anonymous function found)"); 74 | } 75 | 76 | //See list of regex patterns here https://github.com/ytdl-org/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L1344 77 | NSArray*hardCodedPatterns = @[ 78 | @"\\b[cs]\\s*&&\\s*[adf]\\.set\\([^,]+\\s*,\\s*encodeURIComponent\\s*\\(\\s*([a-zA-Z0-9$]+)\\(", 79 | @"\\b[a-zA-Z0-9]+\\s*&&\\s*[a-zA-Z0-9]+\\.set\\([^,]+\\s*,\\s*encodeURIComponent\\s*\\(\\s*([a-zA-Z0-9$]+)\\(", 80 | @"\\b([a-zA-Z0-9$]{2})\\s*=\\s*function\\(\\s*a\\s*\\)\\s*\\{\\s*a\\s*=\\s*a\\.split\\(\\s*\"\"\\s*\\)", 81 | @"([a-zA-Z0-9$]+)\\s*=\\s*function\\(\\s*a\\s*\\)\\s*\\{\\s*a\\s*=\\s*a\\.split\\(\\s*\"\"\\s*\\)", 82 | /*The rest patterns are supposed to be obsolete but I am keep them here in case some older pattern matches the YouTube API in the future 83 | *IMPORTANT: Please note that the patterns above should be placed in the same order as seen in youtube-dl here: https://github.com/ytdl-org/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L1344 84 | *If they do not match the same order some video won't play because of using the wrong signature. 85 | */ 86 | @"([\"\\\'])signature\\1\\s*,\\s*([a-zA-Z0-9$]+)\\(", 87 | @"\\.sig\\|\\|([a-zA-Z0-9$]+)\\(", 88 | @"yt\\.akamaized\\.net/\\)\\s*\\|\\|\\s*.*?\\s*[cs]\\s*&&\\s*[adf]\\.set\\([^,]+\\s*,\\s*(?:encodeURIComponent\\s*\\()?\\s*([a-zA-Z0-9$]+)\\(", 89 | @"\\b[cs]\\s*&&\\s*[adf]\\.set\\([^,]+\\s*,\\s*([a-zA-Z0-9$]+)\\(", 90 | @"\\b[a-zA-Z0-9]+\\s*&&\\s*[a-zA-Z0-9]+\\.set\\([^,]+\\s*,\\s*([a-zA-Z0-9$]+)\\(", 91 | @"\\bc\\s*&&\\s*a\\.set\\([^,]+\\s*,\\s*\\([^)]*\\)\\s*\\(\\s*([a-zA-Z0-9$]+)\\(", 92 | @"\\bc\\s*&&\\s*[a-zA-Z0-9]+\\.set\\([^,]+\\s*,\\s*\\([^)]*\\)\\s*\\(\\s*([a-zA-Z0-9$]+)\\(", 93 | @"\\bc\\s*&&\\s*[a-zA-Z0-9]+\\.set\\([^,]+\\s*,\\s*\\([^)]*\\)\\s*\\(\\s*([a-zA-Z0-9$]+)\\(" 94 | ]; 95 | 96 | NSArray *hardCodedPatternsExpressions = [self regularExpressionFromPatterns:hardCodedPatterns]; 97 | NSArray *customPatternsExpressions = [self regularExpressionFromPatterns:customPatterns]; 98 | 99 | NSMutableArray*validRegularExpressions = [NSMutableArray new]; 100 | 101 | for (NSRegularExpression *regularExpression in customPatternsExpressions.count == 0 ? hardCodedPatternsExpressions : customPatternsExpressions) { 102 | if (_signatureFunction) 103 | break; 104 | 105 | NSArray *regexResults = [regularExpression matchesInString:script options:(NSMatchingOptions)0 range:NSMakeRange(0, script.length)]; 106 | 107 | for (NSTextCheckingResult *signatureResult in regexResults) 108 | { 109 | NSString *signatureFunctionName = signatureResult.numberOfRanges > 1 ? [script substringWithRange:[signatureResult rangeAtIndex:1]] : nil; 110 | if (!signatureFunctionName) 111 | continue; 112 | 113 | JSValue *signatureFunction = self.context[signatureFunctionName]; 114 | if (signatureFunction.isObject) 115 | { 116 | _signatureFunction = signatureFunction; 117 | break; 118 | } 119 | } 120 | } 121 | 122 | if (!_signatureFunction) 123 | XCDYouTubeLogWarning(@"No signature function in player script: \n%@. Regular Expressions: \n%@", script, validRegularExpressions); 124 | 125 | return self; 126 | } 127 | 128 | - (NSString *) unscrambleSignature:(NSString *)scrambledSignature 129 | { 130 | if (!self.signatureFunction || !scrambledSignature) 131 | return nil; 132 | 133 | JSValue *unscrambledSignature = [self.signatureFunction callWithArguments:@[ scrambledSignature ]]; 134 | return [unscrambledSignature isString] ? [unscrambledSignature toString] : nil; 135 | } 136 | 137 | - (NSArray *)regularExpressionFromPatterns:(NSArray *)patterns 138 | { 139 | NSMutableArray*validRegularExpressions = [NSMutableArray new]; 140 | 141 | for (NSString *pattern in patterns) 142 | { 143 | NSError* error = NULL; 144 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error]; 145 | 146 | if (error) 147 | { 148 | XCDYouTubeLogWarning(@"Error when creating regular expression from the pattern: %@", pattern); 149 | continue; 150 | } 151 | 152 | if (regex != nil) 153 | { 154 | [validRegularExpressions addObject:regex]; 155 | } 156 | } 157 | 158 | return validRegularExpressions.copy; 159 | } 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | [![Build Status](https://img.shields.io/circleci/project/0xced/XCDYouTubeKit/develop.svg?style=flat)](https://circleci.com/gh/0xced/XCDYouTubeKit) 4 | [![Coverage Status](https://img.shields.io/codecov/c/github/0xced/XCDYouTubeKit/develop.svg?style=flat)](https://codecov.io/gh/0xced/XCDYouTubeKit/branch/develop) 5 | [![Platform](https://img.shields.io/cocoapods/p/XCDYouTubeKit.svg?style=flat)](http://cocoadocs.org/docsets/XCDYouTubeKit/) 6 | [![Pod Version](https://img.shields.io/cocoapods/v/XCDYouTubeKit.svg?style=flat)](https://cocoapods.org/pods/XCDYouTubeKit) 7 | [![Carthage Compatibility](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage/) 8 | [![Swift Package Manager Compatibility](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen)](ttps://swift.org/package-manager/) 9 | [![License](https://img.shields.io/cocoapods/l/XCDYouTubeKit.svg?style=flat)](LICENSE) 10 | 11 | **XCDYouTubeKit** is a YouTube video player for iOS, tvOS and macOS. 12 | 13 | 14 | 15 | Are you enjoying XCDYouTubeKit? You can say thank you with [a tweet](https://twitter.com/intent/tweet?text=%400xced%20Thank%20you%20for%20XCDYouTubeKit%2E). I am also accepting donations. ;-) 16 | 17 | [![Donate button](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MGEPRSNQFMV3W) 18 | 19 | ## Requirements 20 | 21 | - Runs on iOS 8.0 and later 22 | - Runs on macOS 10.9 and later 23 | - Runs on tvOS 9.0 and later 24 | 25 | ## Warning 26 | 27 | XCDYouTubeKit is against the YouTube [Terms of Service](https://www.youtube.com/t/terms). The only *official* way of playing a YouTube video inside an app is with a web view and the [iframe player API](https://developers.google.com/youtube/iframe_api_reference). Unfortunately, this is very slow and quite ugly, so I wrote this player to give users a better viewing experience. 28 | 29 | ## Installation 30 | 31 | XCDYouTubeKit is available through [CocoaPods](https://cocoapods.org/), [Carthage](https://github.com/Carthage/Carthage) and [Swift Package Manager](https://swift.org/package-manager/). 32 | 33 | CocoaPods: 34 | 35 | ```ruby 36 | pod "XCDYouTubeKit", "~> 2.9" 37 | ``` 38 | 39 | Carthage: 40 | 41 | ```objc 42 | github "0xced/XCDYouTubeKit" ~> 2.9 43 | ``` 44 | 45 | Swift Package Manager: 46 | 47 | Add `XCDYouTubeKit` to the dependencies value of your `Package.swift` 48 | 49 | ```swift 50 | dependencies: [ 51 | .package(url: "https://github.com/0xced/XCDYouTubeKit.git", from: "2.9.0") 52 | ] 53 | ``` 54 | 55 | Alternatively, you can manually use the provided static library or dynamic framework. In order to use the static library, you must: 56 | 57 | 1. Create a workspace (File → New → Workspace…) 58 | 2. Add your project to the workspace 59 | 3. Add the XCDYouTubeKit project to the workspace 60 | 4. Drag and drop the `libXCDYouTubeKit.a` file referenced from XCDYouTubeKit → Products → libXCDYouTubeKit.a into the *Link Binary With Libraries* build phase of your app’s target. 61 | 62 | These steps will ensure that `#import ` will work properly in your project. 63 | 64 | ## Usage 65 | 66 | XCDYouTubeKit is [fully documented](http://cocoadocs.org/docsets/XCDYouTubeKit/). 67 | 68 | ### iOS 8.0+ & tvOS (AVPlayerViewController) 69 | 70 | ```objc 71 | AVPlayerViewController *playerViewController = [AVPlayerViewController new]; 72 | [self presentViewController:playerViewController animated:YES completion:nil]; 73 | 74 | __weak AVPlayerViewController *weakPlayerViewController = playerViewController; 75 | [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo * _Nullable video, NSError * _Nullable error) { 76 | if (video) 77 | { 78 | NSDictionary *streamURLs = video.streamURLs; 79 | NSURL *streamURL = streamURLs[XCDYouTubeVideoQualityHTTPLiveStreaming] ?: streamURLs[@(XCDYouTubeVideoQualityHD720)] ?: streamURLs[@(XCDYouTubeVideoQualityMedium360)] ?: streamURLs[@(XCDYouTubeVideoQualitySmall240)]; 80 | weakPlayerViewController.player = [AVPlayer playerWithURL:streamURL]; 81 | [weakPlayerViewController.player play]; 82 | } 83 | else 84 | { 85 | [self dismissViewControllerAnimated:YES completion:nil]; 86 | } 87 | }]; 88 | ``` 89 | 90 | ### iOS, tvOS and macOS 91 | 92 | ```objc 93 | NSString *videoIdentifier = @"9bZkp7q19f0"; // A 11 characters YouTube video identifier 94 | [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo *video, NSError *error) { 95 | if (video) 96 | { 97 | // Do something with the `video` object 98 | } 99 | else 100 | { 101 | // Handle error 102 | } 103 | }]; 104 | ``` 105 | 106 | ### iOS 8.0 107 | 108 | On iOS, you can use the class `XCDYouTubeVideoPlayerViewController` the same way you use a `MPMoviePlayerViewController`, except you initialize it with a YouTube video identifier instead of a content URL. 109 | 110 | #### Present the video in full-screen 111 | 112 | ```objc 113 | - (void) playVideo 114 | { 115 | XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"9bZkp7q19f0"]; 116 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:videoPlayerViewController.moviePlayer]; 117 | [self presentMoviePlayerViewControllerAnimated:videoPlayerViewController]; 118 | } 119 | 120 | - (void) moviePlayerPlaybackDidFinish:(NSNotification *)notification 121 | { 122 | [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:notification.object]; 123 | MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue]; 124 | if (finishReason == MPMovieFinishReasonPlaybackError) 125 | { 126 | NSError *error = notification.userInfo[XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey]; 127 | // Handle error 128 | } 129 | } 130 | 131 | ``` 132 | 133 | #### Present the video in a non full-screen view 134 | 135 | ```objc 136 | XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"9bZkp7q19f0"]; 137 | [videoPlayerViewController presentInView:self.videoContainerView]; 138 | [videoPlayerViewController.moviePlayer play]; 139 | ``` 140 | 141 | See the demo project for more sample code. 142 | 143 | ## Logging 144 | 145 | Since version 2.2.0, XCDYouTubeKit produces logs. XCDYouTubeKit supports [CocoaLumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack) but does not require it. 146 | 147 | See the `XCDYouTubeLogger` class [documentation](http://cocoadocs.org/docsets/XCDYouTubeKit/) for more information. 148 | 149 | ## Credits 150 | 151 | The URL extraction algorithms in *XCDYouTubeKit* are inspired by the [YouTube extractor](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py) module of the *youtube-dl* project. 152 | 153 | ## Contact 154 | 155 | Cédric Luthi 156 | 157 | - http://github.com/0xced 158 | - http://twitter.com/0xced 159 | 160 | ## License 161 | 162 | XCDYouTubeKit is available under the MIT license. See the [LICENSE](LICENSE) file for more information. 163 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #if !__has_feature(nullability) 6 | #define NS_ASSUME_NONNULL_BEGIN 7 | #define NS_ASSUME_NONNULL_END 8 | #define nullable 9 | #define __nullable 10 | #endif 11 | 12 | #import 13 | 14 | #import "XCDYouTubeOperation.h" 15 | #import "XCDYouTubeVideo.h" 16 | #import "XCDYouTubeError.h" 17 | 18 | NS_ASSUME_NONNULL_BEGIN 19 | 20 | /** 21 | * The `XCDYouTubeClient` class is responsible for interacting with the YouTube API. Given a YouTube video identifier, you will get video information with the `<-getVideoWithIdentifier:completionHandler:>` method. 22 | * 23 | * On iOS, you probably don’t want to use `XCDYouTubeClient` directly but the higher level class ``. 24 | */ 25 | @interface XCDYouTubeClient : NSObject 26 | 27 | /** 28 | * ------------------ 29 | * @name Initializing 30 | * ------------------ 31 | */ 32 | 33 | /** 34 | * Returns the shared client with the default language, i.e. the preferred language of the main bundle. 35 | * 36 | * @return The default client. 37 | */ 38 | + (instancetype) defaultClient; 39 | 40 | /** 41 | * Initializes a client with the specified language identifier. 42 | * 43 | * @param languageIdentifier An [ISO 639-1 two-letter language code](http://www.loc.gov/standards/iso639-2/php/code_list.php) used for error localization. If you pass a nil language identifier, the preferred language of the main bundle will be used. 44 | * 45 | * @return A client with the specified language identifier. 46 | */ 47 | - (instancetype) initWithLanguageIdentifier:(nullable NSString *)languageIdentifier; 48 | 49 | /** 50 | * --------------------------------- 51 | * @name Accessing client properties 52 | * --------------------------------- 53 | */ 54 | 55 | /** 56 | * The language identifier of the client, used for error localization. 57 | * 58 | * @see -initWithLanguageIdentifier: 59 | */ 60 | @property (nonatomic, readonly) NSString *languageIdentifier; 61 | 62 | /** 63 | * -------------------------------------- 64 | * @name Interacting with the YouTube API 65 | * -------------------------------------- 66 | */ 67 | 68 | /** 69 | * Starts an asynchronous operation for the specified video identifier, and calls a handler upon completion. 70 | * 71 | * @param videoIdentifier A 11 characters YouTube video identifier. If the video identifier is invalid (including nil) the completion handler will be called with an error with `XCDYouTubeVideoErrorDomain` domain and `XCDYouTubeErrorInvalidVideoIdentifier` code. 72 | * @param completionHandler A block to execute when the client finishes the operation. The completion handler is executed on the main thread. If the completion handler is nil, this method throws an exception. 73 | * 74 | * @discussion If the operation completes successfully, the video parameter of the handler block contains a `` object, and the error parameter is nil. If the operation fails, the video parameter is nil and the error parameter contains information about the failure. The error's domain is always `XCDYouTubeVideoErrorDomain`. 75 | * 76 | * @see XCDYouTubeErrorCode 77 | * 78 | * @return An opaque object conforming to the `` protocol for canceling the asynchronous video information operation. If you call the `cancel` method before the operation is finished, the completion handler will not be called. It is recommended that you store this opaque object as a weak property. 79 | */ 80 | - (id) getVideoWithIdentifier:(nullable NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler; 81 | 82 | /** 83 | * Starts an asynchronous operation for the specified video identifier, and calls a handler upon completion. 84 | * 85 | * @param videoIdentifier A 11 characters YouTube video identifier. If the video identifier is invalid (including nil) the completion handler will be called with an error with `XCDYouTubeVideoErrorDomain` domain and `XCDYouTubeErrorInvalidVideoIdentifier` code. 86 | * @param cookies An array of `NSHTTPCookie` objects, can be nil. These cookies can be used for certain videos that require a login. 87 | * @param completionHandler A block to execute when the client finishes the operation. The completion handler is executed on the main thread. If the completion handler is nil, this method throws an exception. 88 | * 89 | * @discussion If the operation completes successfully, the video parameter of the handler block contains a `` object, and the error parameter is nil. If the operation fails, the video parameter is nil and the error parameter contains information about the failure. The error's domain is always `XCDYouTubeVideoErrorDomain`. 90 | * 91 | * @see XCDYouTubeErrorCode 92 | * 93 | * @return An opaque object conforming to the `` protocol for canceling the asynchronous video information operation. If you call the `cancel` method before the operation is finished, the completion handler will not be called. It is recommended that you store this opaque object as a weak property. 94 | */ 95 | - (id) getVideoWithIdentifier:(NSString *)videoIdentifier cookies:(nullable NSArray *)cookies completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler; 96 | 97 | /** 98 | * Starts an asynchronous operation for the specified video identifier, and calls a handler upon completion. 99 | * 100 | * @param videoIdentifier A 11 characters YouTube video identifier. If the video identifier is invalid (including nil) the completion handler will be called with an error with `XCDYouTubeVideoErrorDomain` domain and `XCDYouTubeErrorInvalidVideoIdentifier` code. 101 | * @param cookies An array of `NSHTTPCookie` objects, can be nil. These cookies can be used for certain videos that require a login. 102 | * @param customPatterns An array of `NSString` objects, can be nil. These patterns can be used to create custom regular expression objects in favor of the internal hard-coded patterns for video parsing. If none of these patterns produces a valid `NSRegularExpression` object then the internal hard-coded regular expression objects are used. Typically, you do not need to use this parameter, however, it can be used a way to use update patterns when needed (i.e to adapt to YouTube API changes). See https://github.com/0xced/XCDYouTubeKit/blob/master/REGULAR_EXPRESSION.md for more info. 103 | * @param completionHandler A block to execute when the client finishes the operation. The completion handler is executed on the main thread. If the completion handler is nil, this method throws an exception. 104 | * 105 | * @discussion If the operation completes successfully, the video parameter of the handler block contains a `` object, and the error parameter is nil. If the operation fails, the video parameter is nil and the error parameter contains information about the failure. The error's domain is always `XCDYouTubeVideoErrorDomain`. 106 | * 107 | * @see XCDYouTubeErrorCode 108 | * 109 | * @return An opaque object conforming to the `` protocol for canceling the asynchronous video information operation. If you call the `cancel` method before the operation is finished, the completion handler will not be called. It is recommended that you store this opaque object as a weak property. 110 | */ 111 | - (id) getVideoWithIdentifier:(NSString *)videoIdentifier cookies:(nullable NSArray *)cookies customPatterns:(nullable NSArray *)customPatterns completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler; 112 | 113 | @end 114 | 115 | NS_ASSUME_NONNULL_END 116 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-apnspushsimulate/Pods-apnspushsimulate-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/Cosmos/Cosmos.framework" 165 | install_framework "${BUILT_PRODUCTS_DIR}/XCDYouTubeKit/XCDYouTubeKit.framework" 166 | fi 167 | if [[ "$CONFIGURATION" == "Release" ]]; then 168 | install_framework "${BUILT_PRODUCTS_DIR}/Cosmos/Cosmos.framework" 169 | install_framework "${BUILT_PRODUCTS_DIR}/XCDYouTubeKit/XCDYouTubeKit.framework" 170 | fi 171 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 172 | wait 173 | fi 174 | -------------------------------------------------------------------------------- /Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. 3 | // 4 | 5 | #import "XCDYouTubeVideoPlayerViewController.h" 6 | 7 | #import "XCDYouTubeClient.h" 8 | 9 | #import 10 | 11 | #if TARGET_OS_IOS 12 | 13 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 14 | 15 | NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey = @"error"; // documented in -[MPMoviePlayerController initWithContentURL:] 16 | 17 | NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification = @"XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification"; 18 | NSString *const XCDMetadataKeyTitle = @"Title"; 19 | NSString *const XCDMetadataKeySmallThumbnailURL = @"SmallThumbnailURL"; 20 | NSString *const XCDMetadataKeyMediumThumbnailURL = @"MediumThumbnailURL"; 21 | NSString *const XCDMetadataKeyLargeThumbnailURL = @"LargeThumbnailURL"; 22 | 23 | NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification = @"XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification"; 24 | NSString *const XCDYouTubeVideoUserInfoKey = @"Video"; 25 | 26 | @interface XCDYouTubeVideoPlayerViewController () 27 | @property (nonatomic, weak) id videoOperation; 28 | @property (nonatomic, assign, getter = isEmbedded) BOOL embedded; 29 | @end 30 | #pragma clang diagnostic push 31 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 32 | @implementation XCDYouTubeVideoPlayerViewController 33 | #pragma clang diagnostic pop 34 | /* 35 | * MPMoviePlayerViewController on iOS 7 and earlier 36 | * - (id) init 37 | * `-- [super init] 38 | * 39 | * - (id) initWithContentURL:(NSURL *)contentURL 40 | * |-- [self init] 41 | * `-- [self.moviePlayer setContentURL:contentURL] 42 | * 43 | * MPMoviePlayerViewController on iOS 8 and later 44 | * - (id) init 45 | * `-- [self initWithContentURL:nil] 46 | * 47 | * - (id) initWithContentURL:(NSURL *)contentURL 48 | * |-- [super init] 49 | * `-- [self.moviePlayer setContentURL:contentURL] 50 | */ 51 | 52 | - (instancetype) init 53 | { 54 | return [self initWithVideoIdentifier:nil]; 55 | } 56 | 57 | #pragma clang diagnostic push 58 | #pragma clang diagnostic ignored "-Wobjc-designated-initializers" 59 | - (instancetype) initWithContentURL:(NSURL *)contentURL 60 | { 61 | @throw [NSException exceptionWithName:NSGenericException reason:@"Use the `initWithVideoIdentifier:` method instead." userInfo:nil]; 62 | } // LCOV_EXCL_LINE 63 | 64 | - (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier 65 | { 66 | #if defined(DEBUG) && DEBUG 67 | NSString *callStackSymbols = [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]; 68 | if (([callStackSymbols rangeOfString:@"-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]_block_invoke"].length > 0) || ([callStackSymbols rangeOfString:@"-[XCDYouTubeClient getVideoWithIdentifier:cookies:completionHandler:]_block_invoke"].length > 0) || ([callStackSymbols rangeOfString:@"-[XCDYouTubeClient getVideoWithIdentifier:cookies:customPatterns:completionHandler:]_block_invoke"].length > 0)) 69 | { 70 | NSString *reason = @"XCDYouTubeVideoPlayerViewController must not be used in the completion handler of `-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]` or `-[XCDYouTubeClient getVideoWithIdentifier:cookies:completionHandler:]` or `-[XCDYouTubeClient getVideoWithIdentifier:cookies:customPatterns:completionHandler:]`. Please read the documentation and sample code to properly use XCDYouTubeVideoPlayerViewController."; 71 | @throw [NSException exceptionWithName:NSGenericException reason:reason userInfo:nil]; 72 | } 73 | #endif 74 | 75 | if ([[[UIDevice currentDevice] systemVersion] integerValue] >= 8) 76 | self = [super initWithContentURL:nil]; 77 | else 78 | self = [super init]; // LCOV_EXCL_LINE 79 | 80 | if (!self) 81 | return nil; // LCOV_EXCL_LINE 82 | 83 | // See https://github.com/0xced/XCDYouTubeKit/commit/cadec1c3857d6a302f71b9ce7d1ae48e389e6890 84 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; 85 | 86 | if (videoIdentifier) 87 | self.videoIdentifier = videoIdentifier; 88 | 89 | return self; 90 | } 91 | #pragma clang diagnostic pop 92 | 93 | #pragma mark - Public 94 | 95 | - (NSArray *) preferredVideoQualities 96 | { 97 | if (!_preferredVideoQualities) 98 | _preferredVideoQualities = @[ XCDYouTubeVideoQualityHTTPLiveStreaming, @(XCDYouTubeVideoQualityHD720), @(XCDYouTubeVideoQualityMedium360), @(XCDYouTubeVideoQualitySmall240) ]; 99 | 100 | return _preferredVideoQualities; 101 | } 102 | 103 | - (void) setVideoIdentifier:(NSString *)videoIdentifier 104 | { 105 | if ([videoIdentifier isEqual:self.videoIdentifier]) 106 | return; 107 | 108 | _videoIdentifier = [videoIdentifier copy]; 109 | 110 | [self.videoOperation cancel]; 111 | self.videoOperation = [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo *video, NSError *error) 112 | { 113 | if (video) 114 | { 115 | NSURL *streamURL = nil; 116 | for (NSNumber *videoQuality in self.preferredVideoQualities) 117 | { 118 | streamURL = video.streamURLs[videoQuality]; 119 | if (streamURL) 120 | { 121 | [self startVideo:video streamURL:streamURL]; 122 | break; 123 | } 124 | } 125 | 126 | if (!streamURL) 127 | { 128 | NSError *noStreamError = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:XCDYouTubeErrorNoStreamAvailable userInfo:nil]; 129 | [self stopWithError:noStreamError]; 130 | } 131 | } 132 | else 133 | { 134 | [self stopWithError:error]; 135 | } 136 | }]; 137 | } 138 | 139 | - (void) presentInView:(UIView *)view 140 | { 141 | static const void * const XCDYouTubeVideoPlayerViewControllerKey = &XCDYouTubeVideoPlayerViewControllerKey; 142 | 143 | self.embedded = YES; 144 | 145 | self.moviePlayer.controlStyle = MPMovieControlStyleEmbedded; 146 | self.moviePlayer.view.frame = CGRectMake(0, 0, view.bounds.size.width, view.bounds.size.height); 147 | self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 148 | if (![view.subviews containsObject:self.moviePlayer.view]) 149 | [view addSubview:self.moviePlayer.view]; 150 | objc_setAssociatedObject(view, XCDYouTubeVideoPlayerViewControllerKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 151 | } 152 | 153 | #pragma mark - Private 154 | 155 | - (void) startVideo:(XCDYouTubeVideo *)video streamURL:(NSURL *)streamURL 156 | { 157 | #pragma clang diagnostic push 158 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 159 | NSMutableDictionary *userInfo = [NSMutableDictionary new]; 160 | if (video.title) 161 | userInfo[XCDMetadataKeyTitle] = video.title; 162 | if (video.smallThumbnailURL) 163 | userInfo[XCDMetadataKeySmallThumbnailURL] = video.smallThumbnailURL; 164 | if (video.mediumThumbnailURL) 165 | userInfo[XCDMetadataKeyMediumThumbnailURL] = video.mediumThumbnailURL; 166 | if (video.largeThumbnailURL) 167 | userInfo[XCDMetadataKeyLargeThumbnailURL] = video.largeThumbnailURL; 168 | 169 | [[NSNotificationCenter defaultCenter] postNotificationName:XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification object:self userInfo:userInfo]; 170 | #pragma clang diagnostic pop 171 | 172 | self.moviePlayer.contentURL = streamURL; 173 | 174 | [[NSNotificationCenter defaultCenter] postNotificationName:XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification object:self userInfo:@{ XCDYouTubeVideoUserInfoKey: video }]; 175 | } 176 | 177 | - (void) stopWithError:(NSError *)error 178 | { 179 | NSDictionary *userInfo = @{ MPMoviePlayerPlaybackDidFinishReasonUserInfoKey: @(MPMovieFinishReasonPlaybackError), 180 | XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey: error }; 181 | [[NSNotificationCenter defaultCenter] postNotificationName:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer userInfo:userInfo]; 182 | 183 | if (self.isEmbedded) 184 | [self.moviePlayer.view removeFromSuperview]; 185 | else 186 | [self.presentingViewController dismissMoviePlayerViewControllerAnimated]; 187 | } 188 | 189 | #pragma mark - UIViewController 190 | 191 | - (void) viewWillAppear:(BOOL)animated 192 | { 193 | [super viewWillAppear:animated]; 194 | 195 | if (![self isBeingPresented]) 196 | return; 197 | 198 | self.moviePlayer.controlStyle = MPMovieControlStyleFullscreen; 199 | [self.moviePlayer play]; 200 | } 201 | 202 | - (void) viewWillDisappear:(BOOL)animated 203 | { 204 | [super viewWillDisappear:animated]; 205 | 206 | if (![self isBeingDismissed]) 207 | return; 208 | 209 | [self.videoOperation cancel]; 210 | } 211 | 212 | @end 213 | #endif 214 | -------------------------------------------------------------------------------- /Pods/Cosmos/README.md: -------------------------------------------------------------------------------- 1 | # Cosmos, a star rating control for iOS and tvOS 2 | 3 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 4 | [![CocoaPods Version](https://img.shields.io/cocoapods/v/Cosmos.svg?style=flat)](http://cocoadocs.org/docsets/Cosmos) 5 | [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) 6 | [![License](https://img.shields.io/cocoapods/l/Cosmos.svg?style=flat)](LICENSE) 7 | [![Platform](https://img.shields.io/cocoapods/p/Cosmos.svg?style=flat)](http://cocoadocs.org/docsets/Cosmos) 8 | 9 | Cosmos, star rating control for iOS / Swift 10 | 11 | This is a UI control for iOS and tvOS written in Swift. It shows a star rating and takes rating input from the user. Cosmos is a subclass of a UIView that will allow your users to post those inescapable 1-star reviews! 12 | 13 | * Shows star rating with an optional text label. 14 | * Can be used as a rating input control (iOS only). 15 | * Cosmos view can be customized in the Storyboard without writing code. 16 | * Includes different star filling modes: full, half-filled and precise. 17 | * Cosmos is accessible and works with voice-over. 18 | * Supports right-to-left languages. 19 | 20 | 21 | Binary star system of Sirius A and Sirius B (artist's impression) 23 | 24 | *Picture of binary star system of Sirius A and Sirius B by [NASA](http://www.nasa.gov), [ESA](http://www.esa.int/ESA) and G. Bacon ([STScI](http://www.stsci.edu/portal/)). Source: [spacetelescope.org](http://www.spacetelescope.org/images/heic0516b/).* 25 | 26 | ## Video tutorial 27 | 28 | Thanks to Alex Nagy from [rebeloper.com](https://rebeloper.com/) for creating this amazing [video tutorial](https://www.youtube.com/watch?v=Y4A_y29cy7Q) that shows how to use and customize Cosmos from code. 29 | 30 | Cosmos rating video tutorial for Swift 4.2 (Xcode10) 31 | 32 | 33 | ## Setup 34 | 35 | There are various ways you can add Cosmos to your Xcode project. 36 | 37 | #### Add source (iOS 8+) 38 | 39 | Simply add [CosmosDistrib.swift](https://github.com/evgenyneu/Cosmos/blob/master/Distrib/CosmosDistrib.swift) file into your Xcode project. 40 | 41 | #### Setup with Carthage (iOS 8+) 42 | 43 | Alternatively, add `github "evgenyneu/Cosmos" ~> 21.0` to your Cartfile and run `carthage update`. 44 | 45 | #### Setup with CocoaPods (iOS 8+) 46 | 47 | If you are using CocoaPods add this text to your Podfile and run `pod install`. 48 | 49 | use_frameworks! 50 | target 'Your target name' 51 | pod 'Cosmos', '~> 21.0' 52 | 53 | 54 | #### Setup with Swift Package Manager 55 | 56 | * In Xcode 11+ select *File > Packages > Add Package Dependency...*. 57 | * Enter this project's URL: https://github.com/evgenyneu/Cosmos.git 58 | 59 | 60 | #### Legacy Swift versions 61 | 62 | Setup a [previous version](https://github.com/evgenyneu/Cosmos/wiki/Legacy-Swift-versions) of the library if you use an older version of Swift. 63 | 64 | 65 | ## Usage 66 | 67 | 68 | 1) Drag `View` object from the *Object Library* into your storyboard. 69 | 70 | 71 | Add view control in attributes inspector 72 | 73 | 74 | 2) Set the view's class to `CosmosView` in the *Identity Inspector*. Set its *module* property to `Cosmos`, unless you used the file setup method. 75 | 76 | 77 | Add Cosmos rating view to the storyboard 78 | 79 | *tvOS note*: read the collowing [setup instructions for tvOS](https://github.com/evgenyneu/Cosmos/wiki/tvOS-CocoaPods-error) if you see build errors at this stage. 80 | 81 | 82 | 3) Customize the Cosmos view appearance in the *Attributes Inspector*. If storyboard does not show the stars click **Refresh All Views** from the **Editor** menu. 83 | 84 | 85 | Customize cosmos appearance in the attributes inspector in Xcode. 86 | 87 | 88 | ## Positioning the Cosmos view 89 | 90 | One can position the Cosmos view using Auto Layout constaints. The width and height of the view is determined automatically based on the size of its content - stars and text. Therefore, there is no need to set width/height constaints on the Cosmos view. 91 | 92 | ## Using Cosmos in code 93 | 94 | Add `import Cosmos` to your source code, unless you used the file setup method. 95 | 96 | You can style and control Cosmos view from your code by creating an outlet in your view controller. Alternatively, one can instantiate `CosmosView` class and add it to the view manually without using Storyboard. 97 | 98 | 99 | ```Swift 100 | // Change the cosmos view rating 101 | cosmosView.rating = 4 102 | 103 | // Change the text 104 | cosmosView.text = "(123)" 105 | 106 | // Called when user finishes changing the rating by lifting the finger from the view. 107 | // This may be a good place to save the rating in the database or send to the server. 108 | cosmosView.didFinishTouchingCosmos = { rating in } 109 | 110 | // A closure that is called when user changes the rating by touching the view. 111 | // This can be used to update UI as the rating is being changed by moving a finger. 112 | cosmosView.didTouchCosmos = { rating in } 113 | ``` 114 | 115 | 116 | ## Customization 117 | 118 | One can customize Cosmos from code by changing its `settings`. See the [Cosmos configuration manual](https://github.com/evgenyneu/Cosmos/wiki/Cosmos-configuration) for the complete list of configuration options. 119 | 120 | ```Swift 121 | // Do not change rating when touched 122 | // Use if you need just to show the stars without getting user's input 123 | cosmosView.settings.updateOnTouch = false 124 | 125 | // Show only fully filled stars 126 | cosmosView.settings.fillMode = .full 127 | // Other fill modes: .half, .precise 128 | 129 | // Change the size of the stars 130 | cosmosView.settings.starSize = 30 131 | 132 | // Set the distance between stars 133 | cosmosView.settings.starMargin = 5 134 | 135 | // Set the color of a filled star 136 | cosmosView.settings.filledColor = UIColor.orange 137 | 138 | // Set the border color of an empty star 139 | cosmosView.settings.emptyBorderColor = UIColor.orange 140 | 141 | // Set the border color of a filled star 142 | cosmosView.settings.filledBorderColor = UIColor.orange 143 | ``` 144 | 145 | 146 | 147 | ## Supplying images for the stars 148 | 149 | By default, Cosmos draws the stars from an array of points. Alternatively, one can supply custom images for the stars, both in the Storyboard and from code. 150 | 151 | #### Using star images from the Storyboard 152 | 153 | Supplying an image for a star in Xcode. 154 | 155 | #### Using star images from code 156 | 157 | ```Swift 158 | // Set image for the filled star 159 | cosmosView.settings.filledImage = UIImage(named: "GoldStarFilled") 160 | 161 | // Set image for the empty star 162 | cosmosView.settings.emptyImage = UIImage(named: "GoldStarEmpty") 163 | ``` 164 | Note: you need to have the images for the filled and empty star in your project for this code to work. 165 | 166 | #### Download star image files 167 | 168 | Images for the golden star used in the demo app are available in [here](https://github.com/evgenyneu/Cosmos/tree/master/graphics/Stars/GoldStar). Contributions for other star images are very welcome: add vector images to `/graphics/Stars/` directory and submit a pull request. 169 | 170 | 171 | ## Using Cosmos in a scroll/table view 172 | 173 | [Here](https://github.com/evgenyneu/Cosmos/wiki/Using-Cosmos-in-a-scroll-view) is how to use Cosmos in a scroll view or a table view. 174 | 175 | 176 | ## Using Cosmos in a modal screen 177 | 178 | iOS 13 introduced swiping gesture for closing modal screens, which [prevents Cosmos from working properly](https://github.com/evgenyneu/Cosmos/issues/148). The following setting fixes this problem: 179 | 180 | ``` 181 | cosmosView.settings.disablePanGestures = true 182 | ``` 183 | 184 | ## Using Cosmos settings from Objective-C 185 | 186 | [This manual](https://github.com/evgenyneu/Cosmos/wiki/Using-Cosmos-settings-in-Objective-C) describes how to set/read Cosmos settings in Objective-C apps. 187 | 188 | 189 | ## Demo app 190 | 191 | This project includes a demo iOS app. 192 | 193 | Five star rating control for iOS written in Swift 194 | 195 | #### Using cosmos in a table view 196 | 197 | Using cosmos in a table view 198 | 199 | 200 | 201 | ## Alternative solutions 202 | 203 | Here are some other star rating controls for iOS: 204 | 205 | * [danwilliams64/DJWStarRatingView](https://github.com/danwilliams64/DJWStarRatingView) 206 | * [dlinsin/DLStarRating](https://github.com/dlinsin/DLStarRating) 207 | * [dyang/DYRateView](https://github.com/dyang/DYRateView) 208 | * [erndev/EDStarRating](https://github.com/erndev/EDStarRating) 209 | * [hugocampossousa/HCSStarRatingView](https://github.com/hugocampossousa/HCSStarRatingView) 210 | * [shuhrat10/STRatingControl](https://github.com/shuhrat10/STRatingControl) 211 | * [strekfus/FloatRatingView](https://github.com/strekfus/FloatRatingView) 212 | * [yanguango/ASStarRatingView](https://github.com/yanguango/ASStarRatingView) 213 | 214 | ## Thanks 👍 215 | 216 | We would like to thank the following people for their valuable contributions. 217 | 218 | * [jsahoo](https://github.com/jsahoo) for adding ability to customize the Cosmos view from the interface builder with Carthage setup method. 219 | * [0x7fffffff](https://github.com/0x7fffffff) for changing `public` access-level modifiers to `open`. 220 | * [ali-zahedi](https://github.com/ali-zahedi) for updating to the latest version of Swift 3.0. 221 | * [augmentedworks](https://github.com/augmentedworks) for adding borders to filled stars. 222 | * [craiggrummitt](https://github.com/craiggrummitt) for Xcode 8 beta 4 support. 223 | * [JimiSmith](https://github.com/JimiSmith) for Xcode 8 beta 6 support. 224 | * [nickhart](https://github.com/nickhart) for adding compatibility with Xcode 6. 225 | * [staticdreams](https://github.com/staticdreams) for bringing tvOS support. 226 | * [wagnersouz4](https://github.com/wagnersouz4) for Swift 3.1 update. 227 | * [paoloq](https://github.com/paoloq) for reporting the CosmoView frame size issue when the view is used without Auto Layout. 228 | * [danshevluk](https://github.com/danshevluk) for adding ability to reuse settings in multiple cosmos views. 229 | * [xrayman](https://github.com/xrayman) for reporting a table view reusability bug and improving the table view screen of the demo app. 230 | * [chlumik](https://github.com/chlumik) for updating to Swift 4.2. 231 | * [rebeloper](https://github.com/rebeloper) for creating a [video tutorial](https://www.youtube.com/watch?v=Y4A_y29cy7Q). 232 | * [yuravake](https://github.com/yuravake) for adding `passTouchesToSuperview` setting. 233 | * [gcharita](https://github.com/gcharita) for adding Swift Package Manager support. 234 | * [benpackard](https://github.com/benpackard) for fixing Cosmos when used in a modal screen on iOS 13. 235 | 236 | 237 | 238 | ## License 239 | 240 | Cosmos is released under the [MIT License](LICENSE). 241 | 242 | ## 🌌⭐️🌕🚀🌠 243 | 244 | > We are a way for the cosmos to know itself. 245 | 246 | *Carl Sagan, from 1980 "Cosmos: A Personal Voyage" TV series.* 247 | --------------------------------------------------------------------------------