فهرست منبع

VLCURLHandler: Have a designated Class for handling openURL calls

Add first UnitTests for the parts of the URLHandler as well
Carola Nitz 7 سال پیش
والد
کامیت
0ed12bba99

+ 1 - 0
Podfile

@@ -29,6 +29,7 @@ target 'VLC-iOS' do
   target 'VLC for iOSUITests' do
     pod 'SimulatorStatusMagic'
   end
+  target 'VLC for iOSTests'
 end
 
 target 'VLC-tvOS' do

+ 1 - 1
Podfile.lock

@@ -168,6 +168,6 @@ SPEC CHECKSUMS:
   XKKeychain: 852ef663c56a7194c73d3c68e8d9d4f07b121d4f
   xmlrpc: 109bb21d15ed6d108b2c1ac5973a6a223a50f5f4
 
-PODFILE CHECKSUM: 3dcf0322da92b8ffd75b8fef5df1ef5633b2f3ce
+PODFILE CHECKSUM: 0c8231ce51e9fc3523731b5752caab4a1bef5e13
 
 COCOAPODS: 1.5.2

+ 203 - 0
Sources/URLHandler.swift

@@ -0,0 +1,203 @@
+/*****************************************************************************
+ * URLHandler.swift
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2018 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Authors: Carola Nitz <caro # videolan.org>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+import Foundation
+
+@objc protocol VLCURLHandler {
+    func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool
+    func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool
+}
+
+@objc class URLHandlers: NSObject {
+    @objc static let googleURLHandler = GoogleURLHandler()
+
+    @objc static let handlers =
+        [
+            googleURLHandler,
+            DropBoxURLHandler(),
+            FileURLHandler(),
+            XCallbackURLHandler(),
+            VLCCallbackURLHandler(),
+            ElseCallbackURLHandler()
+        ]
+}
+
+class DropBoxURLHandler: NSObject, VLCURLHandler {
+
+    @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        return url.scheme == "db-a60fc6qj9zdg7bw"
+    }
+
+    @objc func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        let authResult = DBClientsManager.handleRedirectURL(url)
+
+        if  let authResult = authResult, authResult.isSuccess() == true {
+            //TODO:update Dropboxcontrollers
+            return true
+        }
+        return false
+    }
+}
+
+class GoogleURLHandler: NSObject, VLCURLHandler {
+
+    @objc var currentGoogleAuthorizationFlow: OIDAuthorizationFlowSession?
+
+    @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        return url.scheme == "com.googleusercontent.apps.CLIENT"
+    }
+
+    @objc func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        if currentGoogleAuthorizationFlow?.resumeAuthorizationFlow(with: url) == true {
+            currentGoogleAuthorizationFlow = nil
+            return true
+        }
+        return false
+    }
+}
+
+class FileURLHandler: NSObject, VLCURLHandler {
+
+    @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        return url.isFileURL
+    }
+
+    @objc func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        let subclass = DocumentClass(fileURL: url)
+        subclass.open { _ in
+            play(url: url) { _ in
+                subclass.close(completionHandler: nil)
+            }
+        }
+        return true
+    }
+}
+
+class XCallbackURLHandler: NSObject, VLCURLHandler {
+
+    @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        return url.scheme == "vlc-x-callback" || url.scheme == "x-callback-url"
+    }
+
+    @objc func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        let action = url.path.replacingOccurrences(of: "/", with: "")
+        var movieURL: URL?
+        var successCallback: URL?
+        var errorCallback: URL?
+        var fileName: String?
+        guard let query = url.query else {
+            assertionFailure("no query")
+            return false
+        }
+        for entry in query.components(separatedBy: "&") {
+            let components = entry.components(separatedBy: "=")
+            if components.count < 2 {
+                continue
+            }
+
+            if let key = components.first, let value = components[1].removingPercentEncoding {
+                if key == "url"{
+                    movieURL = URL(string: value)
+                } else if key == "filename" {
+                    fileName = value
+                } else if key == "x-success" {
+                    successCallback = URL(string: value)
+                } else if key == "x-error" {
+                    errorCallback = URL(string: value)
+                }
+            } else {
+                assertionFailure("no key or app value")
+            }
+        }
+        if action == "stream", let movieURL = movieURL {
+            play(url: movieURL) { success in
+                guard let callback = success ? successCallback : errorCallback else {
+                    assertionFailure("no CallbackURL")
+                    return
+                }
+                if #available(iOS 10, *) {
+                    UIApplication.shared.open(callback, options: [:], completionHandler: nil)
+                } else {
+                    UIApplication.shared.openURL(callback)
+                }
+            }
+            return true
+        } else if action == "download", let movieURL = movieURL {
+            downloadMovie(from:movieURL, fileNameOfMedia:fileName)
+            return true
+        }
+        return false
+    }
+}
+
+class VLCCallbackURLHandler: NSObject, VLCURLHandler {
+
+    @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        return url.scheme == "vlc"
+    }
+
+    // Safari fixes URLs like "vlc://http://example.org" to "vlc://http//example.org"
+    func transformVLCURL(_ url: URL) -> URL {
+        var parsedString = url.absoluteString.replacingOccurrences(of: "vlc://", with: "")
+        if let location = parsedString.range(of: "//"), parsedString[parsedString.index(location.lowerBound, offsetBy: -1)] != ":" {
+            parsedString = "\(parsedString[parsedString.startIndex..<location.lowerBound])://\(parsedString[location.upperBound...])"
+        } else if !parsedString.hasPrefix("http://") && !parsedString.hasPrefix("https://") && !parsedString.hasPrefix("ftp://") {
+            parsedString = "http://\(parsedString)"
+        }
+        return URL(string: parsedString)!
+    }
+
+    func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+
+        let transformedURL = transformVLCURL(url)
+        let scheme = transformedURL.scheme
+        if scheme == "http" || scheme == "https" || scheme == "ftp" {
+            let alert = UIAlertController(title: NSLocalizedString("OPEN_STREAM_OR_DOWNLOAD", comment:""), message: url.absoluteString, preferredStyle: .alert)
+            let downloadAction = UIAlertAction(title: NSLocalizedString("BUTTON_DOWNLOAD", comment:""), style: .default) { _ in
+                downloadMovie(from:transformedURL, fileNameOfMedia:nil)
+            }
+            alert.addAction(downloadAction)
+            let playAction = UIAlertAction(title: NSLocalizedString("PLAY_BUTTON", comment:""), style: .default) { _ in
+                play(url: transformedURL, completion: nil)
+            }
+            alert.addAction(playAction)
+            alert.show(UIApplication.shared.keyWindow!.rootViewController!, sender: nil)
+        } else {
+            play(url: transformedURL, completion: nil)
+        }
+        return true
+    }
+}
+
+class ElseCallbackURLHandler: NSObject, VLCURLHandler {
+    @objc func canHandleOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        return true
+    }
+
+    func performOpen(url: URL, options: [UIApplicationOpenURLOptionsKey: AnyObject]) -> Bool {
+        play(url: url, completion: nil)
+        return true
+    }
+}
+
+// TODO: This code should probably not live here
+func play(url: URL, completion: ((Bool) -> Void)?) {
+   let vpc = VLCPlaybackController.sharedInstance()
+    vpc.fullscreenSessionRequested = true
+    if let mediaList = VLCMediaList(array: [VLCMedia(url: url)]) {
+        vpc.playMediaList(mediaList, firstIndex: 0, subtitlesFilePath: nil, completion: completion)
+    }
+}
+
+func downloadMovie(from url: URL, fileNameOfMedia fileName: String?) {
+    VLCDownloadViewController.sharedInstance().addURL(toDownloadList: url, fileNameOfMedia: fileName)
+}

+ 0 - 6
Sources/VLCAppDelegate.h

@@ -14,14 +14,8 @@
  * Refer to the COPYING file of the official project for license.
  *****************************************************************************/
 
-#import <AppAuth/AppAuth.h>
-
-extern NSString *const VLCDropboxSessionWasAuthorized;
-
 @interface VLCAppDelegate : UIResponder <UIApplicationDelegate>
 
 @property (nonatomic, strong) UIWindow *window;
 
-@property (atomic, strong) id<OIDAuthorizationFlowSession> currentGoogleAuthorizationFlow;
-
 @end

+ 5 - 135
Sources/VLCAppDelegate.m

@@ -23,21 +23,16 @@
 #import "UIDevice+VLC.h"
 #import "VLCHTTPUploaderController.h"
 #import "VLCMigrationViewController.h"
-#import <BoxSDK/BoxSDK.h>
 #import "VLCPlaybackController.h"
 #import "VLCPlaybackController+MediaLibrary.h"
 #import <MediaPlayer/MediaPlayer.h>
 #import <HockeySDK/HockeySDK.h>
 #import "VLCActivityManager.h"
 #import "VLCDropboxConstants.h"
-#import "VLCDownloadViewController.h"
-#import <ObjectiveDropboxOfficial/ObjectiveDropboxOfficial.h>
 #import "VLCPlaybackNavigationController.h"
 #import "PAPasscodeViewController.h"
 #import "VLC_iOS-Swift.h"
 
-NSString *const VLCDropboxSessionWasAuthorized = @"VLCDropboxSessionWasAuthorized";
-
 #define BETA_DISTRIBUTION 1
 
 @interface VLCAppDelegate () <VLCMediaFileDiscovererDelegate>
@@ -242,120 +237,13 @@ didFailToContinueUserActivityWithType:(NSString *)userActivityType
 
 - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
 {
-    //Handles Dropbox Authorization flow.
-    DBOAuthResult *authResult = [DBClientsManager handleRedirectURL:url];
-    if (authResult != nil) {
-        if ([authResult isSuccess]) {
-            return YES;
+    for (id<VLCURLHandler> handler in URLHandlers.handlers) {
+        if ([handler canHandleOpenWithUrl:url options:options]) {
+            if ([handler performOpenWithUrl:url options:options]) {
+                break;
+            }
         }
     }
-
-    //Handles Google Authorization flow.
-    if ([_currentGoogleAuthorizationFlow resumeAuthorizationFlowWithURL:url]) {
-        _currentGoogleAuthorizationFlow = nil;
-        return YES;
-    }
-
-    //TODO: we need a model of URLHandlers that registers with the VLCAppdelegate
-    // then we can go through the list of handlers ask if they can handle the url and the first to say yes handles the call.
-    // that way internal if elses get encapsulated
-    /*
-    protocol VLCURLHandler {
-        func canHandleOpen(url: URL, options:[UIApplicationOpenURLOptionsKey:AnyObject]=[:]()) -> bool
-        func performOpen(url: URL, options:[UIApplicationOpenURLOptionsKey:AnyObject]=[:]()) -> bool
-    } */
-
-//    if (_libraryViewController && url != nil) {
-//        APLog(@"requested %@ to be opened", url);
-//
-//        if (url.isFileURL) {
-//            VLCDocumentClass *subclass = [[VLCDocumentClass alloc] initWithFileURL:url];
-//            [subclass openWithCompletionHandler:^(BOOL success) {
-//                [self playWithURL:url completion:^(BOOL success) {
-//                    [subclass closeWithCompletionHandler:nil];
-//                }];
-//            }];
-//        } else if ([url.scheme isEqualToString:@"vlc-x-callback"] || [url.host isEqualToString:@"x-callback-url"]) {
-//            // URL confirmes to the x-callback-url specification
-//            // vlc-x-callback://x-callback-url/action?param=value&x-success=callback
-//            APLog(@"x-callback-url with host '%@' path '%@' parameters '%@'", url.host, url.path, url.query);
-//            NSString *action = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
-//            NSURL *movieURL;
-//            NSURL *successCallback;
-//            NSURL *errorCallback;
-//            NSString *fileName;
-//            for (NSString *entry in [url.query componentsSeparatedByString:@"&"]) {
-//                NSArray *keyvalue = [entry componentsSeparatedByString:@"="];
-//                if (keyvalue.count < 2) continue;
-//                NSString *key = keyvalue[0];
-//                NSString *value = [keyvalue[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
-//
-//                if ([key isEqualToString:@"url"])
-//                    movieURL = [NSURL URLWithString:value];
-//                else if ([key isEqualToString:@"filename"])
-//                    fileName = value;
-//                else if ([key isEqualToString:@"x-success"])
-//                    successCallback = [NSURL URLWithString:value];
-//                else if ([key isEqualToString:@"x-error"])
-//                    errorCallback = [NSURL URLWithString:value];
-//            }
-//            if ([action isEqualToString:@"stream"] && movieURL) {
-//                [self playWithURL:movieURL completion:^(BOOL success) {
-//                    NSURL *callback = success ? successCallback : errorCallback;
-//                    if (@available(iOS 10, *)) {
-//                         [[UIApplication sharedApplication] openURL:callback options:@{} completionHandler:nil];
-//                    } else {
-//#pragma clang diagnostic push
-//#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-//                        /* UIApplication's replacement calls require iOS 10 or later, which we can't enforce as of yet */
-//                       [[UIApplication sharedApplication] openURL:callback];
-//#pragma clang diagnostic pop
-//                    }
-//                }];
-//            }
-//            else if ([action isEqualToString:@"download"] && movieURL) {
-//                [self downloadMovieFromURL:movieURL fileNameOfMedia:fileName];
-//            }
-//        } else {
-//            NSString *receivedUrl = [url absoluteString];
-//            if ([receivedUrl length] > 6) {
-//                NSString *verifyVlcUrl = [receivedUrl substringToIndex:6];
-//                if ([verifyVlcUrl isEqualToString:@"vlc://"]) {
-//                    NSString *parsedString = [receivedUrl substringFromIndex:6];
-//                    NSUInteger location = [parsedString rangeOfString:@"//"].location;
-//
-//                    /* Safari & al mangle vlc://http:// so fix this */
-//                    if (location != NSNotFound && [parsedString characterAtIndex:location - 1] != 0x3a) { // :
-//                            parsedString = [NSString stringWithFormat:@"%@://%@", [parsedString substringToIndex:location], [parsedString substringFromIndex:location+2]];
-//                    } else {
-//                        parsedString = [receivedUrl substringFromIndex:6];
-//                        if (![parsedString hasPrefix:@"http://"] && ![parsedString hasPrefix:@"https://"] && ![parsedString hasPrefix:@"ftp://"]) {
-//                            parsedString = [@"http://" stringByAppendingString:[receivedUrl substringFromIndex:6]];
-//                        }
-//                    }
-//                    url = [NSURL URLWithString:parsedString];
-//                }
-//            }
-//            [[VLCSidebarController sharedInstance] selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
-//                                                         scrollPosition:UITableViewScrollPositionNone];
-//
-//            NSString *scheme = url.scheme;
-//            if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"ftp"]) {
-//                VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"OPEN_STREAM_OR_DOWNLOAD", nil) message:url.absoluteString cancelButtonTitle:NSLocalizedString(@"BUTTON_DOWNLOAD", nil) otherButtonTitles:@[NSLocalizedString(@"PLAY_BUTTON", nil)]];
-//                alert.completion = ^(BOOL cancelled, NSInteger buttonIndex) {
-//                    if (cancelled)
-//                        [self downloadMovieFromURL:url fileNameOfMedia:nil];
-//                    else {
-//                        [self playWithURL:url completion:nil];
-//                    }
-//                };
-//                [alert show];
-//            } else {
-//                [self playWithURL:url completion:nil];
-//            }
-//        }
-//        return YES;
-//    }
     return NO;
 }
 
@@ -447,22 +335,4 @@ didFailToContinueUserActivityWithType:(NSString *)userActivityType
     }
 }
 
-#pragma mark - download handling
-
-- (void)downloadMovieFromURL:(NSURL *)url
-             fileNameOfMedia:(NSString *)fileName
-{
-    [[VLCDownloadViewController sharedInstance] addURLToDownloadList:url fileNameOfMedia:fileName];
-    //TODO: open DownloadViewController
-}
-
-#pragma mark - playback
-- (void)playWithURL:(NSURL *)url completion:(void (^ __nullable)(BOOL success))completion
-{
-    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
-    vpc.fullscreenSessionRequested = YES;
-    VLCMediaList *mediaList = [[VLCMediaList alloc] initWithArray:@[[VLCMedia mediaWithURL:url]]];
-    [vpc playMediaList:mediaList firstIndex:0 subtitlesFilePath:nil completion:completion];
-}
-
 @end

+ 0 - 4
Sources/VLCDropboxTableViewController.m

@@ -50,10 +50,6 @@
     self.controller.delegate = self;
 
 #if TARGET_OS_IOS
-    [[NSNotificationCenter defaultCenter] addObserver:self
-                                             selector:@selector(sessionWasUpdated:)
-                                                 name:VLCDropboxSessionWasAuthorized
-                                               object:nil];
 
     self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"dropbox-white"]];
 

+ 2 - 1
Sources/VLCGoogleDriveTableViewController.m

@@ -166,7 +166,8 @@
                                                                              additionalParameters:nil];
 
         // Perform the previously built request and saving the current authorization flow.
-        ((VLCAppDelegate *)[UIApplication sharedApplication].delegate).currentGoogleAuthorizationFlow = [OIDAuthState authStateByPresentingAuthorizationRequest:request presentingViewController:self
+
+        URLHandlers.googleURLHandler.currentGoogleAuthorizationFlow = [OIDAuthState authStateByPresentingAuthorizationRequest:request presentingViewController:self
            callback:^(OIDAuthState *_Nullable authState, NSError *_Nullable error) {
 
                self.authorizationInProgress = NO;

+ 2 - 0
VLC-iOS-Bridging-Header.h

@@ -1,5 +1,7 @@
+#import <AppAuth/AppAuth.h>
 #import <MediaLibraryKit/MediaLibraryKit.h>
 #import <MobileVLCKit/MobileVLCKit.h>
+#import <ObjectiveDropboxOfficial/ObjectiveDropboxOfficial.h>
 #import <PAPasscode/PAPasscodeViewController.h>
 #import <XKKeychain/XKKeychain.h>
 #import "VLCExternalDisplayController.h"

+ 22 - 0
VLC-iOS-Tests/Info.plist

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>

+ 30 - 0
VLC-iOS-Tests/URLHandlerTests.swift

@@ -0,0 +1,30 @@
+/*****************************************************************************
+ * URLHandlerTests.swift
+ * VLC for iOS
+ *****************************************************************************
+ * Copyright (c) 2018 VideoLAN. All rights reserved.
+ * $Id$
+ *
+ * Authors: Carola Nitz <caro # videolan.org>
+ *
+ * Refer to the COPYING file of the official project for license.
+ *****************************************************************************/
+
+import XCTest
+@testable import VLC_iOS
+
+class URLHandlerTests: XCTestCase {
+
+    func testVLCHandler() {
+        let handler = VLCCallbackURLHandler()
+        let transformURLString = { handler.transformVLCURL(URL(string: $0)!) }
+        XCTAssertEqual(transformURLString("vlc://http//test"), URL(string: "http://test")!, "strip the custom scheme and re-add the colon")
+        XCTAssertEqual(transformURLString("vlc://http://test"), URL(string: "http://test")!, "just strip the custom scheme")
+        XCTAssertEqual(transformURLString("vlc://test"), URL(string: "http://test")!, "strip the custom scheme and add http")
+        XCTAssertEqual(transformURLString("vlc://ftp//test"), URL(string: "ftp://test")!, "strip the custom scheme and readd :")
+        XCTAssertEqual(transformURLString("vlc://ftp://test"), URL(string: "ftp://test")!, "strip custom scheme")
+        XCTAssertEqual(transformURLString("vlc://https//test"), URL(string: "https://test")!, "strip the custom scheme and readd :")
+        XCTAssertEqual(transformURLString("vlc://https://test"), URL(string: "https://test")!, "strip the custom scheme")
+    }
+    
+}

+ 5 - 0
VLC-iOS-Tests/VLC-iOSTests-Bridging-Header.h

@@ -0,0 +1,5 @@
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+
+#import "VLC-iOS-Bridging-Header.h"

+ 299 - 1
VLC.xcodeproj/project.pbxproj

@@ -11,6 +11,7 @@
 		26F1BFD01A770408001DF30C /* libMediaVLC.xml in Resources */ = {isa = PBXBuildFile; fileRef = 26F1BFCF1A770408001DF30C /* libMediaVLC.xml */; };
 		29125E5617492219003F03E5 /* index.html in Resources */ = {isa = PBXBuildFile; fileRef = 29125E5417492219003F03E5 /* index.html */; };
 		2915544317490D4A00B86CAD /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2915544217490D4A00B86CAD /* Security.framework */; };
+		2A002188D3D6E76DEF147F08 /* libPods-VLC-iOS-VLC for iOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CD92B7DB3994F879C74A70E0 /* libPods-VLC-iOS-VLC for iOSTests.a */; };
 		411DC0FD20F650B10044305E /* VLCMediaSubcategory+VLCDragAndDrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412BE7521FC4947400ACCC42 /* VLCMediaSubcategory+VLCDragAndDrop.swift */; };
 		41251ED01FD0CF7900099110 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41251ECE1FD0CF7900099110 /* AppCoordinator.swift */; };
 		41273A3C1A955C4100A2EF77 /* VLCMigrationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 41273A3A1A955C4100A2EF77 /* VLCMigrationViewController.m */; };
@@ -23,6 +24,7 @@
 		4144156C20ECE6330078EC37 /* VLCFileServerSectionTableHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4144156B20ECE6330078EC37 /* VLCFileServerSectionTableHeaderView.swift */; };
 		4144C4661A0ED6C700918C89 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D3784E6183A99E1009EE944 /* Reachability.m */; };
 		4152F1621FEF19BD00F1908B /* KeychainCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4152F1611FEF19BD00F1908B /* KeychainCoordinator.swift */; };
+		41533C9E2113392F00EC3ABA /* URLHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41533C9D2113392F00EC3ABA /* URLHandlerTests.swift */; };
 		416443862048419E00CAC646 /* DeviceMotion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416443852048419E00CAC646 /* DeviceMotion.swift */; };
 		416DACB720B6DB9A001BC75D /* VLCPlayingExternallyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416DACB620B6DB9A001BC75D /* VLCPlayingExternallyView.swift */; };
 		4170152C209A1D3600802E44 /* MediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4170152B209A1D3600802E44 /* MediaViewController.swift */; };
@@ -38,6 +40,7 @@
 		418B144D20179C75000447AA /* VLCTabBarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418B144C20179C74000447AA /* VLCTabBarCoordinator.swift */; };
 		418B145020179CB9000447AA /* LayoutAnchorContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418B144F20179CB9000447AA /* LayoutAnchorContainer.swift */; };
 		418B145920179E50000447AA /* VLCDragAndDropManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418B145820179E50000447AA /* VLCDragAndDropManager.swift */; };
+		418E88412110E51B00DDA6A7 /* URLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 418E88402110E51B00DDA6A7 /* URLHandler.swift */; };
 		4195747D206A92ED00393A42 /* RemoteNetworkDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4195747C206A92ED00393A42 /* RemoteNetworkDataSource.swift */; };
 		419A2C661F37A4B70069D224 /* VLCStringsForLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = 419A2C651F37A4B70069D224 /* VLCStringsForLocalization.m */; };
 		419A2C681F37A4B70069D224 /* VLCStringsForLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = 419A2C651F37A4B70069D224 /* VLCStringsForLocalization.m */; };
@@ -378,6 +381,13 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
+		41533C96211338D600EC3ABA /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 7D94FCD316DE7D1000F2623B /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 7D94FCDA16DE7D1000F2623B;
+			remoteInfo = "VLC-iOS";
+		};
 		41B0BC8B1F73ED7D0063BA26 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = 7D94FCD316DE7D1000F2623B /* Project object */;
@@ -476,16 +486,19 @@
 /* Begin PBXFileReference section */
 		018698905AC809BE4496D84D /* Pods-vlc-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-vlc-ios.debug.xcconfig"; path = "Pods/Target Support Files/Pods-vlc-ios/Pods-vlc-ios.debug.xcconfig"; sourceTree = "<group>"; };
 		090F2933E962C424E9A80ABB /* Pods-VLC-iOS-no-watch.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-iOS-no-watch.release.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-iOS-no-watch/Pods-VLC-iOS-no-watch.release.xcconfig"; sourceTree = "<group>"; };
+		1C174528BFB46C3490E773B6 /* Pods-VLC-iOS-VLC for iOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-iOS-VLC for iOSTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-iOS-VLC for iOSTests/Pods-VLC-iOS-VLC for iOSTests.debug.xcconfig"; sourceTree = "<group>"; };
 		1C7724AABC40B96010E01F65 /* Pods-VLC-iOS-no-watch.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-iOS-no-watch.debug.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-iOS-no-watch/Pods-VLC-iOS-no-watch.debug.xcconfig"; sourceTree = "<group>"; };
 		260624EB5BF9F1FBE58BF816 /* libPods-VLC for iOSUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VLC for iOSUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		268BDA7D1B4FE1E200D622DD /* backArrow_black.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = backArrow_black.png; path = ImportedSources/OneDrive/src/LiveSDK/Library/Internal/Resources/backArrow_black.png; sourceTree = SOURCE_ROOT; };
 		26F1BFCF1A770408001DF30C /* libMediaVLC.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = libMediaVLC.xml; sourceTree = "<group>"; };
 		27E1FE5B2995E84315076C45 /* Pods-VLC-iOS-VLC for iOSUITests.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-iOS-VLC for iOSUITests.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-iOS-VLC for iOSUITests/Pods-VLC-iOS-VLC for iOSUITests.distribution.xcconfig"; sourceTree = "<group>"; };
+		27F3CE7A10961E7DA841096D /* Pods-VLC-iOS-VLC for iOSTests.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-iOS-VLC for iOSTests.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-iOS-VLC for iOSTests/Pods-VLC-iOS-VLC for iOSTests.distribution.xcconfig"; sourceTree = "<group>"; };
 		29125E5417492219003F03E5 /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = "<group>"; };
 		2915544217490D4A00B86CAD /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
 		2B1BC34A3BC4BCE7588BD68A /* Pods-VLC-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-tvOS/Pods-VLC-tvOS.release.xcconfig"; sourceTree = "<group>"; };
 		2CD27DA5256F95F3B8594C53 /* Pods-VLC-watchOS-Extension.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-watchOS-Extension.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-watchOS-Extension/Pods-VLC-watchOS-Extension.distribution.xcconfig"; sourceTree = "<group>"; };
 		31BBF816A2507AA02CD551A5 /* Pods-vlc-ios.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-vlc-ios.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-vlc-ios/Pods-vlc-ios.distribution.xcconfig"; sourceTree = "<group>"; };
+		31E8A4CF7E65EE1E129A2D21 /* Pods-VLC-iOS-VLC for iOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-iOS-VLC for iOSTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-iOS-VLC for iOSTests/Pods-VLC-iOS-VLC for iOSTests.release.xcconfig"; sourceTree = "<group>"; };
 		31FB5C5DBFDA9DD74867E2D1 /* libPods-VLC-iOS-VLC for iOSUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VLC-iOS-VLC for iOSUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		34DC96A915665AE9EABD30E8 /* Pods-VLC-iOS-Debug.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-iOS-Debug.release.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-iOS-Debug/Pods-VLC-iOS-Debug.release.xcconfig"; sourceTree = "<group>"; };
 		39DA271F3BEEFE045489B542 /* Pods-VLC-TV.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-TV.release.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-TV/Pods-VLC-TV.release.xcconfig"; sourceTree = "<group>"; };
@@ -507,6 +520,10 @@
 		4144156920ECD2620078EC37 /* VLCSectionTableHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = VLCSectionTableHeaderView.swift; path = Sources/VLCSectionTableHeaderView.swift; sourceTree = "<group>"; };
 		4144156B20ECE6330078EC37 /* VLCFileServerSectionTableHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = VLCFileServerSectionTableHeaderView.swift; path = Sources/VLCFileServerSectionTableHeaderView.swift; sourceTree = "<group>"; };
 		4152F1611FEF19BD00F1908B /* KeychainCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = KeychainCoordinator.swift; path = Sources/KeychainCoordinator.swift; sourceTree = "<group>"; };
+		41533C91211338D500EC3ABA /* VLC for iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "VLC for iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		41533C9C2113392E00EC3ABA /* VLC-iOSTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VLC-iOSTests-Bridging-Header.h"; sourceTree = "<group>"; };
+		41533C9D2113392F00EC3ABA /* URLHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLHandlerTests.swift; sourceTree = "<group>"; };
+		41533CA1211343D100EC3ABA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "VLC-iOS-Tests/Info.plist"; sourceTree = SOURCE_ROOT; };
 		416443852048419E00CAC646 /* DeviceMotion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeviceMotion.swift; path = Sources/DeviceMotion.swift; sourceTree = "<group>"; };
 		416DACB620B6DB9A001BC75D /* VLCPlayingExternallyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCPlayingExternallyView.swift; sourceTree = "<group>"; };
 		416DEFF51FEEA76A00F4FC59 /* LayoutAnchorContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LayoutAnchorContainer.swift; path = Sources/LayoutAnchorContainer.swift; sourceTree = "<group>"; };
@@ -570,6 +587,7 @@
 		418B144C20179C74000447AA /* VLCTabBarCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VLCTabBarCoordinator.swift; path = Sources/VLCTabBarCoordinator.swift; sourceTree = SOURCE_ROOT; };
 		418B144F20179CB9000447AA /* LayoutAnchorContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LayoutAnchorContainer.swift; path = Sources/LayoutAnchorContainer.swift; sourceTree = "<group>"; };
 		418B145820179E50000447AA /* VLCDragAndDropManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VLCDragAndDropManager.swift; path = Sources/VLCDragAndDropManager.swift; sourceTree = "<group>"; };
+		418E88402110E51B00DDA6A7 /* URLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = URLHandler.swift; path = Sources/URLHandler.swift; sourceTree = "<group>"; };
 		4195747C206A92ED00393A42 /* RemoteNetworkDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNetworkDataSource.swift; sourceTree = "<group>"; };
 		419A2C651F37A4B70069D224 /* VLCStringsForLocalization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCStringsForLocalization.m; sourceTree = "<group>"; };
 		419D7F041F54176900AF69A2 /* VLCTimeNavigationTitleView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = VLCTimeNavigationTitleView.xib; path = Resources/VLCTimeNavigationTitleView.xib; sourceTree = SOURCE_ROOT; };
@@ -1007,6 +1025,7 @@
 		CC87148317A56C85003C7383 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; lineEnding = 0; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.simpleColoring; };
 		CCAF837E17DE46D800E3578F /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
 		CCE2A22D17A5859E00D9EAAD /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
+		CD92B7DB3994F879C74A70E0 /* libPods-VLC-iOS-VLC for iOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VLC-iOS-VLC for iOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		CF79FF6D83030EB30CF91FD1 /* libPods-VLC-iOS-Debug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VLC-iOS-Debug.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		D0BB9293AAFE75CF15A6C8BC /* Pods-vlc-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-vlc-ios.release.xcconfig"; path = "Pods/Target Support Files/Pods-vlc-ios/Pods-vlc-ios.release.xcconfig"; sourceTree = "<group>"; };
 		D2711C77E8DF23DB7347ADC9 /* Pods-VLC-watchOS-Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-watchOS-Extension.release.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-watchOS-Extension/Pods-VLC-watchOS-Extension.release.xcconfig"; sourceTree = "<group>"; };
@@ -1171,6 +1190,14 @@
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+		41533C8E211338D500EC3ABA /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				2A002188D3D6E76DEF147F08 /* libPods-VLC-iOS-VLC for iOSTests.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		41B0BC831F73ED7D0063BA26 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -1275,6 +1302,16 @@
 			name = Coordinators;
 			sourceTree = "<group>";
 		};
+		41533C92211338D500EC3ABA /* VLC-iOS-Tests */ = {
+			isa = PBXGroup;
+			children = (
+				41533CA1211343D100EC3ABA /* Info.plist */,
+				41533C9D2113392F00EC3ABA /* URLHandlerTests.swift */,
+				41533C9C2113392E00EC3ABA /* VLC-iOSTests-Bridging-Header.h */,
+			);
+			path = "VLC-iOS-Tests";
+			sourceTree = "<group>";
+		};
 		41B0948320E6844700DE38AD /* MediaSubcategory */ = {
 			isa = PBXGroup;
 			children = (
@@ -1672,6 +1709,7 @@
 				DD7110ED1AF38AFD00854776 /* SharedSources */,
 				7D13293E1BA1F10100BE647E /* VLC-TV */,
 				41B0BC871F73ED7D0063BA26 /* VLC-iOS-UITests */,
+				41533C92211338D500EC3ABA /* VLC-iOS-Tests */,
 				7D94FCDD16DE7D1000F2623B /* Frameworks */,
 				7D94FCDC16DE7D1000F2623B /* Products */,
 				A77803FFDA800BD6A3DAED0C /* Pods */,
@@ -1686,6 +1724,7 @@
 				7D94FCDB16DE7D1000F2623B /* VLC for iOS.app */,
 				7D13293D1BA1F10100BE647E /* VLC-tvOS.app */,
 				41B0BC861F73ED7D0063BA26 /* VLC for iOSUITests.xctest */,
+				41533C91211338D500EC3ABA /* VLC for iOSTests.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -1760,6 +1799,7 @@
 				FF73F10D7535681155A87975 /* libPods-VLC-watchOS.a */,
 				260624EB5BF9F1FBE58BF816 /* libPods-VLC for iOSUITests.a */,
 				31FB5C5DBFDA9DD74867E2D1 /* libPods-VLC-iOS-VLC for iOSUITests.a */,
+				CD92B7DB3994F879C74A70E0 /* libPods-VLC-iOS-VLC for iOSTests.a */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -1770,6 +1810,7 @@
 				415163B01FD0AEC700161354 /* Coordinators */,
 				41273A391A955C4100A2EF77 /* VLCMigrationViewController.h */,
 				41273A3A1A955C4100A2EF77 /* VLCMigrationViewController.m */,
+				418E88402110E51B00DDA6A7 /* URLHandler.swift */,
 				7D6B08BB174A72A900A05173 /* VLCConstants.h */,
 				7DBBF180183AB3B80009A339 /* VLCAppDelegate.h */,
 				7DBBF181183AB3B80009A339 /* VLCAppDelegate.m */,
@@ -2193,6 +2234,9 @@
 				A945E02675B76197DEFE0486 /* Pods-VLC-iOS-VLC for iOSUITests.debug.xcconfig */,
 				E8DB9015F858FF86D1419F2C /* Pods-VLC-iOS-VLC for iOSUITests.release.xcconfig */,
 				27E1FE5B2995E84315076C45 /* Pods-VLC-iOS-VLC for iOSUITests.distribution.xcconfig */,
+				1C174528BFB46C3490E773B6 /* Pods-VLC-iOS-VLC for iOSTests.debug.xcconfig */,
+				31E8A4CF7E65EE1E129A2D21 /* Pods-VLC-iOS-VLC for iOSTests.release.xcconfig */,
+				27F3CE7A10961E7DA841096D /* Pods-VLC-iOS-VLC for iOSTests.distribution.xcconfig */,
 			);
 			name = Pods;
 			sourceTree = "<group>";
@@ -2494,6 +2538,26 @@
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
+		41533C90211338D500EC3ABA /* VLC for iOSTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 41533C98211338D600EC3ABA /* Build configuration list for PBXNativeTarget "VLC for iOSTests" */;
+			buildPhases = (
+				16C1AB96786C00EC189F241D /* [CP] Check Pods Manifest.lock */,
+				41533C8D211338D500EC3ABA /* Sources */,
+				41533C8E211338D500EC3ABA /* Frameworks */,
+				41533C8F211338D500EC3ABA /* Resources */,
+				92EAC000EE54D3264CC62EF8 /* [CP] Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				41533C97211338D600EC3ABA /* PBXTargetDependency */,
+			);
+			name = "VLC for iOSTests";
+			productName = "VLC for iOSTests";
+			productReference = 41533C91211338D500EC3ABA /* VLC for iOSTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 		41B0BC851F73ED7D0063BA26 /* VLC for iOSUITests */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = 41B0BC901F73ED7D0063BA26 /* Build configuration list for PBXNativeTarget "VLC for iOSUITests" */;
@@ -2563,10 +2627,17 @@
 			isa = PBXProject;
 			attributes = {
 				CLASSPREFIX = VLC;
-				LastSwiftUpdateCheck = 0720;
+				LastSwiftUpdateCheck = 0940;
 				LastUpgradeCheck = 0930;
 				ORGANIZATIONNAME = VideoLAN;
 				TargetAttributes = {
+					41533C90211338D500EC3ABA = {
+						CreatedOnToolsVersion = 9.4.1;
+						DevelopmentTeam = 75GAHG3SZQ;
+						LastSwiftMigration = 0940;
+						ProvisioningStyle = Automatic;
+						TestTargetID = 7D94FCDA16DE7D1000F2623B;
+					};
 					41B0BC851F73ED7D0063BA26 = {
 						CreatedOnToolsVersion = 9.0;
 						DevelopmentTeam = 75GAHG3SZQ;
@@ -2676,6 +2747,7 @@
 				7D94FCDA16DE7D1000F2623B /* VLC-iOS */,
 				7D13293C1BA1F10100BE647E /* VLC-tvOS */,
 				41B0BC851F73ED7D0063BA26 /* VLC for iOSUITests */,
+				41533C90211338D500EC3ABA /* VLC for iOSTests */,
 			);
 		};
 /* End PBXProject section */
@@ -2747,6 +2819,13 @@
 /* End PBXReferenceProxy section */
 
 /* Begin PBXResourcesBuildPhase section */
+		41533C8F211338D500EC3ABA /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		41B0BC841F73ED7D0063BA26 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -2913,6 +2992,24 @@
 			shellPath = /bin/sh;
 			shellScript = "${PODS_ROOT}/SwiftLint/swiftlint";
 		};
+		16C1AB96786C00EC189F241D /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-VLC-iOS-VLC for iOSTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
 		23B425DEB65EA5C88D873ED2 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -2944,6 +3041,66 @@
 			shellPath = /bin/sh;
 			shellScript = "python Tools/update_strings.py Resources/en.lproj/Localizable.strings";
 		};
+		92EAC000EE54D3264CC62EF8 /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${SRCROOT}/Pods/Target Support Files/Pods-VLC-iOS-VLC for iOSTests/Pods-VLC-iOS-VLC for iOSTests-resources.sh",
+				"${PODS_CONFIGURATION_BUILD_DIR}/HockeySDK/HockeySDKResources.bundle",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/Base.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/de.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/el.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/en.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/es.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/fr.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/it.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/ja.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/nl.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/pt-PT.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/pt.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/ru.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/sv.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/th.lproj",
+				"${PODS_ROOT}/InAppSettingsKit/InAppSettingsKit/Resources/tr.lproj",
+				"${PODS_ROOT}/MediaLibraryKit-prod/MappingModel_2_5_to_2_6.xcmappingmodel",
+				"${PODS_ROOT}/MediaLibraryKit-prod/MediaLibrary.xcdatamodeld",
+				"${PODS_ROOT}/VLC-LiveSDK/src/LiveSDK/Library/Internal/Resources/backArrow_black.png",
+				"${PODS_ROOT}/VLC-LiveSDK/src/LiveSDK/Library/Internal/LiveAuthDialog_iPad.xib",
+				"${PODS_ROOT}/VLC-LiveSDK/src/LiveSDK/Library/Internal/LiveAuthDialog_iPhone.xib",
+				"${PODS_CONFIGURATION_BUILD_DIR}/XKKeychain-iOS/XKKeychain.bundle",
+			);
+			name = "[CP] Copy Pods Resources";
+			outputPaths = (
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/HockeySDKResources.bundle",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Base.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/de.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/el.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/en.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/es.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fr.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/it.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ja.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nl.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pt-PT.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pt.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ru.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/sv.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/th.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/tr.lproj",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MappingModel_2_5_to_2_6.cdm",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MediaLibrary.momd",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/backArrow_black.png",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LiveAuthDialog_iPad.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LiveAuthDialog_iPhone.nib",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/XKKeychain.bundle",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-VLC-iOS-VLC for iOSTests/Pods-VLC-iOS-VLC for iOSTests-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 		9706D52A0CDF446E336826A6 /* [CP] Copy Pods Resources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -3085,6 +3242,14 @@
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
+		41533C8D211338D500EC3ABA /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				41533C9E2113392F00EC3ABA /* URLHandlerTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		41B0BC821F73ED7D0063BA26 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -3267,6 +3432,7 @@
 				8DD651C4208F786F0052EE68 /* VLCActionSheetSectionHeader.swift in Sources */,
 				7D37849E183A98DD009EE944 /* VLCThumbnailsCache.m in Sources */,
 				DD3EFF411BDEBCE500B68579 /* VLCNetworkServerBrowserSharedLibrary.m in Sources */,
+				418E88412110E51B00DDA6A7 /* URLHandler.swift in Sources */,
 				7D3784A1183A98EB009EE944 /* VLCBugreporter.m in Sources */,
 				7D3784A6183A98F5009EE944 /* VLCAboutViewController.m in Sources */,
 				8DE187812105DAB100A091D2 /* VideoViewController.swift in Sources */,
@@ -3354,6 +3520,11 @@
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
+		41533C97211338D600EC3ABA /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 7D94FCDA16DE7D1000F2623B /* VLC-iOS */;
+			targetProxy = 41533C96211338D600EC3ABA /* PBXContainerItemProxy */;
+		};
 		41B0BC8C1F73ED7D0063BA26 /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = 7D94FCDA16DE7D1000F2623B /* VLC-iOS */;
@@ -3470,6 +3641,123 @@
 /* End PBXVariantGroup section */
 
 /* Begin XCBuildConfiguration section */
+		41533C99211338D600EC3ABA /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 1C174528BFB46C3490E773B6 /* Pods-VLC-iOS-VLC for iOSTests.debug.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Automatic;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				DEVELOPMENT_TEAM = 75GAHG3SZQ;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				INFOPLIST_FILE = "VLC-iOS-Tests/Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 11.4;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_ENABLE_DEBUG_INFO = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = "org.example.com.VLC-for-iOSTests";
+				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_OBJC_BRIDGING_HEADER = "VLC-iOS-Tests/VLC-iOSTests-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 4.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VLC for iOS.app/VLC for iOS";
+				TEST_TARGET_NAME = "VLC-iOS";
+			};
+			name = Debug;
+		};
+		41533C9A211338D600EC3ABA /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 31E8A4CF7E65EE1E129A2D21 /* Pods-VLC-iOS-VLC for iOSTests.release.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Automatic;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				DEVELOPMENT_TEAM = 75GAHG3SZQ;
+				ENABLE_NS_ASSERTIONS = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				INFOPLIST_FILE = "VLC-iOS-Tests/Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 11.4;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_ENABLE_DEBUG_INFO = NO;
+				PRODUCT_BUNDLE_IDENTIFIER = "org.example.com.VLC-for-iOSTests";
+				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "VLC-iOS-Tests/VLC-iOSTests-Bridging-Header.h";
+				SWIFT_VERSION = 4.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VLC for iOS.app/VLC for iOS";
+				TEST_TARGET_NAME = "VLC-iOS";
+			};
+			name = Release;
+		};
+		41533C9B211338D600EC3ABA /* Distribution */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 27F3CE7A10961E7DA841096D /* Pods-VLC-iOS-VLC for iOSTests.distribution.xcconfig */;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Automatic;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				DEVELOPMENT_TEAM = 75GAHG3SZQ;
+				ENABLE_NS_ASSERTIONS = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				INFOPLIST_FILE = "VLC-iOS-Tests/Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 11.4;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_ENABLE_DEBUG_INFO = NO;
+				PRODUCT_BUNDLE_IDENTIFIER = "org.example.com.VLC-for-iOSTests";
+				PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "VLC-iOS-Tests/VLC-iOSTests-Bridging-Header.h";
+				SWIFT_VERSION = 4.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VLC for iOS.app/VLC for iOS";
+				TEST_TARGET_NAME = "VLC-iOS";
+			};
+			name = Distribution;
+		};
 		41B0BC8D1F73ED7D0063BA26 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = A945E02675B76197DEFE0486 /* Pods-VLC-iOS-VLC for iOSUITests.debug.xcconfig */;
@@ -4031,6 +4319,16 @@
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
+		41533C98211338D600EC3ABA /* Build configuration list for PBXNativeTarget "VLC for iOSTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				41533C99211338D600EC3ABA /* Debug */,
+				41533C9A211338D600EC3ABA /* Release */,
+				41533C9B211338D600EC3ABA /* Distribution */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		41B0BC901F73ED7D0063BA26 /* Build configuration list for PBXNativeTarget "VLC for iOSUITests" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (

+ 56 - 0
VLC.xcodeproj/xcshareddata/xcschemes/VLC-iOS-Tests.xcscheme

@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0940"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "41533C90211338D500EC3ABA"
+               BuildableName = "VLC for iOSTests.xctest"
+               BlueprintName = "VLC for iOSTests"
+               ReferencedContainer = "container:VLC.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 3 - 3
VLC.xcodeproj/xcshareddata/xcschemes/VLC-iOS.xcscheme

@@ -47,9 +47,9 @@
             skipped = "NO">
             <BuildableReference
                BuildableIdentifier = "primary"
-               BlueprintIdentifier = "4100F9691F78F77800D94E5C"
-               BuildableName = "VLC for iOSVideoCodecTests.xctest"
-               BlueprintName = "VLC for iOSVideoCodecTests"
+               BlueprintIdentifier = "41533C90211338D500EC3ABA"
+               BuildableName = "VLC for iOSTests.xctest"
+               BlueprintName = "VLC for iOSTests"
                ReferencedContainer = "container:VLC.xcodeproj">
             </BuildableReference>
          </TestableReference>