How to run iOS unit tests with cocoapods + static library - ios

We are having many development pods that are currently linked as dynamic frameworks.
I'm trying to migrate the dynamic framework to static libraries
For static-libs, I'm setting resource bundle like below (pod_spec)
s.resource_bundles = { 'BundleName' => [ 'BundleName/**/*.{xcassets,lproj,storyboard,xib,xcassets,imageset,png,mp3,mp4,wma}' ] }
and accessing in code like below
Bundle.main.url(forResource: "BundleName", withExtension: "bundle")
when I run the app, I'm getting the resource bundle path, from that I can able to get images/localized text
The same is not working when I run Unit tests. resource bundle path is nil and all my test related to localized text are failing
how can I fix this?
thanks in advance

The issue is that your tests are a separate bundle from your application, so when running Bundle.main.url(...) it will try to give a resource from your test bundle, not your app bundle.
I guess the easiest way of doing things is to add the resources to your test bundle. Depending on what you want to achieve. Some more background information might be helpful.

Answering my own question after struggled for a whole day
When the unit test runs your code, your unit test bundle is NOT the main bundle.
Even though you are running tests, not your application, your application bundle is still the main bundle. so, when you run unit tests with static libraries mode, you won't find resource_bundles if you search the main bundle.
you have to check in the list of Bundle and find the one with ".xctest" extension and append the module name
enter code here
class func bundle(for name: String) -> Bundle {
var url = Bundle.main.bundleURL
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
url = bundle.bundleURL.deletingLastPathComponent()
}
url = url.appendingPathComponent( name + "/" + name + ".bundle")
guard let bundle = Bundle(url: url) else {
return Bundle.main
}
return bundle
}

Related

Bundle.main.url cannot find file

I tried several answers from different posts but none of them solved my problem.
Situation I am in:
The project is called XcodeTests and the only target it has is XcodeTests
I have a my_file.txt file at <ProjectRoot>/XcodeTests/res/
Upon creating my_file.txt, I made sure that target XcodeTests was selected
Both res folder and my_file.txt exists in storage directories confirmed by Finder
In Build Phase of XcodeTests, CopyBundleResources does include my_file.txt
The following code throws Fatal error: Couldn't find requested file:
import Foundation
guard let myFileURL = Bundle.main.url(forResource: "my_file", withExtension: "txt")
else
{
fatalError("Couldn't find requested file")
}
What should I do so that Bundle.main.url can find my_file.txt?
Turns out it's because the target itself is a command line program and does not support Copy Bundle Resources. Even tho resources can be listed there, none of them will be copied.

How can I run a script as part of a Swift Package Manager build?

I am converting existing iOS frameworks to modules using Swift Package Manager (SPM); in one case I have a script that was in the Build Phases of the original framework version, that I need to run as part of the new Swift Package Manager build process.
I have tried adding a Pre-action script to the Build portion of the scheme for the module in question (per Ben-xD's comment at https://forums.swift.org/t/how-to-run-a-build-phase-script-when-building-a-standalone-swift-package-in-xcode/40117/8, though his later comment indicates it doesn't work). I found that approach does work if I build the module directly. However, if the module is being built as a dependency of a client app or module (or even as a dependency of its unit tests) that script does not seem to get called.
I am currently relying on the Package.swift manifest file to manage all the module settings, and am using this within Xcode to build and test.
Is there a way to run scripts as part of an SPM module build, so that they will get run reliably, whether the module is built directly or indirectly as a dependency? (Possibly something that can be added to the Package.swift file?)
Update: I have done some additional searching, and came across this:
https://forums.swift.org/t/pitch-swiftpm-extensible-build-tools/44715/5
suggesting that it's not possible, but may be in the future. Until then, wondering if there is any workaround to do something as simple as take a file that is autogenerated as part of the SPM build, but rename it before adding it to the resource bundle for the package.
What you might want to try is to execute your script through command line.
this snippet could be useful for you:
import Foundation
extension Process {
public func shell(command: String) -> String {
launchPath = "/bin/bash"
arguments = ["-c", command]
let outputPipe = Pipe()
standardOutput = outputPipe
launch()
let data = outputPipe.fileHandleForReading.readDataToEndOfFile()
guard let outputData = String(data: data, encoding: String.Encoding.utf8) else { return "" }
return outputData.characters.reduce("") { (result, value) in
return result + String(value)
}
}
}
public func launch(command: String, arguments: [String]) -> String {
let process = Process()
let command = "\(command) \(arguments.joined(separator: " "))"
return process.shell(command: command)
}
By the definition of Package in Swift, there is currently no Script-related definition.

Xcode 10: Load the same .xml file in project target and unit test target

I try to load the same .xml file in project target and unit test target.
It seems to be a similar problem like this question: Xcode. Image resources added to a test target are not copied into the tests bundle
In project target, this code snipped worked fine. I just have to add the "xmlString.xml" file to "Copy Files" (see image 1).
func parseFile() {
let stringPath = Bundle.main.path(forResource: "xmlString", ofType: "xml")
let url = NSURL(fileURLWithPath: stringPath!)
...
}
If I run the code snipped in unit test target, I get an error because the .xml file can not be found. I tried to add the .xml file to the "Copy Bundle Resources" without any success (see image 2).
The only way to get it working is to use the absolute path
func parseFile() {
let stringPath: String? = "/Users/.../Documents/Git/.../.../.../.../xmlString.xml"
let url = NSURL(fileURLWithPath: stringPath!)
...
}
Is there a way to use the Bundle.main.path(forResource: "xmlString", ofType: "xml") function instead of the absolute path?
Thanks in advance!
You can get the bundle for your project like this:
let projectBundle = Bundle(for: AnyClassInMyProject.self)
where you replace AnyClassInMyProject with the name of an actual class in your project.
You can then get the path to your .xml file like this:
let stringPath = projectBundle.path(forResource: "xmlString", ofType: "xml")

Accessing a resource within a Bundle HL7-FHIR

I was just wondering if there is a way to access a resource within a bundle.
I.E
FhirContext ctx = FhirContext.forDstu3();
String baseSite= "http://fhirtest.uhn.ca/baseDstu3/";
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu3");
System.out.println("Connected to server");
Bundle bundle = client.search().forResource(DiagnosticReport.class).where(DiagnosticReport.IDENTIFIER.exactly().identifier(id)).returnBundle(Bundle.class).execute();
DiagnosticReport diag =client.read().resource(DiagnosticReport.class).withId(bundle.getEntry().get(0).getResource());
String finalBundle=ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(diag);
System.out.println(finalBundle);
Observation obv = client.read().resource(Observation.class).withUrl(baseSite+diag.getResult().get(0).getReference()).execute();
Sequence seq = client.read().resource(Sequence.class).withUrl(baseSite+obv.getRelated().get(0).getTarget()).execute();
diag is currently what is causing issues. Since I have a client access their report via ID that is generated (thus the bundle search command) but to access all the other resources that are referenced by diagnosticReport I can't find a way to either separate the resource from the bundle or directly grab from the bundle.
Thank you
If you just want to grab the DiagnosticReport resource from the bundle you should be able to do something like:
DiagnosticReport dr = (DiagnosticReport) bundle.getEntry().get(0).getResource();
If you wanted, you could also use includes to return the other linked resources in a single call to the server:
Bundle bundle = client.search().forResource(DiagnosticReport.class)
.where(new StringClientParam("_id").matches().value("117376"))
.include(new Include("DiagnosticReport:patient"))
.include(new Include("DiagnosticReport:result"))
.returnBundle(Bundle.class)
.execute();

Xcode writing to file in a unit test

I understand that when a unit test executes, it is in the sandbox environment of Xcode.
In a unit test, I need to write and read data to a file.
Since the project resides in a git repository, this must be done completely from within code (i.e. no special manual test setup of directories is possible)
For all my searching and trying today, I cannot find a way to write to a file from within the unit test.
Does anybody know of a method to write to a file under these conditions?
Edit: This is the problematic code:
let dirPath = NSTemporaryDirectory()!
var filePath = dirPath.stringByAppendingPathComponent("unittest.json")
if !NSFileManager.defaultManager().isWritableFileAtPath(filePath) {
return (nil, "Directory missing or write access not granted for \(path)")
}
You should be able to use the temporary directory: NSTemporaryDirectory() and have it reliably point to a safe place to read and write. I'd suggest making sure you clean up any created files.
let dirPath = NSTemporaryDirectory()
To see whether a file can be created (per the updated question), check the directory for write permissions:
let canCreate = NSFileManager.defaultManager().isWritableFileAtPath(dirPath)

Resources