Can someone explain what URLResourceKeys are? - swift

I was looking at the documentation for contentsOfDirectory(at:includingPropertiesForKeys:options:)
Particularly I've been focusing on the argument includingPropertiesForKeys, which said:
An array of keys that identify the file properties that you want pre-fetched for each item in the directory. For each returned URL, the specified properties are fetched and cached in the NSURL object. For a list of keys you can specify, see Common File System Resource Keys.
Clicking on URLResourceKey led me to the Apple Documentation about it.
And I was wondering, if I passed in keys like fileResourceTypeKey, fileResourceIdentifierKey, and creationDateKey how could I access those in the returned URL list (after calling contentsOfDirectory(at:includingPropertiesForKeys:options:))?
And I was also confused by the URLResourceKey enum b/c a lot of types have similar descriptions and names to other keys like:
documentIdentifierKey vs fileResourceIdentifierKeyvs localizedNameKey vs nameKey
localizedTypeDescriptionKey vs fileResourceTypeKey vs typeIdentifierKey
the urls returned by the contentsOfDirectory(at:includingPropertiesForKeys:options:) vs pathKey
Like what would be the differences between these keys?
Basically I have a really low understanding of the file system at this point so please bear with my "simple" questions. If someone could explain what all these keys means and how I can access/use them that would be great!

First of all the URLResourceKey documentation describes the kind of the attribute information very well. For example nameKey returns always Desktop for an URL representing ~/Desktop while localizedNameKey returns the localized name Schreibtisch on a German system or Bureau on a French system. However documentIdentifierKey and fileResourceIdentifierKey are completely different attributes.
Regarding the contentsOfDirectory(at:includingPropertiesForKeys:options:) API: The keys passed in the includingPropertiesForKeys parameter tells the framework to pre-fetch the corresponding attributes while getting the contents for performance reasons. For example
let contentURLs = try fileManager.contentsOfDirectory(at: anURL, includingPropertiesForKeys: [.nameKey, .fileSizeKey], options: .skipsHiddenFiles)
To read the attributes call resourceValues(forKeys on the URL passing the same keys as in contentsOfDirectory. Then get the value with the corresponding property of the resource key. The benefit of the combination URLResourceKey / URLResourceValues is you get always the proper type from the file attributes. This avoids any type casting.
for fileURL in contentURLs {
do {
let fileAttributes = try fileURL.resourceValues(forKeys:[.nameKey, .fileSizeKey])
print(fileAttributes.name!) // is String
print(fileAttributes.fileSize!) // is Int
} catch { print(error, fileURL) }
}

Related

NSDocument XML read Issue

I am working on a NSDocument based Mac app. Which imports .xml file. It's working fine for some xml files but for few having issues.
Issue is read() is modifying the data when we import file, i need to keep the original data as it is.
what do i need to do to make sure i get original xml data in the read()?
I am using below function to read the file
override func read(from data: Data, ofType typeName: String) throws {
var error:NSError? = nil
var xmlDocument1:XMLDocument? = XMLDocument()
do{
xmlDocument1 = try XMLDocument(data: data, options: XMLNode.Options(rawValue: XMLNode.Options.RawValue(Int(XMLNode.Options.nodePreserveWhitespace.rawValue))))
}catch let err as NSError{
error = err
}
if error != nil {
throw error!
}
}
and i parse xmlDocument1 to read and get all the xml information.
Issue: Doing this way swift is modifying the document, as mentioned below.
Example 1:
Original:
<iws:attr-option name="1 - Poor" />
<iws:attr-option name="2 - Needs Improvement" />
Data getting from Read(), notice the closing tags added automatically
<iws:attr-option name="1 - Poor"></iws:attr-option>
<iws:attr-option name="2 - Needs Improvement"></iws:attr-option>
Example 2:
Original:
<source>
<ph id="12" x="</span>">{12}</ph>
</source>
Data getting from Read(), notice the ">" symbol is replaced with "& gt;"
<source>
<ph id="12" x="</span>">{12}</ph>
</source>
Example 3:
I am not able to paste the code here as the special character is not even displaying here, so adding image.
left is the original and right side one is what i am getting in read(), special character is missing.
Code Sameple : (I am not sure if we can post code directly here)
https://drive.google.com/drive/folders/1WWGE7fFJPKvs5KU5f_PlwWtoqCVxTcS0?usp=sharing
Above drive we have sample xml file and code.
"DevelopingADocumentBasedApp" is the code, just open the "DocumentBasedApp.xcodeproj", run it.
3 .Once it runs, click on Menu->File->Open and open the provided xml file.
In content.swift, Keep a break point at "print(xmlDocument!)"
Here we can see the document is modified by NSDocument, and it is different from the original
Edit:
#matt Thank you for making me understand real problem, Initially i thought that i have issue with NSDocument's read(). But issues is XMLDocument() not returning exact data. I need to find a solution for that.
Reading is not changing your document.
You make an xml document, with XMLDocument(data:...). You are asking for a new valid XML document based on your original, and that is exactly what you get. The resulting structure is not a big string, like your original data; it is an elaborate node tree reflecting the structure of your XML. That node tree is identical to the structure described by your original. That fact does not affect in any way your ability to parse the document; indeed, it is why you are able to parse the document. If you think it does cause an inability to parse the document, your parsing code is wrong (but you didn't show that, so no more can be said).
Also note that your evidence for what is "in" the XML document is indirect; the XML document is a node tree, but the strings you display are the output of a secondary rendering into a string. That rendering representation is arbitrary and malleable; it obeys its own rules of formatting. (And again, you didn't show anything about how you obtain that rendering. Perhaps we are talking about your print statement?)
The point is, you seem to have to some sort of expectation about how passing into an XMLDocument and then back out of it will "round trip" your original string in such a way that the output looks just like the original. That expectation is incorrect. That's not what XMLDocument does.
And merely reading the original data into an XMLDocument did not change the data, I can promise you that.
So don't worry, be happy; as far as the validity of your XML is concerned, everything is fine, and the data you started with has not been altered in any way.
Here's a demonstration:
let xmlstring = """
<testing>
<fun whatever="thingy" />
</testing>
"""
print(xmlstring)
let xmldata = xmlstring.data(using: .utf8)!
let xml = try? XMLDocument(data: xmldata, options: [])
print("=======")
print(xml!)
The output is:
<testing>
<fun whatever="thingy" />
</testing>
=======
<?xml version="1.0"?><testing><fun whatever="thingy"></fun></testing>
As you can see, the output from the print is not the same as the input string. But it is a valid XML representation of the original string, and that's all that matters. And the original xmlstring and xmldata that I started with are, I assure you, completely untouched.

FileManager: Attribute for when a file was last opened

I need a way of checking when a file was last opened. I tried by creating a custom FileAttributeKey and setting that to the current Date, but when I go to open the file again the attribute does not exist:
private let key = FileAttributeKey(rawValue: "lastOpenedAt")
do {
try FileManager.default.setAttributes(
[key: Date()],
ofItemAtPath: videoNameDirectoryPath
)
} catch {
Log.error(error.localizedDescription)
}
So now I am resorting to using the modification date key to say when I last opened the file, it is not ideal so I am wondering if there is a better way to do this
setAttributes doesn't support custom attributes, you can only use the documented ones.
To set your own attributes, you may use xattr as described in this question:
Write extend file attributes swift example
If you're lucky, you may use kMDItemLastUsedDate from Spotlight aka MDItem as described in the documentation archive of File Metadata Attributes.

Get the user's home directory from XPC

My (non-sandboxed) app has an embedded XPC helper which runs as root.
I would like to reference the (real) user's home directory from inside my helper, but these usual suspects simply return /var/root:
FileManager.default.homeDirectoryForCurrentUser
NSHomeDirectory()
I can't simply pass Users/bob to my helper for security reasons — if an exploit managed to call my helper method with any URL it wished, and my helper did things based on that as root, I fear bad things could be achieved.
As vadian commented there are fundamental conceptual issues with what you're asking. What you probably actually want to do is be sure the process communicating with your helper tool is in fact trusted.
To do that you need to use SecCodeCreateWithXPCMessage and then use the resulting SecCode instance to validate the caller. For an example of how to do that, take a look at the acceptMessage function in the SecureXPC framework.
EDIT: Turns out there is a way to do this that does work from a Command Line Tool such as one installed with SMJobBless. This answer is adapted from Apple's Technical Q&A QA1133.
If you for whatever reason want to ignore the above, there's an approach you can take which may produce unexpected results if multiple users have active consoles. From Apple's documentation for SCDynamicStoreCopyConsoleUser: "Note that this function only provides information about the primary console. It does not provide any details about console sessions that have fast user switched out or about other consoles."
import SystemConfiguration
extension FileManager {
var homeDirectoryForConsoleUser: URL? {
var homeDirectory: URL?
if let consoleUser = SCDynamicStoreCopyConsoleUser(nil, nil, nil) as String?,
consoleUser != "loginwindow" {
homeDirectory = URL(fileURLWithPath: "/Users/\(consoleUser)")
}
return homeDirectory
}
}
And then you can make use of this anywhere in your helper tool:
if let homeDirectory = FileManager.default.homeDirectoryForConsoleUser {
// Do something useful here
}

How to get original path when alias fails to resolve

How can the original target path be programatically retrieved when the alias fails to resolve?
do {
let resolutionOptions: URL.BookmarkResolutionOptions = [
.withoutUI, .withoutMounting
]
let _ = try URL(resolvingAliasFileAt: fileURL, options: resolutionOptions)
}
catch _ {
// since non-resolvable, then retrieve & print original target string
}
The existing StackOverflow question "Getting alias path of file in swift" does not cover original target path retrieval for the situation of a non-resolvable alias.
The information would seem to be available somehow because the Finder GUI Get Info will still show the Original: /Some/Path even if the original is not found or available.
Also, mdls metadata listing did not provide the original target path.
I think you can load the bookmark data using URL.bookmarkData(withContentsOf:), then use resourceValues(forKeys:fromBookmarkData:) with [.pathKey] as the keys. Then, query the path of the returned URLResourcesKey object.

CQ5.5: getting Style of a target page

I've been working on this for sometime now, and I keep running into a wall. I think I'm close, but I figured someone out here in the land of SO might have some deeper insight if not a better way of doing what I'm trying to do.
Basically lets look at this scenario. I have a logo w/ some text that can be set from a few different places. If we look at the setup here is what it looks like.
Hiearchy:
Homepage [has designPath]
- Child Microsite Page [has designPath]
- Logo Component
Logic Flow (in logo component):
if properties.get("logoText") {
use this
} else if currentStyle.get("logoTextFromStyle") {
use this
} else if parentStyle.get("logoTextFromGlobal") {
use this
} else {
be blank
}
My query is with how to get the "parentStyle" of this page. Looking at the docs here: http://dev.day.com/docs/en/cq/5-5/javadoc/com/day/cq/wcm/api/designer/Style.html
I've been able to come up with the fact that I can get a Style object from the "designer" object made available via defineObjects. This is defined with the other utility objects like "pageManager, resourceUtil, resource, currentPage, etc".
With that being said this doesn't seem to work.
//assuming we have getting homePage earlier and it is a valid cq Page resource
Resource homePageResource.slingRequest.getResourceResolver().getResource(homePage.getPath());
Style homePageStyle = designer.getStyle(homePageResource);
at this point homePageStyle is null. To do some more testing I i tried passing currentPage.getPath() instead of homePage.getPath(). I assumed this would give me the currentPage resource and would in end yield the currentStyle object. This also resulted in a null Style object. From this I think I can safely conclude I'm passing the incorrect resource type.
I attempted to load the the cq:designPath into the resource hoping to get a Designer resourceType but to no avail.
I am curious if anyone has run into this problem before. I apologize if I've gone into too much detail, but I wanted to lay out the "why" to my question as well, just in case there was a better way overall of accomplishing this.
I've figured out how to return the style. Here is the rundown of what I did.
//get your page object
Page targetPage = pageManager.getPage("/path/to/target");
//get the Design object of the target page
Design homePageDesign = designer.getDesign(homePage);
//extract the style from the design using the design path
Style homePageStyle = homePageDesign.getStyle(homePageDesign.getPath());
it's very interesting the definition of "getStyle" is a little different from the designer.getStyle vs a Design.getStyle. designer.getStyle asks for a resource whereas Design.getStyle will take the path to a Design "cell" and return the appropriate Style.
I did some testing and it looks like it does work with inherited Styles/Designs. So if my cq:designPath is set at level 1 and I look up a page on at level 2 they will return the Design/Style at the cq:designPath set at level 1.
I hope this helps someone else down the way.
I tried this approach but was not getting the Styles in the Style object.
When we do this:
Design homePageDesign = designer.getDesign(homePage);
In this Design object we get the path till the project node i.e etc/design/myproject
After this if we try to extract the Style from the design path we do not get it.
However I implemented it in a different way.
In the design object, we also get the complete JSON of designs for(etc/design/myproject).
Get the sling:resourceType of the target page and get the value after last index of "/".
Check if this JSON contains the last value. If it contains, you can get your styles, i.e. image, etc.