Read local JSON file in XCUITest - ios

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.

Related

Saving to xml document Swift

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.

How to correctly reference/retrieve a temp file created in AppData for file upload to a server?

So the app I'm making creates a file called "logfile" and I'm trying to send that file via Alamofire upload to a server. The file path printed in the console log is
/var/mobile/Containers/Data/Application/3BE13D78-3BF0-4880-A79A-27B488ED9EFE/Documents/logfile.txt
and the file path I can use to manually access the log created in the .xcappdata is
/AppData/Documents/logfile.txt
To access it, I'm using
let fileURL = Bundle.main.url(forResource: "", withExtension: "txt")
where inbetween the double quotes for "forResource", I've tried both file paths I listed in the previous paragraph as well as just the file name but I'm getting a nil value for file found for either. The file isn't recognized to be there, presumably because the file path I'm using is wrong as Alamofire is returning nil when trying to locate send the file. Anyone know the direct file path I'm supposed to use to be able to grab my file since the other two don't supposedly work? Thank you!
Use below code to get string data from text file to upload to server:
let fileName = "logfile"
let documentDirURL = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileURL = documentDirURL.appendingPathComponent(fileName).appendingPathExtension("txt")
print("FilePath: \(fileURL.path)")
var readString = "" // Used to store the file contents
do {
// Read the file contents
readString = try String(contentsOf: fileURL)
} catch let error as NSError {
print("Failed reading from URL: \(fileURL), Error: " + error.localizedDescription)
}
print("File Text: \(readString)") // Send 'readString' to server
If you're dynamically creating the file at runtime, it won't be in your app bundle so the Bundle class won't be able to find it. The directories you see are also dynamically-generated and not only platform-specific, but also device-specific, so you can't use the file paths directly. Instead, you'll have to ask for the proper directory at runtime from the FileManager class, like this:
guard let documents = FileManager.default.urls(for: .documentsDirectory, in: .userDomainMask).first else{
// This case will likely never happen, but forcing anything in Swift is bad
return
}
let logURL = URL(string: "logfile.txt", relativeTo: documents)
do{
let fileContents = String(contentsOf: logURL)
// Send your file to your sever here
catch{
// Handle any errors you might've encountered
}
Note that I'm guessing based on the paths you pasted in your answer you put it in your application's documents directory. That's a perfectly fine place to put this type of thing, but if I'm wrong and you put it in a different place, you'll have to modify this code to point to the right place

Swift 3.0 FileManager.fileExists(atPath:) always return false

When I use method .fileExists(atPath:)to judge whether the file is exist in file system, the method always return false to me. I checked the file system and the file do exist. Here is my code:
let filePath = url?.path
var isDir : ObjCBool = false
if(self.fileManager.fileExists(atPath: filePath!, isDirectory: &isDir)){
let result = NSData(contentsOfFile: filePath!)
}
or
let filePath = url?.path
if(self.fileManager.fileExists(atPath: filePath!)){
let result = NSData(contentsOfFile: filePath!)
}
the if clause will always be skipped.
I assume your url is an URL type. If so try this out:
let filePath = url?.path // always try to work with URL when accessing Files
if(FileManager.default.fileExists(atPath: filePath!)){ // just use String when you have to check for existence of your file
let result = NSData(contentsOf: url!) // use URL instead of String
}
Saying enough, you should change your implementation like this:
if(FileManager.default.fileExists(atPath: (url?.path)!)){ // just use String when you have to check for existence of your file
let result = NSData(contentsOf: url!) // use URL instead of String
}
EDIT: 1
There is even more better way, you can call it swift-way (:D). You don't have to explicitly check for file existence.
guard let result = NSData(contentsOf: fileURL) else {
// No data in your fileURL. So no data is received. Do your task if you got no data
// Keep in mind that you don't have access to your result here.
// You can return from here.
return
}
// You got your data successfully that was in your fileURL location. Do your task with your result.
// You can have access to your result variable here. You can do further with result constant.
print(result)
Update for Swift 3.0+ without the Objective-Cish NS prefix:
do {
let result = try Data(contentsOf: fileURL)
print(result)
} catch {
print(error)
}
in swift 3
just in case anyone gets confused like i did, here's the full snippets:
let str = "file:///Users/martian2049/Library/Developer/CoreSimulator/Devices/67D744AA-6EEC-4AFD-A840-366F4D78A18C/data/Containers/Data/Application/DD96F423-AF9F-4F4D-B370-94ADE7D6D0A5/Documents/72b8b0fb-7f71-7f31-ac9b-f9cc95dfe90d.mp3"
let url = URL(string: str)
print(url!.path,"\n")
if FileManager.default.fileExists(atPath: url!.path) {
print("FILE Yes AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
this prints
/Users/martian2049/Library/Developer/CoreSimulator/Devices/67D744AA-6EEC-4AFD-A840-366F4D78A18C/data/Containers/Data/Application/DD96F423-AF9F-4F4D-B370-94ADE7D6D0A5/Documents/72b8b0fb-7f71-7f31-ac9b-f9cc95dfe90d.mp3
FILE Yes AVAILABLE
notice how the 'file://' got chopped off?
I want to share my experience, in case anyone else gets baffled by this.
Tested on iOS 10-11, Xcode 9.2 and Swift 3.2.
Short answer: if you save a file path to disk, you may solve by not including the Documents directory in it.
Instead, every time you need to retrieve the file with the saved path, get the Documents directory and append the path.
For an iOS app, I was saving an image to .../Documents/Pictures through the relative URL, let's say url.
As the image was saved, a path, let's say url.path, was saved too in a Core Data entity.
When I later tried retrieving the image through FileManager.default.fileExists(atPath: url.path), it always returned false.
I was testing the app on my iPhone. It turned out that, for some reason, every time I ran the app from Xcode, the app identifier folder changed!!
So:
App opened from Xcode -> Image saved -> app closed -> app opened from physical device ->
fileExists -> TRUE
App opened from Xcode -> Image saved -> app closed -> app opened from Xcode -> fileExists -> FALSE
You can check if this is your case by getting and printing the Document folder path (or URL, it doesn't matter) and comparing it with the saved path (or URL). If you get something like this:
/var/mobile/Containers/Data/Application/5D4632AE-C432-4D37-A3F7-ECD05716AD8A/Documents..
/var/mobile/Containers/Data/Application/D09904C3-D80D-48EB-ACFB-1E42D878AFA4/Documents..
you found the issue.
Just use path instead of absoluteString to remove file://
FileManager.default.fileExists(atPath: URL.init(string: "your_url")!.path)
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true);
var path = paths[0] as String;
path = path + "/YourFilePath"
if((NSFileManager.defaultManager().fileExistsAtPath(path))) {
let result = NSData(contentsOfFile: filePath!)}
Try the above code and check again
I had the same problem this worked for me
filePath.replacingOccurrences(of: "file://", with: "")
First, what does your file path looks like? If the path begins with a ~,then it must be expanded with expandingTildeInPath;
Check if the path is inaccessible to your app. iOS App can only visits its sandbox directories.

SWIFT 3 access JSON that is within a folder

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

Invalid filename/path when getting JSON

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") .

Resources