iOS: How do you find the creation date of a file? - ios

I'm trying to find the creation date (NOT modification date) of a file.
Creation date doesn't appear to be in the attributes of a file, though modified date is.
I'm using this code..
NSFileManager* fm = [NSFileManager defaultManager];
NSString* path = [PathHelpers pathInDocumentsFolderWithFilename:FILE_NAME];
NSDictionary* attrs = [fm attributesOfItemAtPath:path error:nil];
if (attrs != nil) {
return (NSDate*)[attrs objectForKey: NSFileCreationDate];
} else {
return nil;
}
This always returns nil. Typing 'po attrs' into the debugger to get the list of key/value pairs in the NSDictionary returns the following..
NSFileGroupOwnerAccountID = 20;
NSFileGroupOwnerAccountName = staff;
NSFileModificationDate = 2010-01-21 11:47:55 +0000;
NSFileOwnerAccountID = 501;
NSFileOwnerAccountName = ben;
NSFilePosixPermissions = 420;
NSFileReferenceCount = 1;
NSFileSize = 338;
NSFileSystemFileNumber = 2234;
NSFileSystemNumber = 42553324;
NSFileType = NSFileTypeRegular;
No creation date.. bah..
Anyone know another way of getting the creation date or does it just not exist in iOS?

This code actually returns the good creation date to me:
NSFileManager* fm = [NSFileManager defaultManager];
NSDictionary* attrs = [fm attributesOfItemAtPath:path error:nil];
if (attrs != nil) {
NSDate *date = (NSDate*)[attrs objectForKey: NSFileCreationDate];
NSLog(#"Date Created: %#", [date description]);
}
else {
NSLog(#"Not found");
}
Are you creating the file inside the App? Maybe that's where the problem is.

There is a special message fileCreationDate for that in NSDictionary. The following works for me:
Objective-C:
NSDate *date = [attrs fileCreationDate];
Swift:
let attrs = try NSFileManager.defaultManager().attributesOfItemAtPath(path) as NSDictionary
attrs.fileCreationDate()

Updated answer for Swift 4 to pull out the modified (.modifiedDate) or creation (.creationDate) date:
let file: URL = ...
if let attributes = try? FileManager.default.attributesOfItem(atPath: file.path) as [FileAttributeKey: Any],
let creationDate = attributes[FileAttributeKey.creationDate] as? Date {
print(creationDate)
}
Using a file that you provide in advance via a URL, it will request its attributes. If successful a dictionary of [FileAttributeKey: Any] is returned
Using the dictionary from step 1, it then pulls out the creation date (or modified if you prefer) and using the conditional unwrap, assigns it to a date if successful
Assuming the first two steps are successful, you now have a date that you can work with

Swift 2.0 version:
do {
let fileAttributes = try NSFileManager.defaultManager().attributesOfItemAtPath(YOURPATH)
let creationDate = fileAttributes[NSFileCreationDate] as? NSDate
let modificationDate = fileAttributes[NSFileModificationDate] as? NSDate
print("creation date of file is", creationDate)
print("modification date of file is", modificationDate)
}catch let error as NSError {
print("file not found:", error)
}

In Swift 5:
let date = (try? FileManager.default.attributesOfItem(atPath: path))?[.creationDate] as? Date

In Swift 4, if you want to know the file created date for a specific file in DocumentsDirectory, you can use this method
func getfileCreatedDate(theFile: String) -> Date {
var theCreationDate = Date()
do{
let aFileAttributes = try FileManager.default.attributesOfItem(atPath: theFile) as [FileAttributeKey:Any]
theCreationDate = aFileAttributes[FileAttributeKey.creationDate] as! Date
} catch let theError as Error{
print("file not found \(theError)")
}
return theCreationDate
}

NSDate *creationDate = nil;
if ([fileManager fileExistsAtPath:filePath]) {
NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
creationDate = attributes[NSFileCreationDate];
}
Its here

Swift 5
let url: URL = ....
let attributes = try? FileManager.default.attributesOfItem(atPath: url.path)
if let date = attributes?[.modificationDate] {
print("File Modification date is %#", date)
}
if let date = attributes?[.creationDate] {
print("File Creation date is %#", date)
}

Swift 3 version code:
do {
let fileAttributes = try FileManager.default.attributesOfItem(atPath: yourPathString)
let modificationDate = fileAttributes[FileAttributeKey.modificationDate] as! Date
print("Modification date: ", modificationDate)
} catch let error {
print("Error getting file modification attribute date: \(error.localizedDescription)")
}

Can you use fstat64 to get the st_birthtimespec member of the returned struct? You'll need to create a C file handle for the file and convert the timespec value into an NSDate, but that's better than nothing.

Related

How to get Values from a NSString

I have an NSString. It is a URL I am getting when using Universal Links. I want to get id value from it. Is there any direct methods in SDK or do we need to use componententsSeparated String values?
Below is the NSString/URL:
https://some.com/cc/test.html?id=3039#value=test
I want to get two things: "test" from test.html and "id" value.
Use NSURLComponents created from an NSURL or NSString of your URL.
From there you can use path to get the /cc/test.html part. Then use lastPathComponent to get test.html and finally use stringByDeletingPathExtension to get test.
To get the "id" value start with the components' queryItems value. Iterate that array finding the NSURLQueryItem with the name of "id" and then get its value.
You could create NSURLComponents from URL string get parameters by calling queryItems method. It will return array of NSURLQueryItem
NSURLComponents *components = [NSURLComponents componentsWithString:#"https://some.com/cc/test.html?id=3039&value=test"];
NSArray *array = [components queryItems];
for(NSURLQueryItem *item in array){
NSLog(#"Name: %#, Value: %#", item.name, item.value);
}
NSLog(#"Items: %#", array);
We can make extension
extension URL {
func getParamValue(paramaterName: String) -> String? {
guard let url = URLComponents(string:self.absoluteString ) else { return nil }
return url.queryItems?.first(where: { $0.name == paramaterName})?.value
}
}
Now you can call like below
let someURL = URL(string: "https://some.com/cc/test.html?id=3039&value=test")!
someURL.getParamValue("id") // 3039
someURL.getParamValue("value") // test

Get filename from url without timestamp and with out file name extension - Objective C

From below different urls i need file name only
URL 1 = uploads/4/jobs/2017-02-15-05-17-05pmThe_Wolf_of_Wall_Street_-_Jordan_Belfort.pdf
URL 2 = uploads/4/jobs/2017-02-15-05-17-05ammaster.jpg
What I need is
Result1 = The_Wolf_of_Wall_Street_-_Jordan_Belfort
Result2 = master
Try with this:
let name = "uploads/4/jobs/2017-02-15-05-17-05pmThe_Wolf_of_Wall_Street_-_Jordan_Belfort.pdf"
var names = name.components(separatedBy: "/")
let lastComponent = names[names.count - 1]
var nameOfFile:String = ""
let pm = lastComponent.components(separatedBy: "pm")
let am = lastComponent.components(separatedBy: "am")
if(pm.count > 1){
nameOfFile = pm[1]
} else if(am.count > 1){
nameOfFile = am[1]
}
print(nameOfFile)
If your url will always follow start naming format.
let urlStr = "uploads/4/jobs/2017-02-15-05-17-05pmThe_Wolf_of_Wall_Street_-_Jordan_Belfort.pdf"
let indexStart = urlStr.index(urlStr.startIndex, offsetBy: +36)
let str = urlStr.substring(from: indexStart )
let indexEnd = str.index(str.endIndex, offsetBy: -4)
let name = str.substring(to: indexEnd )
You can try the below code for Objective-C
NSString* string = #"uploads/4/jobs/2017-02-15-05-17-05pmThe_Wolf_of_Wall_Street_-_Jordan_Belfort.pdf";
NSArray* array = [string componentsSeparatedByString:#"/"];
NSString* lastElement = [array lastObject];
NSArray* finalArrayForPM = [lastElement componentsSeparatedByString:#"pm"];
NSArray* finalArrayForAM = [lastElement componentsSeparatedByString:#"am"];
if (finalArrayForPM.count > 1) {
NSString* fileName = [[finalArrayForPM lastObject] stringByDeletingPathExtension];
NSLog(#"The fileName is %#",fileName);
} else if (finalArrayForAM.count > 1) {
NSString* fileName = [[finalArrayForAM lastObject] stringByDeletingPathExtension];
NSLog(#"The fileName is %#",fileName);
}
Hope this helps.

How to export .JSON file using NSMutableDictionary?

I have NSMutableDictionary inside this values are present like this
{
0 = {
imageangle = "0.09630692";
imageheight = "129.0245";
imagepath = "assets-library://asset/asset.PNG?id=BD849AC4-EDF2-4E05-B5F8-F5EE34385A97&ext=PNG";
imagescalex = "85.48777";
imagescaley = "85.48777";
imagewidth = "129.0245";
mainheight = 282;
mainwidth = 316;
};
memes = {
alpha = 1;
alphaFont = 1;
blue = 1;
blueFont = 0;
green = 1;
greenFont = "0.5";
memestext = "Kishore kumar kumar ";
red = 0;
redFont = 1;
textheight = 34;
textscalex = 13;
textscaley = "30.5";
textwidth = 316;
};
}
Using this i like to export this dictionary in .json file.
After that i am converting into JSONSTRING:
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dickeyjson options:NSJSONWritingPrettyPrinted error:&error];
NSString* aStr;
aStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
My Question:
Is it possible to export it as .json and store it to local storage?
You certainly can export a string to a .json file, and this would be an outline of a correctly error handling way to do so:
do {
let jsonString = "{\"what\":\"ever\"}"
let jsonFile = "file.json"
guard let docs = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first else {
throw NSError(domain: "what-no-Documents-folder??", code: 1, userInfo: nil)
}
let path = NSURL(fileURLWithPath: docs).URLByAppendingPathComponent(jsonFile)
try text.writeToURL(path, atomically: false, encoding: NSUTF8StringEncoding)
} catch {
fatalError("Writing failed!")
}
and to read it back in,
do {
let path = NSURL(fileURLWithPath: "same as for writing")
let jsonString = try NSString(contentsOfURL: path, encoding: NSUTF8StringEncoding)
}
catch {
fatalError("Reading failed!")
}
However, it is probable that you want to put this JSON back into a dictionary when reading, yes? In that case, there's no need to convert to JSON, you should just use NSDictionary's writeToURL method directly, using the same outline as above; as I don't believe there's any JSON-representable object not conforming to NSCoder and therefore archivable with writeToFile/writeToURL.
What exactly is the problem? You convert the dictionary to NSData using NSJSONSerialization. Then you write the NSData to a file using a variant of writeToFile: or writeToURL: There is no reason to go through NSString, it's just a waste of time, memory, and battery life.
To read the data, use dataWithContentsOfFile or dataWithContentsOfURL, and again NSJSONSerialization. If you use a mapped file, the NSData doesn't even use memory; that would be useful if your data is multiple megabytes.

Alexa: productId, code challenge: what should they be?

I am following closely this document to authorize a hardware from my iOS app:
In iOS section, at step 5):
- (IBAction)onLogInButtonClicked:(id)sender {
NSArray *requestScopes = [NSArray arrayWithObjects:#"alexa:all", nil];
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
NSString* scopeData = [NSString stringWithFormat:#"{\"alexa:all\":{\"productID\":\"%#\","
"\"productInstanceAttributes\":{\"deviceSerialNumber\":\"%#\"}}}",
productId, deviceSerialNumber];
options[kAIOptionScopeData] = scopeData;
options[kAIOptionReturnAuthCode] = #YES;
options[kAIOptionCodeChallenge] = #"CODE_CHALLENGE_GOES_HERE";
options[kAIOptionCodeChallengeMethod] = #"S256";
[AIMobileLib authorizeUserForScopes:requestScopes delegate:delegate options:options];
}
what should productId in scopeData be? As I have read some other posts, it is said that productId is taken from ID column of an app created in AVS Developer Portal, which is not linked with the document as it mentions to App Console in Getting Started guide for iOS. So I am confused at how/where to take the productId.
deviceSerialNumber can be any unique string?
I implemented a code-challenge method in ObjC based on item 1) and 2) described in "Transferring an Authorization Code from a Mobile App to an Alexa-enabled Product" section. Is it correct? (since I have no reference example)
- (NSString *)codeChallenge {
verifier = [NSString randomStringWithLength:128]; // generate 128-char string containing [A-Z], [a-z], [0-9], "-", "_", ".", "~"
NSData *sha256 = [[verifier dataUsingEncoding:NSUTF8StringEncoding] SHA256]; // SHA256 that string
NSString *base64Enc = [sha256 base64EncodedStringWithOptions:0]; // base64 encode SHA256 result
NSLog(#"base64Enc: %#", base64Enc);
NSMutableString *ret = [NSMutableString string];
for (NSInteger i=0; i<base64Enc.length; i++) { // remove "="; replace "+" with "-"; replace "/" with "_" as referenced from: http://tools.ietf.org/html/draft-ietf-oauth-spop-10#appendix-A
unichar c = [base64Enc characterAtIndex:i];
if (c == '=') {
continue;
}
else if (c == '+') {
[ret appendString:#"-"];
}
else if (c == '/') {
[ret appendString:#"_"];
}
else {
[ret appendFormat:#"%C", c];
}
}
return ret;
}
Regards,
So, it turned out that the App ID is the productID. You should able able find yours on Developer Console under Application Type Info tab. Your code challenge seems fine to me, but I'm not sure why you'd want to strip off =+-_. And yeah, deviceSerailNumber could be anything unique, I'm assuming that it should also be unique per installation.
The following in the Swift example for the same,
#IBAction func loginWithAmazon() {
let scopes = ["alexa:all"];
let codeChallenge = sha256("CODE_CHALLENGE_GOES_HERE".dataUsingEncoding(NSUTF8StringEncoding)!).base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
var options: [String: AnyObject] = [:]
let scopeData = String(format: "{\"alexa:all\":{\"productID\":\"%#\",\"productInstanceAttributes\":{\"deviceSerialNumber\":\"%#\"}}}", "yourAppIDHere", "anyUniqueID")
options[kAIOptionScopeData] = scopeData;
options[kAIOptionReturnAuthCode] = true;
options[kAIOptionCodeChallenge] = codeChallenge;
options[kAIOptionCodeChallengeMethod] = "S256";
AIMobileLib.authorizeUserForScopes(scopes, delegate: self, options: options);
}
func requestDidSucceed(apiResult: APIResult!) {
accessToken = apiResult.result
print(apiResult.result)
}
func requestDidFail(errorResponse: APIError!) {
print(errorResponse.error)
}
=================================================
func sha256(data : NSData) -> NSData {
var hash = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA256(data.bytes, CC_LONG(data.length), &hash)
let res = NSData(bytes: hash, length: Int(CC_SHA256_DIGEST_LENGTH))
return res
}

JSON Processing in iOS: Removing newlines

I am trying to decode json being sent to my application from Twitter's Streaming API.
Twitter will send my client data which has the following structure:
The body of a streaming API response consists of a series of newline-delimited messages, where "newline" is considered to be \r\n (in hex, 0x0D 0x0A) and "message" is a JSON encoded data structure or a blank line.
They basically separate tweets with \r\n. Sometimes they send you one tweet, sometimes they send you many at once.
Decoding the 'many' case is problematic for me. I use NSJSONSerialization class built into iOS. The problem is that it reads the first tweet and then see's the \r\n and considers this as garbage and returns an empty array and accompanying error.
To get around this problem, I use the following code to basically remove the newlines.
// json is the received json data
NSString* responseString = [[NSString alloc] initWithData:json encoding:NSASCIIStringEncoding];
NSMutableArray* responseMutableArray = [[responseString componentsSeparatedByString:#"\r\n"] mutableCopy];
[responseMutableArray removeLastObject];
NSArray* responseArray = [NSArray arrayWithArray:responseMutableArray];
[responseMutableArray removeAllObjects];
NSError* error;
for(NSString* tweetString in responseArray)
{
NSDictionary* dict = [NSJSONSerialization JSONObjectWithData:[tweetString dataUsingEncoding:NSUTF8StringEncoding] options: NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
[responseMutableArray addObject:dict];
}
//responseMutableArray contains the parsed tweets
The approach is, convert the data into a NSString, NSString into an array (by spliting by \r\n), array back into data and then parse the JSON into a dictionary. Its a messy process and I wonder if there is something more efficient way that I can do this.
Thanks very much
Vb
Disclaimer: I actually got here by encountering this issue myself.
I think the simplest solution would be a revision of #Elmundo 's answer that removes the newlines completely. It worked for me.
Note: this is Swift version 3.something
guard var string = String(data: data, encoding: .utf8) else {
return
}
string = string.stringByReplacingOccurrencesOfString("\r\n", withString: "")
guard let data = string.data(using: .utf8) else {
return false
}
do {
let json = try NSJSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:Any]
}catch let e as NSError {
print(e.description)
}
Try this:
NSString *string = [[NSString alloc] initWithData:json encoding:NSASCIIStringEncoding]:
string = [NSString stringWithFormat:#"[%#]", [string stringByReplacingOccurrencesOfString:#"\\r\\n" withString:#","]];
NSArray *array= [NSJSONSerialization JSONObjectWithData:[string dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
Touch this:
let stringData = String(data: data, encoding: NSUTF8StringEncoding)
if let strData = stringData {
let newString = strData.stringByReplacingOccurrencesOfString("\r\n", withString: "\\r\\n")
if let newString = newString {
do {
let dict:[AnyObject] = try NSJSONSerialization.JSONObjectWithData(newString.dataUsingEncoding(NSUTF8StringEncoding)!, options: [.AllowFragments, .MutableContainers]) as! [AnyObject]
}catch let e as NSError {
print(e.description)
}
}
}

Resources