Rewriting metadata into .jpg file by using Swift 2.2 OS X - swift

I'm try rewrite metadata from JPG file. I want addition one keyword to metadata. Xcode don't give any errors, but file not changed.
Here is my code:
var pathToOpenFile:NSURL?
Next I write path from file to variable "pathToOpenFile".
If user pushed ENTER button into NSTextField, then work action:
#IBAction func endEditKeys(sender: AnyObject) {
if (pathToOpenFile != nil) {
let imageSource = CGImageSourceCreateWithURL(pathToOpenFile!, nil)
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource!, 0, nil)! as NSDictionary;
let exifDict = imageProperties.valueForKey("{IPTC}") as! NSDictionary;
var Keywords:[String] = exifDict.valueForKey("Keywords") as! [String];
Keywords.append("ANY")
exifDict.setValue(Keywords, forKey: "Keywords")
let type = CGImageSourceGetType(imageSource!)
let count = CGImageSourceGetCount(imageSource!)
let mutableData = NSMutableData(contentsOfURL: pathToOpenFile!)
let destination = CGImageDestinationCreateWithData(mutableData!, type!, count, nil)
let removeExifProperties: CFDictionary = exifDict
for i in 0..<count {
CGImageDestinationAddImageFromSource(destination!, imageSource!, i, removeExifProperties)
}
CGImageDestinationFinalize(destination!)
}
}
Can you help me, why it isn't work (not change metadata)? Thank you!

Thanks all!
It is working, just need rewrite .JPG file in end.
if let _ = try? mutableData!.writeToURL(pathToOpenFile!, options: NSDataWritingOptions.AtomicWrite) {
// if you need, do anything. For example "print ("Savig file")"
}

Related

Need help about UIImage to change into a string

Need help...
private func classifyImage() {
let currentImageName = classifyimage
guard let image = UIImage(named: currentImageName?.convertToBuffer() as! String),
let resizedImage = image.resizeImageTo(size:CGSize(width: 224, height: 224)),
let buffer = resizedImage.convertToBuffer()
else {
return
}
let output = try? model.prediction(image: buffer)
}
Originally the codes should be produced as arrays but I'm trying to put image input without arrays and recognize the images. It keep saying it has to be a string and I have placed an unwrapped but still it doesn't work... Any idea what to do?

Should a plist file update when data is saved to it?

I'm aware that this has been discussed in many other ways, but I have tried using previous answers and have remained stuck on this problem for over a week.
I am trying to gather questions from one plist file (with unique question IDs) and then saving that question ID with the user's answer into another plist.
I am having no issues loading the questions, but the following code doesn't seem to update the AnsweredQs plist file.
It prints "true" which suggests the write worked, but when I return to xcode there is no change to the plist.
func documentFilePath(fileName: String) -> String {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent(fileName)
return fileURL.path
}
func saveQuestion(ans: String) {
let path = documentFilePath(fileName: "AnsweredQs.plist")
let data = NSMutableDictionary()
data.setValue(QuestionView.questionID, forKey: "id")
data.setValue(ans, forKey: "answer")
data.setValue(Date(), forKey: "date")
data.setValue(loadQuestionData(id: QuestionView.questionID).expiry, forKey: "expiry")
data.setValue(loadQuestionData(id: QuestionView.questionID).maxAge, forKey: "max-age")
let didWriteSucess = data.write(toFile: path, atomically: false)
print(didWriteSucess)
print(data)
}
In addition to this, I am wanting to set up a check when the question is randomly generated so that only unanswered questions are selected for the user to answer. I've started work on this (although this code is not complete) and it doesn't seem to ever find anything in the AnsweredQs plist, suggesting further that the save isn't actually working.
func checkQuestion(id: Any) -> Bool {
var trueOrFalse = Bool()
if QuestionView.questionNumber >= 1 {
var questionDict: NSDictionary = [:]
if let url = Bundle.main.url(forResource:"AnsweredQs", withExtension: ".plist"),
let dict = NSDictionary(contentsOf: url) as? [String:Any] {
print(dict)
questionDict = dict as NSDictionary
let allValues = [questionDict]
if allValues.count != 0 {
if allValues.description == id as! String{
trueOrFalse = true
print("true\(allValues)")
} else {
trueOrFalse = false
print("false\(allValues)")
}
}
}
} else {
trueOrFalse = false
print("didn't check")
}
return(trueOrFalse)
It's my first app and I'm trying to figure it all out myself, but I've gotten myself so stuck that all I was left with was to ask here.
Thanks in advance x

Issue with file manager in swift 3.1

I have the below code that worked fine, up until the release of swift 3.1.
func loadImage() {
id = userPhotoModel.id
let fileManager = FileManager.default
let imagePath = (self.getDirectoryPath() as NSString).appendingPathComponent(photoName)
if fileManager.fileExists(atPath: imagePath){
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath))
print("name of photo retrieved: \(photoName)")
self.userPhoto.image = imageFromPath
}else{
print("No Image")
}
}
Now, swift 3.1 wants to add as! String to:
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath) as! String)
However, when I run the app, it crashes in this location with no error message as per the below image.
What is causing this?
EDIT: Here is the resizeImage func
fileprivate func resizeImage(named name: String) -> UIImage
{
var image = UIImage(named: name)
if image!.size.height > image!.size.width
{
self.userPhoto.contentMode = .scaleAspectFit
}
else
{
image = image!.resizeTo(self.userPhoto.bounds)
}
return image!
}
The issue is the confusing syntax in the line:
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath))
That should simply be:
let imageFromPath = resizeImage(named: imagePath)
No cast required and correct in any Swift 3.x.

Swift Save images (screenshots) to nsuserdefaults

I have a program, where the user "creates" an image, and then the program takes a screenshot of the screen. I would then like to save this screenshot to a database, prefferebly nsuserdefaults, since I am accessing it later in a table view. Any other suggestions on how to approach this, are more than welcome :)
The code is like this
let screenshot = getScreenshot() // saves the screenshot
var imagePaths = [String]()
// get the array of previous screenshots
if let _ = NSUserDefaults.standardUserDefaults().objectForKey(theKey)
{
imagePaths = NSUserDefaults.standardDefaults().objectForKey(theKey) as! [String]
}
// then I want to get a path to the image something like
let imagePath = screenshot.getPath() // although this is not a valid method, this is basically what I want
// add the imagePath
imagePaths.append(imagePath)
// finally I save the image
NSUserDefaults.standardUserDefaults().setObject(imagePaths, forKey: theKey)
You can create directory in Documents and save there screenshots as usual files. Filename can be generated from date and time for uniqueness.
func saveImage(imageData: NSData)
{
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy hh.mm.ss"
let filename = "\(dateFormatter.stringFromDate(NSDate())).png"
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
let imagesDirectory = documentsDirectory.stringByAppendingPathComponent("Images")
let filePath = imagesDirectory.stringByAppendingPathComponent(filename)
if !NSFileManager.defaultManager().fileExistsAtPath(imagesDirectory)
{
var error: NSError?
NSFileManager.defaultManager().createDirectoryAtPath(imagesDirectory, withIntermediateDirectories: false, attributes: nil, error: &error)
if error != nil
{
println("\(error!.localizedDescription)")
return
}
}
imageData.writeToFile(filePath, atomically: true)
}
func getImagesPaths() -> [String]?
{
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
let imagesDirectory = documentsDirectory.stringByAppendingPathComponent("Images")
if let filenames = NSFileManager.defaultManager().contentsOfDirectoryAtPath(imagesDirectory, error: nil)
{
let imagePaths = filenames.map{"\(imagesDirectory)/\($0)"}.filter(){$0.pathExtension == "png"}
return imagePaths.count > 0 ? imagePaths : nil
}
return nil
}
To save image simply use saveImage(data). To get images paths use getImagesPaths().
If you need array of UIImage, you can get it by follow way:
var images : [UIImage] = [ ]
if let imagePaths = getImagesPaths()
{
for path in imagePaths
{
if let image = UIImage(contentsOfFile: path)
{
images.append(image)
}
}
}

CMFormatDescription to CMVideoFormatDescription

I'm trying to get the resolution of the camera of a device using swift.
I'm using CMVideoFormatDescriptionGetDimensions which requires a CMVideoFormatDescription, but AVCaptureDevice.formatDescription returns a CMFormatDescription. I've tried a multitude of ways to cast CMFormatDescription to CMVideoFormatDescription and can't seem to get it working.
Below is a sample of the code that I'm using:
for format in device.formats as [AVCaptureDeviceFormat] {
let videoDimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription)
}
This doesn't seem possible in Swift at the moment. One solution then would be to write a helper function in objective-c, such as:
CMVideoDimensions CMFormatDescriptionGetDimensions(CMFormatDescriptionRef formatDescription)
{
if (CMFormatDescriptionGetMediaType(formatDescription) == kCMMediaType_Video)
return CMVideoFormatDescriptionGetDimensions(formatDescription);
else
return (CMVideoDimensions) {
.width = 0,
.height = 0
};
}
Include the header with the function prototype in the Swift bridging header so that it will be accessible as a global function from your Swift code.
I was able to get the resolution using the swift method below:
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo) as AVCaptureDevice
let formatDesc = captureDevice.activeFormat.formatDescription
let dimensions = CMVideoFormatDescriptionGetDimensions(formatDesc)
Here's a solution in pure Swift, really only usable for logging purposes and such. Paste the following function in your class or somewhere else:
func widthAndHeightFromTrack(track: AVAssetTrack) -> CGSize {
let str = track.formatDescriptions.description
let regex = try! NSRegularExpression(pattern: "[0-9]{2,4} x [0-9]{2,4}", options: [])
if let result = regex.firstMatchInString(str, options: [], range: NSMakeRange(0, str.characters.count)) {
let dimensionString = (str as NSString).substringWithRange(result.range)
let dimensionArray = dimensionString.componentsSeparatedByString(" x ")
let width = Int(dimensionArray[0])
let height = Int(dimensionArray[1])
return CGSize(width: width!, height: height!)
}
return CGSizeZero
}
Example usage:
let allTracks: AVAsset = someAVAsset.tracksWithMediaType(AVMediaTypeVideo)
let videoTrack = allTracks[0]
let videoTrackDimensions = widthAndHeightFromTrack(videoTrack)
// You now have a CGSize, print it
print("Dimensions: \(videoTrackDimensions)")
Of course, the above solution will completely break whenever Apple changes something in the string representation of the CMFormatDescription. But it's useful for logging the dimensions.
Maybe, question is too old, but the Swift issue is still not fixed.
public extension AVURLAsset {
var audioFormatDescription: CMAudioFormatDescription? {
if let track = self.tracks(withMediaType: .audio).first,
let untypedDescription = track.formatDescriptions.first {
// hacks, warnings, disablings of swiftlint below are wrork-around of
// Swift bug: it fails converting 'Any as CMFormatDescription'
let forceTyped: CMFormatDescription?
//swiftlint:disable force_cast
= untypedDescription as! CMAudioFormatDescription
//swiftlint:enable force_cast
if let description = forceTyped {
return description
} else {
return nil
}
} else {
return nil
}
}
}