Cocoa: how to unarchive a NSMutableAttributedString with archived attributes? - swift

I try to unarchive an archived NSMutableAttributedString in a macos-app, written in swift. I get back the text but without any attributes. The attributes are in the exported serialisation but it is ignored by NSMutableAttributedString(coder: unarchiver)!
Let's dive in: first I create a AttributedString and format one word with a red background:
let content = NSMutableAttributedString(string: "Dogs like to play with balls.")
content.addAttributes([NSAttributedString.Key.backgroundColor: NSColor.red], range: NSMakeRange(5, 4))
Then I serialise it with a NSKeyedArchiver:
let archiver = NSKeyedArchiver(requiringSecureCoding: false)
archiver.outputFormat = .xml
content.encode(with: archiver)
archiver.finishEncoding()
I stored the result in a variable:
let serialisedData = archiver.encodedData
to write it back into a new NSMutableAttributedString:
let unarchiver = try NSKeyedUnarchiver(forReadingFrom: serialisedData)
let restored = NSMutableAttributedString(coder: unarchiver)!
the restored AttributedString does not have any formations. :-(
Is there is a way to restore the attributes? I can see that the color information is in the serialisedData stream. I tried outputFormat with .xml and .binary with same results.
Here is the Playground output:

Got it!
let archiver = try NSKeyedArchiver.archivedData(withRootObject: content, requiringSecureCoding: false)
and
let restored = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSMutableAttributedString.self, from: archiver)
Doh!

Related

how to store [UIImage] into Core Data and retrieve to [UIImage] from binary date

my code is like these:
//save to core data
func addPaper(){
let paper = Paper(context: self.context)
paper.id = UUID()
paper.subject = self.subject
paper.score = Float(self.score) ?? 0
paper.title = self.title
let imgs = try? NSKeyedArchiver.archivedData(withRootObject: self.images, requiringSecureCoding: true)
paper.images = imgs
try? self.context.save()
}
//retrieve to [UIImage]
let imgs = NSKeyedUnarchiver.unarchivedObject(ofClass: [UIImage], from: paper.images!)
there is an error tip:Static method 'unarchivedObject(ofClass:from:)' requires that '[UIImage]' conform to 'NSCoding'
I don't know what to do next, can anyone give me some help?
You should not store image in CoreData. Maybe store in base64 format, and you can encode/decode whenever you want.
Base64 To image:
func base64ToImage(data: String) -> Data{
let encodedImageData = data
let imageData = Data(base64Encoded: encodedImageData)
return imageData!
}
imageView.image = UIImage(data: dataDecoded)
Image to base64
func imageToBase64(image: UIImage) -> String {
return image.pngData()!
.base64EncodedString()
}
maybe you need this, any object can be saved to core data.
:https://stackoverflow.com/questions/56453857/how-to-save-existing-objects-to-core-data
I copied the following steps from others, hope it helps
1.making your custom types (Exercise) subclass of NSObject
2.setting the attribute's type in the core data model to Transformable
3.setting the CustomClass to [Exercise]

Round-trip encoding and decoding with NSKeyedArchiver and NSKeyedUnarchiver

In the process of implementing init(coder:) for a custom NSView subclass, I came across some strange behavior with NSKeyedArchiver and NSKeyedUnarchiver that I still don't entirely understand. Consider this sample code:
let label = NSTextField(labelWithString: "Test")
// Encode
let data = try NSKeyedArchiver.archivedData(withRootObject: label, requiringSecureCoding: false)
// Decode
try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? NSTextField
This appears to encode and decode an NSTextField as expected. However, if I try to use decodeTopLevelObject() instead of unarchiveTopLevelObjectWithData(_:), the result is nil:
// Encode
let data = try NSKeyedArchiver.archivedData(withRootObject: label, requiringSecureCoding: false)
// Decode
let decoder = try NSKeyedUnarchiver(forReadingFrom: data)
decoder.decodeTopLevelObject() as? NSTextField // nil
Similarly, if I try to use encodedData instead of archivedData(withRootObject:requiringSecureCoding:), the result is nil:
// Encode
let coder = NSKeyedArchiver(requiringSecureCoding: false)
coder.encodeRootObject(label)
let data = coder.encodedData
// Decode
try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? NSTextField // nil
The result is even nil if I use encode(_:forKey:) and decodeObject(forKey:):
// Encode
let coder = NSKeyedArchiver(requiringSecureCoding: false)
coder.encode(label, forKey: "label")
let data = coder.encodedData
// Decode
let decoder = try NSKeyedUnarchiver(forReadingFrom: data)
decoder.decodeObject(forKey: "label") as? NSTextField // nil
I'm surprised that the first example above appears to work correctly but none of the others do (especially the last one). Could someone help me understand what's going on here?
If you read the documentation for init(forReadingFrom:) it states:
This initializer enables requiresSecureCoding by default....
This has probably been the main source of your confusion. Setting requiresSecureCoding back to false, then, will make the following work:
/* ENCODING */
let archiver = NSKeyedArchiver(requiringSecureCoding: false)
archiver.encodeRootObject(label) // same as .encode(label)
archiver.encode(label, forKey: "SOME_CUSTOM_KEY")
archiver.finishEncoding() // as per documentation
let data = archiver.encodedData
/* DECODING */
let unarchiver = try! NSKeyedUnarchiver(forReadingFrom: data)
// DON'T FORGET THIS!!
unarchiver.requiresSecureCoding = false
let firstResult = unarchiver.decodeTopLevelObject() as! NSTextField . // same as .decodeObject()
let secondResult = unarchiver.decodeObject(forKey: "SOME_CUSTOM_KEY") as! NSTextField
unarchiver.finishDecoding() // as per documentation
When it comes to encoding and decoding correctly, just make sure you have matching keys. encodeRootObject(_:), which is implemented the same as encode(_:), internally uses the key of nil, so then just call decodeTopLevelObject(), or decodeObject().
On the other hand, NSKeyedArchiver.archivedData(withRootObject:requiringSecureCoding:) uses the key NSKeyedArchiveRootObjectKey, so you could technically decode by performing:
let value = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as! NSTextField
...but you wouldn't want to do this, since it's an internal implementation that theoretically could change. Instead you'd just use NSKeyedArchiver.unarchiveTopLevelObjectWithData(_:), as you did in your working example.
Note: if you are using secure coding, there are other considerations to be made, but I think that's beyond the scope of this question.

Retrieve NSDictionary Value with swift 4

I try to retrieve exif data from a picture.
I can load it in a Dictionary, but I am unable to use this Dictionary.
my Current code is :
import Cocoa
import ImageIO
let path = "/Volumes/Olivier/Original/Paysage/affoux/_OPI7684.NEF"
let UrlPath = URL(fileURLWithPath: path)
UrlPath.isFileURL
UrlPath.pathExtension
UrlPath.hasDirectoryPath
let imageSource = CGImageSourceCreateWithURL(UrlPath as CFURL, nil)
let imageProp = CGImageSourceCopyPropertiesAtIndex(imageSource!, 0, nil)
var key = "kCGImagePropertyWidth" as NSString
let h :NSDictionary = CFDictionaryGetValue(imageProp, kCGImagePropertyWidth)
The last line, doesn't work at all.
Any solution ?
Thank's
The problem is that your key name is wrong. You mean kCGImagePropertyPixelWidth. And it's not a string. It's a constant. So it should not be in quotes; just use the constant directly, and don't worry what its value is.
I would suggest also that you convert to a Swift dictionary earlier in the process. Here is actual working code that you can model yourself after:
let src = CGImageSourceCreateWithURL(url as CFURL, nil)!
let result = CGImageSourceCopyPropertiesAtIndex(src, 0, nil)!
let d = result as! [AnyHashable:Any]
let width = d[kCGImagePropertyPixelWidth] as! CGFloat
let height = d[kCGImagePropertyPixelHeight] as! CGFloat
Of course that code is pretty bad because every single line contains an exclamation mark (which means "crash me"), but in real life I don't crash, so I've allowed it to stand.

Swift core data FetchRequest NSData to UIIMage

Good morning all, I am using Swift "new for me" and core data I am trying to fetch my stored UIIMage from core data the following way. FYI it is saved as Binary Data in core. And I can see the data if I NSLog it. My Fetch Request looks like this. I have NO ERRORS but my image is not showing.
When I save to Core Data the NSLog looks like this..
Did I get to Save Image
2015-10-12 09:05:43.307 Car-Doc-Safe-Plus[13972:3524049] The NewImage has this in it (entity: Documents; id: 0x7fd4c0432060 ; data: {
autoClub = nil;
driverLicense = <89504e47 0d0a1a0a 0000000d 49484452 00000215 00000155 08020000 00d7368a d8000000 01735247 4200aece 1ce90000 001c>;
insuranceID = nil;
noteText = nil;
plate = nil;
registration = nil;
})
But when i Fetch it looks like this..
The Request has this in it (entity: Documents; predicate: ((null)); sortDescriptors: ((null)); type: NSManagedObjectResultType; )
**override func viewDidLoad() {
super.viewDidLoad()
let appDel:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context:NSManagedObjectContext = appDel.managedObjectContext!
let request = NSFetchRequest(entityName: "Documents")
request.returnsObjectsAsFaults = false;
let results : NSArray
try! results = context.executeFetchRequest(request)
if results.count > 0 {
let res = results[0] as! NSManagedObject
carImageView.image = res.valueForKey("driverLicense")as? UIImage
}
}**
I know I am missing something but I cannot figure it out. Any help is greatly appreciated.
JZ
The "binary data" type in Core Data is intended for reading and writing NSData objects. Since you want to read/write UIImage, you need to use the "transformable" type. With transformable, Core Data will attempt to use NSCoding to encode/decode the data, converting it to/from NSData as needed (Swift as? will not do this). Since UIImage conforms to NSCoding, you don't need to do any extra work except to tell Core Data to convert the data.

Swift - Populate core data from premade sqlite file

Not the same question as here.
I have a premade sqlite file that has three columns: chapter, verseNumber and verseText. My core data entity has the same name for its attributes. How do I populate the core data database from my sqlite file?
Core Data can not read arbitrary SQLite files. You would have to convert the file into a Core Data database manually. The format that Core Data uses to read from / write to SQLite is proprietary.
You have to use SQLite directly to read the data. I'd suggest using something like FMDB for reading the data. To put the data into a Core Data store, you should follow the guidelines of Efficiently Importing Data.
Here is what I ended up doing:
#IBAction func getSQLDB (sender: NSButton) {
let documentsFolder = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let path = documentsFolder.stringByAppendingPathComponent("kjv.db")
println(documentsFolder)
let database = FMDatabase(path: path)
if !database.open() {
println("Unable to open database")
return
}
//if let rs = database.executeQuery("SELECT * FROM bible WHERE (book = 'Gen') AND (chapter = '1')", withArgumentsInArray: nil) {
if let rs = database.executeQuery("SELECT * FROM bible WHERE (book = 'Genesis') OR (book = 'Exodus')", withArgumentsInArray: nil) {
while rs.next() {
let content = rs.stringForColumn("content")
let verseNumber: Int! = rs.stringForColumn("verse").toInt()
let chapter: Int! = rs.stringForColumn("chapter").toInt()
let name = rs.stringForColumn("book")
// Loops through all the information from the query and creates a book
makeBook(name, content: content, chapter: chapter, verseNumber: verseNumber)
}
} else {
println("select failed: \(database.lastErrorMessage())")
}
database.close()
}