I have a PDF Reader app using PDFKit with iOS 13 Swift, which has a bookmark button that can bookmark a particular page. It works fine for me while I am still looking at my PDF, but if I bookmark at least 1 page in my PDF and go back to my file browser, and pick my PDF again, the app crashes.
Here is my code:
private func updateBookmarkStatus() {
if let documentURL = pdfDocument?.documentURL?.absoluteString,
let bookmarks = UserDefaults.standard.array(forKey: documentURL) as? [Int],
let currentPage = pdfView.currentPage,
let index = pdfDocument?.index(for: currentPage) {
bookmarkButton.image = bookmarks.contains(index) ? #imageLiteral(resourceName: "Bookmark-P") : #imageLiteral(resourceName: "Bookmark-N")
}
}
and it crashes with
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
This update function is called upon loading each separate page of the PDF. However, when I exit and enter my PDF again, it crashes here because I think Swift can't find the array of bookmarks since at least one has been marked. My app only crashes if I mark at least 1 bookmark, then exit to my file browser, then open my PDF again.
Any help is greatly appreciated.
Related
We have an implementation with the UIDocumentPickerViewController that looks something like this:
case .openInitialization:
// Setup UIDocumentPicker.
if #available(iOS 14, *) {
documentsPicker = UIDocumentPickerViewController(forOpeningContentTypes: [
UTType.text,
UTType.utf8PlainText,
UTType.flatRTFD,
UTType.pdf])
} else {
documentsPicker = UIDocumentPickerViewController(documentTypes: [
String(kUTTypeText),
String(kUTTypeUTF8PlainText),
String(kUTTypeFlatRTFD),
String(kUTTypePDF)], in: .open)
}
Everything works great and we can select a document. When we select a document we get a document url but in some cases (especially with one drive) we get issues when we want to turn the url into a bookmark. Following code returns nil:
guard let bookmark = try? url.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil) else { return }
Do anyone have an idea to why this is happening? Or what we can do to get it to work without returning nil?
Edit:
We've tryed to add try catch and we got following error which doesn't quite help much: Error Domain=NSCocoaErrorDomain Code=260 (file doesn't exist).
Edit 2:
So if I open from archive directly into our app it works no issues at all. But we still need to work from UIDocumentPickerViewController.
Also for some reasons files unlocked this way will just work from UIDocumentPickerViewController afterward.
Files can also be opened from onedrive and from there be opened in another app (ours). But this does't and gives a file does not exist error as well.
Edit 3:
So I've tested and read a ton. I can tell that following will return false for some files picked by documentpicker:
var exist = FileManager.default.fileExists(atPath: url.path)
But again if I open the file just once from iOS archive app it will work perfectly fine afterward. If there just were some way to tell it to update/download like apples does.
Edit 4:
I've made a sample project demonstrating the problem at github .
I answered a similar question here: PDFKit doesn’t work on iPads while works fine on a simulator [iOS, Swift]
Can you check if wrapping your url in a security access scope helps?:
url.startAccessingSecurityScopedResource()
print(FileManager.default.fileExists(atPath: url.path))
url.stopAccessingSecurityScopedResource()
The above should print true. This is because these API's access files outside of the applications sandbox.
See: https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories
Used a technical ticket for apple and they came with a solution :D
NSFileCoordinator().coordinate(readingItemAt: url, options: .withoutChanges, error:&err, byAccessor: { (newURL: URL) -> Void in
do {
let bookmark = try newURL.bookmarkData()
} catch let error {
print("\(error)")
}
})
if let err = err {
print(err.localizedDescription)
}
I am making a PDF Reader similar to the Books App built in on iOS. My bookmarks work and update, except when I exit the PDF View and go back to document browsing (file picker), then open my PDF again. Then my app crashes:
private func updateBookmarkStatus() {
if let documentURL = pdfDocument?.documentURL?.absoluteString,
let bookmarks = UserDefaults.standard.array(forKey: documentURL) as? [Int],
let currentPage = pdfView.currentPage,
let index = pdfDocument?.index(for: currentPage) {
/*crashes on this line*/: bookmarkButton.image = bookmarks.contains(index) ? #imageLiteral(resourceName: "Bookmark-P") : #imageLiteral(resourceName: "Bookmark-N")
} else {
print("Bookmark Error")
}
}
with
Unexpectedly found nil while implicitly unwrapping an Optional value:
The line that I put the comment on just updates the bookmark to the "filled version", showing the user that the page is bookmarked.
The bookmark updates and works within the PDF. It is once I go out of the PDF, to the document browsing, then go back into the PDF that it crashes. It might be worth noting that "Bookmark Error" gets printed when I initially open the PDF for the first time.
If I do not touch the bookmark button, then I can go to document browsing and reopen the PDF as many times as I want. But as soon as I touch the bookmark button, I cannot re-open my PDF file again.
Any suggestions?
Thanks in advance.
I am storing information about meal timing in Apple's Health App/DB. When I review Apples Health App for meal information (top screen in the image) the source App Icon is included in the list.
When I attempt to do the same in my App (Bottom Screen in the image) it works fine for my Apps BundleID but I cannot retrieve the App Icon from the Health App supplied BundleID for an alternate source App. I am using the code shown below to try to achieve this. I am not sure what I am doing wrong, perhaps the wrong approach, perhaps missing some setup calls (like opening the Bundle before use). I have seen this used in third-party fitness/nutrition apps so there must be some way for doing this. I would appreciate any help or redirection of my effort. Thanks in advance.
func getAppIcon(_ theBundleID: String) -> UIImage {
guard let iconsDictionary = Bundle.init(identifier: theBundleID)!.infoDictionary?["CFBundleIcons"] as? NSDictionary,
let primaryIconsDictionary = iconsDictionary["CFBundlePrimaryIcon"] as? NSDictionary,
let iconFiles = primaryIconsDictionary["CFBundleIconFiles"] as? [String],
// First will be smallest for the device class, last will be the largest for device class
let firstIcon = iconFiles.first,
let icon = UIImage(named: firstIcon as String) else {
return UIImage()
}
return icon
}
I'm just starting developing apps with Swift 2.0, and as I'm working having problem with initiating a variable with the value of a text field in my app.
this is the function I'm having problems with. It's called when a button under the text field is pressed as submit.
#IBAction func checkName(sender: AnyObject) {
guard let name : String = nameField.text else { print("Name not valid.");return}
let checker = NameChecker()
let result = checker.nameChecker(name)
print(result)
}
this only thing this code returns on the XCode shell is "lldb". I also tried to use the debugger to figure out where I was messing up but unfortunately I found out that was harder than expected and I wasn't able to.
If you have any idea why my code is only returning "lldb" I would really appreciate is you could let me know since I've been experiencing this error quite often lately.
So I'm trying to play a random audio track from an array when a certain view loads. Everything's fine, but when I go to load that view, I get an error. Here is the class code of the View Controller:
class SoundGameViewController: UIViewController {
var levelOneSounds : [NSURL]!
var levelOneAudioPlayer: AVAudioPlayer! = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
var error : NSError?
levelOneSounds = NSBundle.mainBundle().URLsForResourcesWithExtension("mp3", subdirectory: "Level 1") as! [NSURL]
var randomSound = levelOneSounds[Int(arc4random_uniform(UInt32(levelOneSounds.count)))]
levelOneAudioPlayer = AVAudioPlayer(contentsOfURL: randomSound, error: &error)
if error != nil { println(error!) }
levelOneAudioPlayer.play()
}
The error shows up on the last line, levelOneAudioPlayer.play(). This is the error:
Thread 1: EXC_BAD_INSTRUCTION . . . (code = exc_1386_INVOP, subcode = 0 x 0
However, with one of the sounds, it actually ends up working and plays the sound when the view loads. But with all the other sounds, the view doesn't load at all and I get the error. Sometimes, the view does load but I get no sound and no error. Here is an image of the folders I hold all of the sounds in: http://i.stack.imgur.com/JBdGU.png
I have tried numerous times asking questions on Stack Overflow and research about it, but I just can't seem to get it. I have also looked for many tutorials on debugging, but I guess I'm not experienced enough to really understand what I need to do in this situation. I'm 13, and I don't have Apple Developer for much longer, and this is becoming really bothersome. I'll go as far as sending someone the project if they can fix this error.
I'm new to programming, so I'm sorry if I made an obvious mistake. Thanks!