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
}
}