3

I have a parse server set up, and as part of it, small PDFs (425KB) are stored on it. I need my Apple TV to be able to display these, but since they change often, it has to come from Parse server, and not just the main bundle where I update it with each update of the app. The issue I'm running into is the lack of an NSDocumentsDirectory on the Apple TV. How do y'all handle this? I've been using the Cache directory, but it seems to only work half the time with the code I am currently using. If I run it at launch from AppDelegate, by the time the PDF is needed, it may not be there, and if I have it set to run this code right when I need it, there is a delay, and sometimes, it simply doesn't show up. Would using NSTemporaryDirectory() be better? UPDATE, no, it doesn't. Works fine on simulator, on Apple TV, have to run the code two times to get it to both download, and draw the PDF

-(void)sermonTime {
    //Check if PFFile exists, if so, display PDF, if not, blank time.
    if ([self.entry[@"SermonPresIncluded"] isEqualToString:@"NO"]) {
        [self blankTime];
    }
    else {
        NSLog(@"SermonTime");
        PFFileObject *thumbnail = self.entry[@"SermonPDF"];
       
        [thumbnail getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
            NSString *documentsDirectory = [paths objectAtIndex:0];
            NSString *pdfPath = [[documentsDirectory stringByAppendingPathComponent:[self.entry valueForKey:@"DateOfService"]] stringByAppendingString:@".pdf"];
            
            [imageData writeToFile:pdfPath atomically:YES];
            
            
            NSURL *url = [NSURL fileURLWithPath:pdfPath];
            self.view.backgroundColor = [UIColor blackColor];
            self.arrayOfVerses = @[@"allverses"];
            CGPDFDocumentRef pdfDocument = [self openPDF:url];
            [self drawDocument:pdfDocument];

        }];
    }
      
}
user717452
  • 33
  • 14
  • 73
  • 149

2 Answers2

3
-(void)sermonTime {
    // Check if PFFile exists, if so, display PDF, if not, blank time.
    if ([self.entry[@"SermonPresIncluded"] isEqualToString:@"NO"]) {
        [self blankTime];
    }
    else {
        NSLog(@"SermonTime");
        PFFileObject *thumbnail = self.entry[@"SermonPDF"];

        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
        NSString *pdfPath = [[documentsDirectory stringByAppendingPathComponent:[self.entry valueForKey:@"DateOfService"]] stringByAppendingString:@".pdf"];

        NSFileManager *fileManager = [NSFileManager defaultManager];

        if ([fileManager fileExistsAtPath:pdfPath]) {
            // Use cached copy of PDF
            NSURL *url = [NSURL fileURLWithPath:pdfPath];
            self.view.backgroundColor = [UIColor blackColor];
            self.arrayOfVerses = @[@"allverses"];
            CGPDFDocumentRef pdfDocument = [self openPDF:url];
            [self drawDocument:pdfDocument];
        } else {
            // Download and save the PDF
            [thumbnail getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
                if (error) {
                    // Handle the error
                    NSLog(@"Error downloading PDF: %@", error);
                    [self blankTime];
                } else {
                    [imageData writeToFile:pdfPath atomically:YES];

                    // Use completion block to signal that the PDF is ready to display
                    dispatch_async(dispatch_get_main_queue(), ^{
                        NSURL *url = [NSURL fileURLWithPath:pdfPath];
                        self.view.backgroundColor = [UIColor blackColor];
                        self.arrayOfVerses = @[@"allverses"];
                        CGPDFDocumentRef pdfDocument = [self openPDF:url];
                        [self drawDocument:pdfDocument];
                    });
                }
            }];
        }
    }
}

Made some changes to the code.

It will first check if the PDF exists cache, it will use the PDF if it exists in cache and will only proceed download if it does not exists. Then, to make sure that PDF is downloaded and saved successfully you can use a completion block. With completion block, it will only proceed to draw it when the block is called to avoid the PDF does't show up.

Joshua Ooi
  • 1,139
  • 7
  • 18
  • I'm still having issues with it. I ran the app and when I got to the part where this code is ran, it stayed on the same PDF that was previously drawn on the screen. The PDF downloading was only 420 kb so I waited about 30 seconds, which should have been plenty of time. When it still didn't do it, I clicked the button again, and it jumped to the next PDF, going backwards loaded it up. I just simply can't understand what's happening, and it's hard to troubleshoot because it never fails on simulator. – user717452 Feb 23 '23 at 19:54
  • any thoughts? Every time I run with this code, it stays on the previously drawn pdf, and if I click again, it goes to the one after what it’s supposed to download and draw – user717452 Feb 26 '23 at 16:16
  • Did you set any duration or expiry for the cache of the pdf that has been download and save previously? – Joshua Ooi Feb 26 '23 at 16:28
  • I'm just using the code you had set before. The full way the app works is there is an array of names. All but "Sermon" is stored in the bundled files, but Sermon will change week to week, so it has to be downloaded. When one of the other PDFs is clicked, it draws it, clicking to the right advances to the next, and if it detects Sermon, then it runs the code posted above. My understanding is that it is not attempting to download the PDF to the cache until it needs it, so timing out shouldn't be a problem – user717452 Feb 26 '23 at 21:43
  • I added in some NSLogs, and it is finding the PDF in Default File Manager, despite the fact that it hasn't anywhere in code run getDataInBackgroundWithBlock. Of course, I'm trying this out on simulator, so let me try to do on actual device. – user717452 Feb 26 '23 at 22:44
  • Still can't get it to work...it thinks it already has that file, but draws nothing at all. I go to the next PDF in line, and then backtrack (which simply runs same SermonTime method again) and it draws it just fine. I'm completely lost – user717452 Mar 20 '23 at 04:46
  • I've been getting this pop up in Xcode as well "[connection] nw_resolver_start_query_timer_block_invoke [C2.1.1] Query fired: did not receive all answers in time for parsefiles.back4app.com:443" – user717452 Mar 21 '23 at 03:11
0

I think you have problems because of the cache. A solution could be to clear the cache whenever a new PDF becomes available. You can do this by deleting the cached PDF file before downloading the new one.

LE: Here's a revised version that uses writeToFile:atomically: and then calls the completion block

- (void)sermonTime {
if ([self.entry[@"SermonPresIncluded"] isEqualToString:@"NO"]) {
    [self blankTime];
}
else {
    NSLog(@"SermonTime");
    PFFileObject *thumbnail = self.entry[@"SermonPDF"];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *pdfPath = [[documentsDirectory stringByAppendingPathComponent:[self.entry valueForKey:@"DateOfService"]] stringByAppendingString:@".pdf"];

    NSFileManager *fileManager = [NSFileManager defaultManager];

    if ([fileManager fileExistsAtPath:pdfPath]) {
        // Use cached copy of PDF
        NSURL *url = [NSURL fileURLWithPath:pdfPath];
        self.view.backgroundColor = [UIColor blackColor];
        self.arrayOfVerses = @[@"allverses"];
        CGPDFDocumentRef pdfDocument = [self openPDF:url];
        [self drawDocument:pdfDocument];
    } else {
        // Delete the cached PDF file before downloading the new one
        [fileManager removeItemAtPath:pdfPath error:nil];

        // Download and save the PDF
        [thumbnail getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
            if (error) {
                NSLog(@"Error downloading PDF: %@", error);
                [self blankTime];
            } else {
                [imageData writeToFile:pdfPath atomically:YES];

                // Use completion block to signal that the PDF is ready to display
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSURL *url = [NSURL fileURLWithPath:pdfPath];
                    self.view.backgroundColor = [UIColor blackColor];
                    self.arrayOfVerses = @[@"allverses"];
                    CGPDFDocumentRef pdfDocument = [self openPDF:url];
                    [self drawDocument:pdfDocument];
                });
            }
        }];
    }
}
}
Stefan
  • 384
  • 7
  • I tried it, but got an error message on ` [imageData writeToFile:pdfPath atomically:YES completion` that read "No visible @interface for 'NSData' declares the selector 'writeToFile:atomically:completion" I changed it a little to still do dispatch _async but I got the same issues as before. It simply won't draw the PDF until I go past it, then retreat back to try a 2nd time. – user717452 Mar 16 '23 at 20:05
  • I've tried to update the code and I edit the post above, please let me know if now works, i'll try to help you – Stefan Mar 17 '23 at 08:33
  • It still doesn't want to work 100% of the time. It will simply not load that PDF half the time, I end up having to take steps to run the code two times. – user717452 Mar 20 '23 at 03:16
  • Here is what happens when I run this on an actual Apple TV. I added an NSLog to your code in the `fileManager fileExistsAtPath` portion. It showed the log for that portion, indicating that the file did exist, but then did NOT load it for some reason – user717452 Mar 20 '23 at 03:58
  • I added some logs in the drawDocument code to confirm it was running, and it is, but it just won't load that PDF – user717452 Mar 20 '23 at 04:08
  • It shows that it has the file downloaded already, runs the drawDocument code, and even correctly can log how many pages are in the PDF, but it simply doesn't draw it, and doesn't give any error messages. If I move on to the next PDF in the array, and then backtrack (which simply runs the same code you have posted here again) it will the second time load it – user717452 Mar 20 '23 at 04:33
  • I've been getting this pop up in Xcode as well "[connection] nw_resolver_start_query_timer_block_invoke [C2.1.1] Query fired: did not receive all answers in time for parsefiles.back4app.com:443" – user717452 Mar 21 '23 at 03:11
  • The error message you mentioned `([connection] nw_resolver_start_query_timer_block_invoke [C2.1.1] Query fired: did not receive all answers in time for parsefiles.back4app.com:443)` suggests that there might be a network issue causing the delay in receiving the PDF file. To address this issue, you can try adding a network reachability check before attempting to download the PDF. – Stefan Mar 21 '23 at 07:32