@@ -17,17 +17,18 @@ - (instancetype)initWithBasePath:(NSString *)basePath andScheme:(NSString *)sche
1717 return self;
1818}
1919
20- - (void )webView : (WKWebView *)webView startURLSchemeTask : (id <WKURLSchemeTask >)urlSchemeTask
21- {
22- NSString * startPath = @" " ;
23- NSURL * url = urlSchemeTask.request .URL ;
24- NSString * stringToLoad = url.path ;
25- NSString * scheme = url.scheme ;
26-
20+ - (void )webView : (WKWebView *)webView startURLSchemeTask : (id <WKURLSchemeTask >)urlSchemeTask {
21+ NSString * startPath = @" " ; // Initialize startPath
22+ NSURL * url = urlSchemeTask.request .URL ; // Extract URL from the request
23+ NSString * stringToLoad = url.path ; // Get the path component of the URL
24+ NSString * scheme = url.scheme ; // Get the scheme of the URL
25+ // Check if the URL's scheme matches the custom scheme
2726 if ([scheme isEqualToString: self .scheme]) {
27+ // If the path starts with "/_app_file_", strip this prefix
2828 if ([stringToLoad hasPrefix: @" /_app_file_" ]) {
2929 startPath = [stringToLoad stringByReplacingOccurrencesOfString: @" /_app_file_" withString: @" " ];
3030 } else {
31+ // Otherwise, build the path relative to self.basePath
3132 startPath = self.basePath ? self.basePath : @" " ;
3233 if ([stringToLoad isEqualToString: @" " ] || [url.pathExtension isEqualToString: @" " ]) {
3334 startPath = [startPath stringByAppendingString: @" /index.html" ];
@@ -38,28 +39,74 @@ - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)ur
3839 }
3940 NSError * fileError = nil ;
4041 NSData * data = nil ;
41- if ([self isMediaExtension: url.pathExtension]) {
42- data = [NSData dataWithContentsOfFile: startPath options: NSDataReadingMappedIfSafe error: &fileError];
43- }
44- if (!data || fileError) {
45- data = [[NSData alloc ] initWithContentsOfFile: startPath];
46- }
47- NSInteger statusCode = 200 ;
48- if (!data) {
49- statusCode = 404 ;
42+ NSString * mimeType = [self getMimeType: url.pathExtension]; // Get MIME type based on file extension
43+ NSInteger statusCode = 200 ; // Default to 200 OK status
44+ NSURL * localUrl = [NSURL URLWithString: url.absoluteString]; // Create a URL object
45+ id response = nil ;
46+
47+ NSFileManager *fileManager = [NSFileManager defaultManager ];
48+ if (![fileManager fileExistsAtPath: startPath]) {
49+ // File not found, return 404
50+ statusCode = 404 ;
51+ response = [[NSHTTPURLResponse alloc ] initWithURL: localUrl statusCode: statusCode HTTPVersion: nil headerFields: @{ @" Content-Type" : mimeType, @" Cache-Control" : @" no-cache" }];
52+ [urlSchemeTask didReceiveResponse: response];
53+ [urlSchemeTask didFinish ];
54+ return ;
55+ }
56+
57+ // Check for Range header
58+ NSString *rangeHeader = [urlSchemeTask.request valueForHTTPHeaderField: @" Range" ];
59+ NSUInteger fileLength = (NSUInteger )[[fileManager attributesOfItemAtPath: startPath error: nil ] fileSize ];
60+
61+ if (rangeHeader) {
62+ NSRange range = NSMakeRange (NSNotFound , 0 );
63+ if ([rangeHeader hasPrefix: @" bytes=" ]) {
64+ NSString *byteRange = [rangeHeader substringFromIndex: 6 ];
65+ NSArray <NSString *> *rangeParts = [byteRange componentsSeparatedByString: @" -" ];
66+ NSUInteger start = (NSUInteger )[rangeParts[0 ] integerValue ];
67+ NSUInteger end = rangeParts.count > 1 && ![rangeParts[1 ] isEqualToString: @" " ] ? (NSUInteger )[rangeParts[1 ] integerValue ] : fileLength - 1 ;
68+ range = NSMakeRange (start, end - start + 1 );
69+ }
70+
71+ if (range.location != NSNotFound ) {
72+ // Ensure range is valid
73+ if (range.location >= fileLength) {
74+ statusCode = 416 ; // Requested Range Not Satisfiable
75+ response = [[NSHTTPURLResponse alloc ] initWithURL: localUrl statusCode: statusCode HTTPVersion: nil headerFields: @{ @" Content-Type" : mimeType, @" Content-Range" : [NSString stringWithFormat: @" bytes */%lu " , (unsigned long )fileLength], @" Cache-Control" : @" no-cache" }];
76+ [urlSchemeTask didReceiveResponse: response];
77+ [urlSchemeTask didFinish ];
78+ return ;
79+ }
80+
81+ NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath: startPath];
82+ [fileHandle seekToFileOffset: range.location];
83+ data = [fileHandle readDataOfLength: range.length];
84+ [fileHandle closeFile ];
85+
86+ statusCode = 206 ; // Partial Content
87+ NSString *contentRange = [NSString stringWithFormat: @" bytes %lu -%lu /%lu " , (unsigned long )range.location, (unsigned long )(range.location + range.length - 1 ), (unsigned long )fileLength];
88+ response = [[NSHTTPURLResponse alloc ] initWithURL: localUrl statusCode: statusCode HTTPVersion: nil headerFields: @{ @" Content-Type" : mimeType, @" Content-Range" : contentRange, @" Content-Length" : [NSString stringWithFormat: @" %lu " , (unsigned long )range.length], @" Cache-Control" : @" no-cache" }];
89+ }
5090 }
51- NSURL * localUrl = [NSURL URLWithString: url.absoluteString];
52- NSString * mimeType = [self getMimeType: url.pathExtension];
53- id response = nil ;
54- if (data && [self isMediaExtension: url.pathExtension]) {
55- response = [[NSURLResponse alloc ] initWithURL: localUrl MIMEType: mimeType expectedContentLength: data.length textEncodingName: nil ];
56- } else {
57- NSDictionary * headers = @{ @" Content-Type" : mimeType, @" Cache-Control" : @" no-cache" };
91+
92+ if (!response) {
93+ // Load entire file if no range is requested
94+ data = [NSData dataWithContentsOfFile: startPath options: NSDataReadingMappedIfSafe error: &fileError];
95+ if (!data || fileError) {
96+ data = [[NSData alloc ] initWithContentsOfFile: startPath];
97+ }
98+
99+ if (!data) {
100+ statusCode = 404 ;
101+ }
102+
103+ NSDictionary *headers = @{ @" Content-Type" : mimeType, @" Cache-Control" : @" no-cache" };
58104 response = [[NSHTTPURLResponse alloc ] initWithURL: localUrl statusCode: statusCode HTTPVersion: nil headerFields: headers];
59- }
60-
105+ } // Send response and data to the WKWebView
61106 [urlSchemeTask didReceiveResponse: response];
62- [urlSchemeTask didReceiveData: data];
107+ if (data) {
108+ [urlSchemeTask didReceiveData: data];
109+ }
63110 [urlSchemeTask didFinish ];
64111
65112}
0 commit comments