NSData: read contents of file [duplicate] - swift2

This question already has answers here:
Swift Playground - Files are not readable
(3 answers)
Closed 6 years ago.
Here's the code:
let jsonData = NSData(contentsOfFile: "/Users/7stud/xcode_projects/iOS/my_json.json")
That code is in a playground, and I get nil for jsonData. I didn't type the path: I dragged the file from Finder to a Terminal window, which enters the full path of the file at the point of the cursor. Then I copied that absolute path and pasted it into my code--so no typo.
According to the docs:
NSData
+ dataWithContentsOfFile:
Creates and returns a data object by reading every byte from the file
specified by a given path.
Declaration OBJECTIVE-C
+ (instancetype)dataWithContentsOfFile:(NSString *)path
Parameters
path | The absolute path of the file from which to read data.
Return Value
A data object by reading every byte from the file specified by path. Returns nil if the data object could not be
created.
There's no Swift definition.

Faced the same issue and as a quick workaround I placed my file directly at the location returned by the NSFileManager and later accessed the file to read the contents.
Here is the snipped:
var fileManager = NSFileManager.defaultManager()
var path:String! = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first
var data = fileManager.contentsAtPath(path+"/a.txt")
try NSString(data: data!, encoding: NSUTF8StringEncoding)
I placed my file a.txt at the path returned by NSSearchPathForDirectoriesInDomains method and was able to retrieve it in a string.

Related

replaceFormatDescription:withFormatDescription:

I am having trouble with customized CMFormatDescription in AVMutableMovieTrack.
It seems to work as intended though, the modification seems to be volatile and I am unable to write out modified formatDescription into movie header. I guess this is a bug of movieHeaderWithFileType:error:.
Is there any way to make movie header with modified format description?
In detail:
From macOS 10.13 AVMutableMovieTrack supports format description replacement in AVMutableMovieTrack.
- (void)replaceFormatDescription:(CMFormatDescriptionRef)formatDescription
withFormatDescription:(CMFormatDescriptionRef)newFormatDescription;
When I do AVMovie's movieHeaderWithFileType:error: or writeMovieHeaderToURL:fileType:options:error:, resulted movie header contains original unchaged video media format description. So it is unable to save.
- (NSData *)movieHeaderWithFileType:(AVFileType)fileType
error:(NSError * _Nullable *)outError;
- (BOOL)writeMovieHeaderToURL:(NSURL *)URL
fileType:(AVFileType)fileType
options:(AVMovieWritingOptions)options
error:(NSError * _Nullable *)outError;
Sample source:
https://github.com/MyCometG3/cutter2/blob/master/cutter2/MovieMutator.swift
var newFormat : CMVideoFormatDescription? = nil
let codecType = CMFormatDescriptionGetMediaSubType(format) as CMVideoCodecType
let dimensions = CMVideoFormatDescriptionGetDimensions(format)
let result = CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
codecType,
dimensions.width,
dimensions.height,
dict,
&newFormat)
if result == noErr, let newFormat = newFormat {
track.replaceFormatDescription(format, with: newFormat)
count += 1
} else {
https://github.com/MyCometG3/cutter2/blob/master/cutter2/MovieMutatorBase.swift
let movie : AVMovie = internalMovie.mutableCopy() as! AVMutableMovie
let data = try? movie.makeMovieHeader(fileType: AVFileType.mov)
return data
I have find out the reason why the modification is lost.
According to CMFormatDescription.h, I have to remove two type of extensions when copying extensions from original format description.
CM_EXPORT const CFStringRef kCMFormatDescriptionExtension_VerbatimSampleDescription /
__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0);
#discussion This extension is used to ensure that roundtrips from sample descriptions
to CMFormatDescriptions back to sample descriptions preserve the exact original
sample descriptions.
IMPORTANT: If you make a modified clone of a CMFormatDescription, you must
delete this extension from the clone, or your modifications could be lost.
CM_EXPORT const CFStringRef kCMFormatDescriptionExtension_VerbatimISOSampleEntry /
__OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0);
#discussion This extension is used to ensure that roundtrips from ISO Sample Entry (ie. AudioSampleEntry or VisualSampleEntry)
to CMFormatDescriptions back to ISO Sample Entry preserve the exact original
sample descriptions.
IMPORTANT: If you make a modified clone of a CMFormatDescription, you must
delete this extension from the clone, or your modifications could be lost.
So the code snippet will be like:
let formats = track.formatDescriptions as! [CMFormatDescription]
for format in formats {
guard let cfDict = CMFormatDescriptionGetExtensions(format) else { continue }
let dict : NSMutableDictionary = NSMutableDictionary(dictionary: cfDict)
dict[kCMFormatDescriptionExtension_VerbatimSampleDescription] = nil
dict[kCMFormatDescriptionExtension_VerbatimISOSampleEntry] = nil
:

File access permission in Swift on OSX

I'm using:
let dialog = NSOpenPanel()
to get a file URL.
I'm then reading in the contents of the text file with:
let content = try String( contentsOf: dialog.url)
This works!
I'm then trying to read in another text file in the same directory with a different extension:
let b = dialog.url?.deletingPathExtension()
// Add the new file extension
let c = b?.appendingPathExtension("TSN")
let content2 = try String( contentsOf: c)
With this I get:
"The file “FLO5.TSN” couldn’t be opened because you don’t have permission to view it."
If I try and open the .tsn file with a URL from a NSOpenPanel() dialog result it works. I need to open several data files from this same directory with different extensions. Is there a way to do this?
set off sandbox!!)
Xcode 9 and later enables sandboxing by default, which severely limits interactions with the system.
Select your target, then Capabilities and set Downloads Folder to Read/Write:
In my case, solve it's startAccessingSecurityScopedResource
Example:
let selectedFile = try result.get() // get path to file
do {
// get access to open file
if selectedFile.startAccessingSecurityScopedResource() {
let path = selectedFile.path
let data = NSData(contentsOfFile: path)
print(data) // array bytes
selectedFile.stopAccessingSecurityScopedResource()
}
} catch {
// Couldn't read the file.
print(error.localizedDescription)
}
Apple wiki about this feature https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso

Folder of images: create and fill SKShapeNodes, 1 of each

Is it possible to create a folder and have SpriteKit go through that folder, find each image (regardless of name), and create a SKShapeNode for each in that folder (regardless of how many there are)?
Assume all images are the same size, and all .png files ready to be used as unique images as textures for filling SKShapeNodes, and that all SKShapeNodes will be the same size.
I want to know, once done, how many shapes have been created, and have them uniquely named based on the name of the image of the texture used.
But all I'm reading seems to be about knowing how many images are there, and what their names are, before hand.
Clueless as to where to start searching about how to do this.
Update: no idea how to reference the "folder" with images:
I've dumped the images, with their very imaginative names [one, two, three, four, five etc. ], into a folder as "atlas", like this:
but as per Allessandro's wonderful answer, I have no idea how to address this "folder" and get at what's in it.
I think it's this line I'm getting completely wrong:
let folderPath = Bundle.main.path(forResource: "Images", ofType: nil)
I don't know what to replace "Images" with.
Update 2:
Found a much easier way to do this:
let myTextureAtlas = SKTextureAtlas(named: "demoArt")
print(myTextureAtlas)
print(myTextureAtlas.textureNames)
Too many requests , so I try to help you with some code:
Create a folder:
func createDir(folderName:String) {
let fileManager = FileManager.default
if let directory = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "APP_GROUP_IDENTIFIER") {
let newDirectory = directory.appendingPathComponent(folderName)
try! fileManager.createDirectory(at: newDirectory, withIntermediateDirectories: false, attributes: nil)
}
}
Now you have your folder so you can decide to save a file or to copy one file from documents to this new directory. I make an example for the last one.
Copy a file:
let fileManager = FileManager.default
do {
try fileManager.copyItem(atPath: "hero.png", toPath: "subfolder/hero.swift")
}
catch let error as NSError {
print("Something went wrong: \(error)")
}
Now you want to know how to extract all files from a directory.
Extract all files from a directory:
func extractAllFile(atPath path: String, withExtension fileExtension:String) -> [String] {
let pathURL = NSURL(fileURLWithPath: path, isDirectory: true)
var allFiles: [String] = []
let fileManager = FileManager.default
if let enumerator = fileManager.enumerator(atPath: path) {
for file in enumerator {
if #available(iOS 9.0, *) {
if let path = NSURL(fileURLWithPath: file as! String, relativeTo: pathURL as URL).path
, path.hasSuffix(".\(fileExtension)"){
allFiles.append(path)
}
} else {
// Fallback on earlier versions
}
}
}
return allFiles
}
Usage:
let folderPath = Bundle.main.path(forResource: "Images", ofType: nil)
let allPngFiles = extractAllFile(atPath: folderPath!, withExtension: "png") // returns file path of all the png files inside the folder
After that, you can create an array of SKShapeNode based from allPngFiles using fillTexture with a SKTexture created from each image and give to each node the name of the file used (so you know at the end also how many nodes you have created from array.count).
Slightly different answer here. I'm not sure why one needs to create a directory and copy files over. In your screenshot you have a demoArt.atlas with 7 files (one.png, two.png, etc). You indicate you want to generate an SKShapeNode. It still isn't clear to me why you want SKShapeNode, but ignoring that, you just need to know the folder name. You'll notice how the folder is blue, which means it is a reference. This means that in the image, the folder is retained.
This would get you all the files in that directory:
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let texPath = NSURL(fileURLWithPath: documentsPath).appendingPathComponent("demoArt.atlas")
let filemanager:FileManager = FileManager()
// Error check/ensure path exists
let files = filemanager.enumerator(atPath:texPath!.path)
while let file = files?.nextObject() {
print(file)
// Create your SKShapeNode here, you can load file from this
// This is all relative path, so "file" would be one.png, two.png, etc. If you need the full path, you
// need to prepend texPath
}
Note the key diff here is not copying files over. Maybe I just don't understand the problem and why it would require the copy.
If you wanted to put that in an array you can. But here you'll have the file name of the texture which you would of course need to load.
Realistically, you want to load those textures asynchronously first, then construct your SKShapeNode.

Programmatically save an image to xcassets in Swift [duplicate]

This question already has an answer here:
Save image in asset catalog on runtime
(1 answer)
Closed 7 years ago.
Say my app downloads an image, is it possible to save this to Images.xcassets programmatically, so that the image doesn't have to be downloaded again?
Or would the best option be to keep retrieving it from the server?
You can't save it to the images.assets. However you can save it to the document directory buy doing this. (Code from here - How to convert code objective c to Swift to save image?)
let nsDocumentDirectory = NSSearchPathDirectory.DocumentDirectory
let nsUserDomainMask = NSSearchPathDomainMask.UserDomainMask
if let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true) {
if paths.count > 0 {
if let dirPath = paths[0] as? String {
let readPath = dirPath.stringByAppendingPathComponent("Image.png")
let image = UIImage(named: readPath)
let writePath = dirPath.stringByAppendingPathComponent("Image2.png")
UIImagePNGRepresentation(image).writeToFile(writePath, atomically: true)
}
}
}
You can't save to Images.xcassets, however you can save it to a file and access it with imageWithContentsOfFile:.
You could also use a caching library, like JGAFImageCache or Haneke.
you can't write to the bundle at all you need to save the data to you documents/caches folder inside your sandbox

Full path of project

I want to open a text file in Swift and I managed to do it passing the full path as parameter:
let dados = String.stringWithContentsOfFile("/Users/aczuquim/Google Drive/Swift/Verdadeiro ou Falso/Verdadeiro ou Falso/Dados.txt", encoding: NSUTF8StringEncoding, error: nil)
Since, I added the file to the project, is there any way to use relative path?
When I use the relative path, a nil is returned.
UPDATE
In the playground, the line bellow works fine:
let dados = String.stringWithContentsOfFile("/Users/aczuquim/Google Drive/Swift/Verdadeiro ou Falso/Verdadeiro ou Falso/dados.txt", encoding: NSUTF8StringEncoding, error: nil)
But not the following ones (path is nil):
let bundle = NSBundle.mainBundle()
let path = bundle.pathForResource("dados", ofType: "txt")
Bundle resource:
Swift 3.0
if let bundle = Bundle.main().urlForResource("Info", withExtension: "plist") {
print("Path: \(bundle)")
}
Swift 2.0
let bundleURL = NSBundle.mainBundle()!.URLForResource("dados", withExtension: "txt")
println("\(bundleURL)")
If a nil is returned then the resource is not found and an error reported (note the exclamation mark after mainBundle). Check Build Phases - Copy Bundle Resources if the resource is being included.
App and Documents Folder
Get the documents folder from an array of [AnyObject]! which casts the first object to the NSURL type:
let docFolderURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as NSURL
println("DocumentFolderURL: \(docFolderURL)")
Then get the App folder, by stepping up to the parent folder and delete the last path component:
let appFolderURL = docFolderURL.URLByDeletingLastPathComponent
println("AppFolderURL: \(appFolderURL)")
Other directories may be accessed by using .URLByAppendingPathComponent(pathComponent: String?, isDirectory:Bool) etc.
Temporary Directory
var tempURL = NSURL.fileURLWithPath(NSTemporaryDirectory(), isDirectory: true).URLByDeletingLastPathComponent
Note that Apple now prefers the use of URL´s to access folders and files and new methods use them exclusively. To use the path in older methods just call: tempURL.path

Resources