In my project Navigator I have this structure
-MyApp
--ViewController.swift
--AppDelegate.Swift
--Main.StoryBoard
--info.plist
--JSONFiles
---test.json
-MyAppUITests
As you can see, this is the very basic structure that happens when you create a new single view application.
I created a new group called JSONFiles and added in a JSON files called test.
When I try to get the file using:
if let path = Bundle.main.path(forResource: "JSONFiles/test", ofType: "json") {
do {
let data = try NSData(contentsOf: URL(fileURLWithPath: path), options: NSData.ReadingOptions.mappedIfSafe)
let jsonData : NSData = NSData(contentsOfFile: path)!
allEntries = (try! JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSArray
print(allEntries)
} catch let error as NSError {
print(error.localizedDescription)
}
} else {
print("Invalid filename/path.")
}
I get the error:
Invalid filename/path.
If I move the JSON file our of the group folder and change the forResource to just "test" it works fine and prints in the console.
Can anyway tell me how to make it read from the folder? I could have all my JSON files in the root but I am wanting to tidy it up slightly.
Thanks
You must include your json file into "Copy Bundle Ressources". Go to your project target -> Build phases -> Copy Bundle ressources and there, add your json file.
Then, you should be able to retrieve the path using the function: Bundle.main.path(forResource: "test", ofType: "json") .
Related
I'm using this code to save a string to an xml file in my project
let saveTo = statisticsI.toXMLString()
let filepath = Bundle.main.url(forResource: "statistics", withExtension: "xml")
let filepathAlt = Bundle.main.path(forResource: "statistics", ofType: "xml")
print(filepathAlt!)
do {
try saveTo.write(to: filepath!, atomically: false, encoding: String.Encoding.utf8)
let contents = try String(contentsOfFile: filepathAlt!)
print("FILE CONTENTS \(contents)")
}
catch let error as NSError {
print("Error writing values \(error)")
}
Printing the file contents returns the xml correctly, but when I stop running the application, the file hasn't been updated
When the above code is run, the file has already been read by a seperate function. Is the fact that the file has already been accessed (and its path still stored in a variable) the issue?
when I stop running the application, the file hasn't been updated
You can't write into the application bundle in iOS. Try using a path into the Documents folder or some other place within the app's sandbox.
I am trying to download a plist file from a remote location and use it in the iOS app I am creating. The file is going to be used for calendar details within the app's calendar. The goal is obviously that I can update the remote file instead of having to push updates to the app itself every time we need to make changes to calendar details.
I started with the code used in this example: Download File From A Remote URL
Here is my modified version:
// Create destination URL
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("2017.plist")
//let destinationFileUrl = URL(string: Bundle.main.path(forResource: String(currentYear), ofType: "plist")!)
//Create URL to the source file you want to download
let fileURL = URL(string: "https://drive.google.com/open?id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.removeItem(at: destinationFileUrl)
try FileManager.default.moveItem(at: tempLocalUrl, to: destinationFileUrl)
print("File was replaced")
print(NSArray(contentsOf: tempLocalUrl))
//print(tempLocalUrl)
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
}
task.resume()
I originally tried to overwrite the file that is bundled with the app to being with, that resulted in errors. So I instead tried to just save it in the app's documents folder and that removed that error. I had to make sure and remove any previous version of the file because it was giving me a file already exists error after the first run.
While it says everything is working (The outputs for both successful download and replaced file happen) when I print the contents of the array from the downloaded URL it just gives me nil.
This is my first attempt to use any kind of external resources in an app. Before I have always kept everything internal, so I am sure there is something glaringly obvious I am missing.
Update 1:
I realized I didn't have the correct URL to use to download a file from a Google drive. That line of code has been changed to:
let fileURL = URL(string: "https://drive.google.com/uc?export=download&id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
So now I actually am downloading the plist like I originally thought I was. Even removing the deletion issue mentioned in the first comment, I still can't get the downloaded file to actually replace the existing one.
Update 2:
I have reduced the actual file manipulation down to the following:
do {
try FileManager.default.replaceItemAt(destinationFileUrl, withItemAt: tempLocalUrl)
print("File was replaced")
print(NSArray(contentsOf: destinationFileUrl))
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
After the replacement is performed the output of the file shows the correct new contents that were downloaded from the internet.
Later in the code when I try and access the file it seems to be nil in content again.
Look at your download completion code. You:
Delete the file at the destination URL (in case there was one
leftover)
MOVE the temp file to the destination URL (removing it from the temp
URL)
Try to load the file from the temp URL.
What's wrong with this picture?
You are trying to get the contents of the moved file. You already moved the file to destination url and then you are trying to get the contents of the file from temporary location.
For getting file data, Please try the following :
let fileData = try! String(contentsOf: destinationFileUrl, encoding: String.Encoding.utf8)
print(fileData)
I'm facing difficulty to fetch the Json File.In my project there is one folder,in that folder multiple sub folders are there.And in every sub folder there is two json file.Json file name is common in every sub folder.Then how I fetch json file with subfolder name? Which means i want to differentiate json file through sub folder name.I'm new in Swift.Thank you.
(NS)Bundle provides a very convenient way to address subfolders, the optional parameter subdirectory of url(forResource: withExtension:)
if let jsonFileURL = Bundle.main.url(forResource: "jsonFile",
withExtension: "json",
subdirectory: "Subfolder1/SubFolder2") {
let json = try! String(contentsOf: jsonFileURL, encoding: .utf8)
print(json)
} else { fatalError("Check the file paths") }
You can get the file from different folders by providing the actual folder name in the document path.
Make sure that your files are added to resources.
if let docsPath = Bundle.main.resourcePath! + "/Resources/subfolder" {
let fileManager = FileManager.default
do {
let docsArray = try fileManager.contentsOfDirectory(atPath: docsPath)
//get the required json file from the documents array
} catch {
print(error)
}
}
I have a lot of JSON files that all of them are in a new folder called assets. How can I access some of the JSON files that are in a folder within the assets folder. Here is a screenshot of the file I want to work with. http://prntscr.com/eiv7p4
UPDATE:
here is the code with which I access the file "mc-summer-0.json"
if let path = Bundle.main.path(forResource: "mc-summer-0", ofType: "json") {
do {
let jsonData = try NSData(contentsOfFile: path, options: NSData.ReadingOptions.mappedIfSafe)
do {
let jsonResult: NSDictionary = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
if let times_to : [String] = jsonResult["times_to"] as? [String] {
for (value) in times_to {
print("\(value)")
}
}
} catch {}
} catch {}
}
but if I want to access a file that is in the "assets" folder I change the line into if let path = Bundle.main.path(forResource: "assets/mc-summer-1", ofType: "json") {
but unfortunately it doesnt work.
Those files are in groups in your project, not in separate folders. Very likely they will be in the root level of your app bundle, but we can't be sure based on what you've shown. I suggest building your app for the simulator and then opening the resulting bundle in the Finder and examining it.
alright, in case somebody get stuck just like me the real solution is to remove the assets "folder" and re add it and select to add them as reference instead as a group
I am writing UI tests for iOS application. I want to read data from a local json file.
I am using the following code to get the path for my json file:
func testExample() {
readJSON()
}
func readJSON(){
let bundle = Bundle(for:myTestClass.self)
if let path = bundle.url(forResource: "myurl", withExtension: "json"){
print("Got the path")
print(path)
}
else{
print("Invalid filename/path")
}
I have tried using the solution for the following stackoverflow question :
Reading in a JSON File Using Swift. Didn't work!
Also, I had a look at the following Apple documentation:
https://developer.apple.com/swift/blog/?id=37
Any help would be greatly appreciated!
First of all, you need to check the .json file is in the same target with the test file. If the file is in the main target, you have to use Bundle.main. However if it is in the same target with your test, use the below code.
let t = type(of: self)
let bundle = Bundle(for: t.self)
let path = bundle.path(forResource: "myurl", ofType: "json")
That's not how you read local json files from Bundle in iOS. It's done in the following way:
// "myUrl" is assumed to be your json file name
if let pathStr: String = Bundle.main.path(forResource: "myurl", ofType: ".json") {
print("json path: \(pathStr)")
}
I just found resources added to XCUITest targets will be placed under Bundle.main.resourcePath + "/PlugIns/<TARGET_NAME>.xctest/". However, I'm not sure if there were better ways to access them rather than hard-coding the sub-directory path.