NSURL URLByAppendingPathExtension Crash

538 views Asked by At

The following code ran fine on iOS 14.0, but now on iOS 15.0, causes a crash.

var noteFile: NoteFile!

func saveToNoteFile(completion: ((Bool) -> Void)?) {
            let mutableAttrs = NSMutableAttributedString(attributedString: textView.textStorage)
      
        noteFile.attrs = mutableAttrs

        noteDescriptor.overview = (noteFile.attrs.string as NSString).substring(with: NSMakeRange(0, min(150, noteFile.attrs.length)))
        
        self.noteDescriptor.title = String(noteFile.attrs.string.split(separator: "\n").first ?? "" )
        
        let images = NEFileManager.getImagesFromAssetFolder(noteId: noteDescriptor.id)
        for (idx, image) in images.enumerated() {
            if idx == 0 { noteDescriptor.descImage1 = image }
            else if idx == 1 { noteDescriptor.descImage2 = image }
            else { break }
        }

        noteFile.save(to: noteFile.fileURL, for: .forOverwriting) { (success) in //Crash occurs HERE.
            completion?(success)
            print("saved", self.noteDescriptor.title)
        }
    }

The error message is as follows:

NSURL URLByAppendingPathExtension:]: component, components, or pathExtension cannot be nil

What does Xcode mean by 'components' and how can I ensure this variable is not nil?

Thanks.

Edit: NoteFile object below.

    class NoteFile: UIDocument {
    private var noteId: String!
    private let name = "note"
    var attrs: NSMutableAttributedString!
    
    init(noteDescriptor: NoteDescriptor) {
        self.noteId = noteDescriptor.id
        let url = NEFileManager.noteFromAssetFolder(id: noteId)?.appendingPathComponent(name)
        super.init(fileURL: url!)
    }
    
    // MARK: I/O Operations
    
    override func load(fromContents contents: Any, ofType typeName: String?) throws {
        if let fileWrapper = contents as? FileWrapper {
            if let textFileWrapper = fileWrapper.fileWrappers![name] {
                if let data = textFileWrapper.regularFileContents {
                    let string = String(data: data, encoding: .unicode)!
                    self.attrs = NSMutableAttributedString(string: string)
                    self.attrs.addAttribute(.font, value: UIFont.systemFont(ofSize: 16), range: NSRange(location: 0, length: string.count))
                }
            }
        }
    }
    
    override func contents(forType typeName: String) throws -> Any {
        let contentsFileWrapper = FileWrapper(directoryWithFileWrappers: [:])
        if let data = self.attrs.string.data(using: .unicode) {
            let textFileWrapper = FileWrapper(regularFileWithContents: data)
            textFileWrapper.preferredFilename = name
            contentsFileWrapper.addFileWrapper(textFileWrapper)
        }
        return contentsFileWrapper
    }
    

NEFileManager class

import UIKit

public class NEFileManager {
    
    // query app's asset folder path and create  if not existed
    class func assetFolderURL() -> URL? {
        do {
            var path = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
            path = path.appendingPathComponent(APP_DOCUMENT_ASSET_FOLDER)
            var isDir : ObjCBool = false
            if FileManager.default.fileExists(atPath: path.path, isDirectory: &isDir) {
                if isDir.boolValue {
                    return path
                }
            }
            try FileManager.default.createDirectory(at: path, withIntermediateDirectories: false, attributes: nil)
            return path
            
        } catch (let error) {
            print(error)
        }
        return nil
    }
    
    class func noteFromAssetFolder(id: String) -> URL? {
        guard let assetUrl = assetFolderURL() else { return nil }
        do {
            let path = assetUrl.appendingPathComponent(id)
            var isDir : ObjCBool = false
            if FileManager.default.fileExists(atPath: path.path, isDirectory: &isDir) {
                if isDir.boolValue {
                    return path
                }
            }
            try FileManager.default.createDirectory(at: path, withIntermediateDirectories: false, attributes: nil)
            return path
        } catch (let error) {
            print(error)
        }
        return nil
    }
    
    public class func deleteNoteFromAssetFolder(id: String) -> Bool {
        guard let noteFolderURL = noteFromAssetFolder(id: id) else { return false }
        do {
            try FileManager.default.removeItem(at: noteFolderURL)
            return true
        } catch (let error) {
            print(error)
        }
        return false
    }
    
    class func createdDateOfFile(path: String) -> NSDate? {
        do {
            let attrs = try FileManager.default.attributesOfItem(atPath: path)
            return attrs[FileAttributeKey.creationDate] as? NSDate
        }catch (let error) {
            print(error)
        }
        return nil
    }
    
    class func modifiedDateOfFile(path: String) -> NSDate? {
        do {
            let attrs = try FileManager.default.attributesOfItem(atPath: path)
            return attrs[FileAttributeKey.modificationDate] as? NSDate
        }catch (let error) {
            print(error)
        }
        return nil
    }
    
    class func writeImageToAssetFolder(noteId id: String, image: UIImage, fileName: String) -> Bool {
        guard let noteFolderURL = noteFromAssetFolder(id: id) else { return false }
        var filePath = noteFolderURL.appendingPathComponent(fileName)
        filePath = filePath.appendingPathExtension("jpg")
        do {
            if let imageData =  UIImageJPEGRepresentation(image, 1.0) {
                try imageData.write(to: filePath, options: .atomic)
                return true
            }
        } catch (let error){
            print(error)
        }
        return false
    }
    
    class func getImagesFromAssetFolder(noteId id: String) -> [String] {
        do {
            guard let noteFolderURL = noteFromAssetFolder(id: id) else { return [String]() }
            let files = try FileManager.default.contentsOfDirectory(atPath: noteFolderURL.path)
            var images = [String]()
            for file in files {
                if (file as NSString).pathExtension == "jpg" {
                    images.append(file)
                }
            }
            return images
        } catch (let error) {
            print(error)
        }
        return [String]()
    }
    
    class func writeDataToAssetFolder(noteId id: String, data: Data, fileName:String) -> Bool {
        guard let noteFolderURL = noteFromAssetFolder(id: id) else { return false }
        let filePath = noteFolderURL.appendingPathComponent(fileName)
        do {
            try data.write(to: filePath, options: .atomic)
            return true
        }catch (let error){
            print(error)
        }
        return false
    }
    
    class func moveFileToAssetFolder(noteId id: String, sourceUrl: URL, fileName: String) -> Bool {
        guard let noteFolderURL = noteFromAssetFolder(id: id) else { return false }
        let filePath = noteFolderURL.appendingPathComponent(fileName)
        do {
            if !FileManager.default.fileExists(atPath: filePath.path) {
                try FileManager.default.moveItem(at: sourceUrl, to: filePath)
            }
            return true
        }catch (let error){
            print(error)
        }
        return false
    }
    
    class func getImageFromAssetFolder(noteId id:String, image name: String) -> UIImage? {
        guard let noteFolderURL = noteFromAssetFolder(id: id) else { return nil }
        let imagePath = noteFolderURL.appendingPathComponent(name)
        do {
            if FileManager.default.fileExists(atPath: imagePath.path) {
                let data = try Data(contentsOf: imagePath)
                return UIImage(data: data)
            }
        } catch (let error) {
            print(error)
        }
        
        return nil
    }
    
    class func getFileURLFromAssetFolder(noteId id:String, file name:String) -> URL? {
        guard let noteFolderURL = noteFromAssetFolder(id: id) else { return nil }
        let filePath = noteFolderURL.appendingPathComponent(name)
        if FileManager.default.fileExists(atPath: filePath.path) {
            return filePath
        }
        return nil
    }
}
0

There are 0 answers