I am writing a xml parser library in Swift for a specific kind of data. In my project there are two ways to import xml data, either by using a web URL or by giving a local xml file. The first one works great but I have trouble with the second one. Here is a sample of my code:
import Foundation
class PnmlParser: NSObject, XMLParserDelegate {
// DOES NOT WORK
func loadPN(filePath: String) {
print(Bundle.main)
let xmlResponseData = Bundle.main.getFileData(filePath)
let parser = XMLParser(data: xmlResponseData)
parser.delegate = self
parser.parse()
}
// WORK
func loadPN(url: URL) {
let parser = XMLParser(contentsOf: url)!
parser.delegate = self
parser.parse()
}
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
// Some code
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
// Some code
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
// Some code
}
func parserDidEndDocument(_ parser: XMLParser) {
// Some code
}
}
extension Bundle {
func getFileData(_ file: String) -> Data {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) in bundle")
}
return data
}
}
There are two functions loadPN
, but it is only the loadPN(filePath: String)
that does not work, because it does not find my xml file.
I use the Swift Package Manager and not XCode to insert resources. I have already seen how to do it with XCode using the build phase
and copy bundle resources
, but it does not apply to my case.
I am working on Swift 5.6, and I have seen that I should adapt my Package.swift
to insert the wanted resources.
Here is my Package.swift
:
// swift-tools-version: 5.6
import PackageDescription
let package = Package(
name: "swift-xml-import-test",
...
targets: [
.target(
name: "swift-xml-import-test",
dependencies: [],
resources: [.process("Resources/data.xml")]
),
.testTarget(
name: "swift-xml-import-testTests",
dependencies: ["swift-xml-import-test"],
resources: [.process("ResourcesTests/dataTests.xml")]
),
]
)
A sample GitHub project shows the data structure and the problem that I encountered:
Sample of my GitHub project
I created two folders, respectively Resources
(Sources/swift-xml-import-test/Resources
) and ResourcesTests
(Tests/swift-xml-import-testTests/ResourcesTests
), where there is an xml data file in each.
I created a test to try it out:
final class swift_xml_import_testTests: XCTestCase {
func testWork() {
let p = PnmlParser()
if let url = URL(string: "https://www.pnml.org/version-2009/examples/philo.pnml") {
p.loadPN(url: url)
}
}
func testDoesNotWork() {
let p = PnmlParser()
p.loadPN(filePath: "dataTests.xml")
}
}
When I launch my test testDoesNotWork()
, I get the following error: Thread 1: Fatal error: Failed to locate dataTests.xml in bundle
from my earlier extension.
I do not understand what I am missing if someone can help me.
If you want to test yourself, you have the GitHub link of the sample project above.
CodePudding user response:
From Swift 5.3, it is possible to add resources using Package.swift
, which has been done in the code.
To call the added resources, it is required to use:
Bundle.module.url(forResource: myFile, withExtension: myExtension)
However, this is not possible in the extension made previously, where it was self.url
.
Thus, I deleted the extension and changed the function loadPN(filePath: String)
into:
func loadPN(filePath: String) {
if let url = Bundle.module.url(forResource: filePath, withExtension: nil) {
let parser = XMLParser(contentsOf: url)!
parser.delegate = self
parser.parse()
}
}
testDoesNotWork
test finally passed.