Swift Vapor SWXMLHash probably SWXMLHash error - swift

I am building a application in Vapor. My website works fine on localhost but on the Heroku doesn't run correctly. I have got a list with elements from xml. Xml is parsing by SWXMLHash. Heroku printing only static header. I cant see any informations about error in server log. Everything need to works fine but not working.
guard let xmlString = response?.body.bytes?.string else {
throw Abort.custom(status: .badRequest, message: "Could not retrieve xml string")
}
let xml = SWXMLHash.parse(xmlString)
var cars:[Car] = []
for item in xml["findItemsByCategoryResponse"]["searchResult"]["item"].all {
cars.append(Car(item:item))
print("1 "+(item["title"].element?.text ?? ""))
}
var table:[Node]=[]
for car in cars {
table.append(try ["title": car.title,"url": car.auctionUrl,"price":car.price,"imgUrl":car.galeryUrl].makeNode())
}
var nodeTables = try table.makeNode()
return try drop.view.make("index", Node(node: ["cars": nodeTables]))

I was unable to get SWXMLHash working properly on Heroku so I opted to use XML from Zewo instead. I admit that Zewo's XML lib is not as nice to use, nor are the docs as good, as compared to SWXMLHash, but it did not take too much work to make the switch for me and works just fine on Heroku.

Related

How to access the raw content from a response in Vapor 3 unit test?

I'm coming from using tooling such as SuperTest with NodeJS and looking for relevant equivalents to support testing with Vapor 3 and server side swift.
I see a pattern of using making a testable application with Vapor 3 to do testing of endpoints, examples being https://github.com/raywenderlich/vapor-til/blob/master/Tests/AppTests/Application%2BTestable.swift and the write-up at https://medium.com/swift2go/vapor-3-series-iii-testing-b192be079c9e.
When using these in tests, the format generally looks something like:
func testGettingASingleUserFromTheAPI() throws {
let user = try User.create(name: usersName, username: usersUsername, on: conn)
let receivedUser = try app.getResponse(to: "\(usersURI)\(user.id!)", decodeTo: User.Public.self)
XCTAssertEqual(receivedUser.name, usersName)
XCTAssertEqual(receivedUser.username, usersUsername)
XCTAssertEqual(receivedUser.id, user.id)
}
(from Vapor-TIL example code)
In all of these examples, the return values are really set to be handed back to something decodable (the decodeTo: kind of setup). In some cases in my Vapor 3 code, I want to just validate some non-JSON encoded results - just simple strings, and validate the results - but I've not found the methods to get into the content or convenient ways to validate it with XCTAssert.
response.content is available, a container around the overall response (of type ContentContainer). Are there some examples or good ways at getting to the underlying content representation to validate them directly?
You could write your own additional methods in Application+Testable like
func getRawResponse(to path: String) throws -> Response {
return try self.sendRequest(to: path, method: .GET)
}
func getStringResponse(to path: String) throws -> String {
let response = try self.getRawResponse(to: path)
guard let data = response.http.body.data,
let string = String(data: data, encoding: .utf8) else {
throw SomeError("Unable to decode response data into String")
}
return string
}
and then call them to get either raw Response or decoded String like
func testGettingHelloWorldStringFromTheAPI() throws {
let string = try app. getStringResponse(to: "some/endpoint")
XCTAssertEqual(string, "Hello world")
}

Swift CSVImporter framework with remote URLs

Initial use and testing of the framework. The examples provided and most of the searches from across the internet are using "local" or downloaded CSV files to the device, with (path:).
I would like to pass various remote URLs but there are not many examples, using (url: URL).
So far, I am simply in viewDidLoad() following the same code as provided with the sample playground file, and trying to output to the console.
I have tried to run this in a simulator for the iPhone 8 device. Running Xcode 10.1.
From the documentation, there is an ".onFail" handler, which gets invoked on the sourceURL's I have provided, but I do not know what error objects exist to do any further troubleshooting.
let sourceURL = URL(string: "https://files.datapress.com/leeds/dataset/leeds-city-council-dataset-register/Dataset%20register.csv")
guard let sourceURL2 = URL(string: "https://minio.l3.ckan.io/ckan/ni/resources/2477b63a-b1c4-45cc-a5ee-8e33e5b20b5b/supplies-and-services-contracts---2014.2015-yr.csv?AWSAccessKeyId=aspjTDZu90BQVi&Expires=1546982840&Signature=dLDVWMu%2Fp4RiePIRhntCX6WFMpw%3D") else {
fatalError("URL string error")
}
let importer = CSVImporter<[String]>(url: sourceURL)
importer?.startImportingRecords { $0 }.onFail {
print("fail")
}.onFinish({ importedRecords in
print(importedRecords.count)
})

Parsing large XML files fails--ERROR:Error Domain=DDXMLErrorDomain Code=1 "(null)"

I'm parsing XML using KissXML. I can successfully parse small XML but have problem with large XML. Here's my code
let url = URL(fileURLWithPath: xmlPath!)
let xmlData = try! Data(contentsOf: url)
do {
let doc = try DDXMLDocument(data: xmlData, options:0)// This is not working if xml is large (6MB)
let project = try! doc.nodes(forXPath: "//Project") as! [DDXMLElement]
for user in project {
let ProjectName = user.attribute(forName: "ProjectName")!.stringValue
let userTime = user.attribute(forName: "UseTime")!.stringValue
print("ProjectName:\(ProjectName!),userTime:\(userTime!)")
}
}
catch {
print("\(error)") //Get some idea from this error
}
When parsing 12k XML was successful, but 6M XML was a failure. When parsing large XML(6M),doc equal to nil.
I try to use NSXMLParser,the same problem arises,small file can work, big files can't.ERROR:NSXMLParserErrorDomain error 4.
You should not ignore the error using try?, always enclose it in do - catch construct. Use below code and see what error are you getting and then try to resolve it. Don't shoot in the dark, get some idea from the error and if nothing works post your error message in the question.
do {
let doc = try DDXMLDocument(data: xmlData, options:0)
// Your next line of code
}
catch {
print("\(error)") //Get some idea from this error
}

Connecting to VPN with Swift 3

I'm trying to connect to a VPN in an iOS app. What I already know is the VPN type (L2TP over IPSec), account name, password and shared secret. The connection works through Mac's Network settings. Although, it seems a little more complicated, when you have to use this info in code.
First, I have imported the necessary library.
import NetworkExtension
Then, I'm trying to load preferences and in case of error, I'm using my own and save them. Looks like this:
NEVPNManager.shared().loadFromPreferences { error in
// config
NEVPNManager.shared().saveToPreferences { error in
if (error == nil) {
do {
try NEVPNManager.shared().connection.startVPNTunnel()
} catch {
print("Couldn't connect")
}
} else {
print("NEVPNManager.saveToPreferencesWithCompletionHandler failed: \(error!.localizedDescription)")
}
}
}
Where you can see the "// config", my data should be passed. I'm not 100% sure, if I'm doing it right.
There's a constant let p = NEVPNProtocolIPSec() where I'm placing my data. It's like p.username = "smth".
The question is: which fields of p should be filled? Where do I put the shared secret?
--- UPDATE ---
I'm always getting the error:
NEVPNManager.saveToPreferencesWithCompletionHandler failed: The operation couldn’t be completed. (NEVPNErrorDomain error 4.)
I can't find anything specific about that.
During the config, fields .sharedSecretReference and .passwordReference require the Data? object. I'm getting it by using keychain.get("passref")?.data(using: .utf8, allowLossyConversion: true) preceded by
let keychain: KeychainSwift! = KeychainSwift()
keychain.set("<my_password>", forKey: "passref")
(class KeychainSwift comes from here)
Where am I making a mistake?

How to create plugins in swift

I found article describing how to create plugin using Swift and Cocoa. It uses NSBundle to load plugin, but that, as far as I know, is not available in pure swift (no Cocoa). Is there way how to achieve same result without using Cocoa?
More info:
In case it's relevant, here is what I want to achieve. I create app in swift that runs on linux server. User can connect to it using their browser. I want to be able to have other people write "plugins" that will implement functionality itself (what user can see and do once they connect), from printing out hello world, through chat programs to games without having to worry about low level stuff provided by my app. Some sort of dll, that my server application loads and runs.
Solution to this is not trivial, but it's not impossible to do either. I prefer to use swift package manager to manage dependencies and Xcode as IDE. This combination is not perfect as it needs a lot of tinkering but there is not any other useable free swift IDE as of now.
You will need to set up two projects, let's call them Plugin (3rd party library) and PluginConsumer (app that uses other people plugins). You will also need to decide on API, for now we will use simple
TestPluginFunc()
Create Plugin.swift file with TestPluginFunc implementation in your Plugin project:
public func TestPluginFunc() {
print("Hooray!")
}
Set the project to build framework, not executable and build[1]. You will get Plugin.framework file which contains your plugin.
Now switch to your PluginConsumer project
Copy Plugin.framework from your Plugin project somewhere where you can easily find it. To actually load the framework and use it:
// we need to define how our plugin function looks like
typealias TestPluginFunc = #convention(c) ()->()
// and what is its name
let pluginFuncName = "TestPluginFunc"
func loadPlugin() {
let pluginName = "Plugin"
let openRes = dlopen("./\(pluginName).framework/\(pluginName)", RTLD_NOW|RTLD_LOCAL)
if openRes != nil {
// this is fragile
let symbolName = "_TF\(pluginName.utf8.count)\(pluginName)\(initFuncName.utf8.count)\(initFuncName)FT_T_"
let sym = dlsym(openRes, symbolName)
if sym != nil {
// here we load func from framework based on the name we constructed in "symbolName" variable
let f: TestPluginFunc = unsafeBitCast(sym, to: TestPluginFunc.self)
// and now all we need to do is execute our plugin function
f()
} else {
print("Error loading \(realPath). Symbol \(symbolName) not found.")
dlclose(openRes)
}
} else {
print("error opening lib")
}
}
If done correctly, you should see "Hooray!" being printed to your log.
There is a lot of room for improvement, first thing you should do is replace Plugin.framework string with parameter, preferably using some file library (I am using PerfectLib). Another thing to look at is defining plugin API in your PluginConsumer project as a protocol or base class, creating framework out of that, importing that framework in your plugin project and basing your implementation on that protocol/base class. I am trying to figure out exactly how to do that. I will update this post if I mange to do it properly.
[1]: I usually do this by creating Package.swift file and creating xcode project out of it using swift package generate-xcodeproj. If your project doesn't contain main.swift, xcode will create framework instead of executable
What you will want to do is create a folder your program will look in. Let's say it's called 'plugins'. It should make a list of names from the files in there, and then iterate through using them, passing parameters to the files and getting the output and making use of that in some way.
Activating a program and getting output:
func runCommand(cmd : String, args : String...) -> (output: [String], error: [String], exitCode: Int32) {
var output : [String] = []
var error : [String] = []
let task = Process()
task.launchPath = cmd
task.arguments = args
let outpipe = Pipe()
task.standardOutput = outpipe
let errpipe = Pipe()
task.standardError = errpipe
task.launch()
let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String(data: outdata, encoding: .utf8) {
string = string.trimmingCharacters(in: .newlines)
output = string.components(separatedBy: "\n")
}
let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
if var string = String(data: errdata, encoding: .utf8) {
string = string.trimmingCharacters(in: .newlines)
error = string.components(separatedBy: "\n")
}
task.waitUntilExit()
let status = task.terminationStatus
return (output, error, status)
}
`
Here is how a swift plugin would accept arguments:
for i in 1..C_ARGC {
let index = Int(i);
let arg = String.fromCString(C_ARGV[index])
switch arg {
case 1:
println("1");
case 2:
println("2")
default:
println("3)
}
}
So once you have the program and plugin communicating you just have to add handling in your program based on the output so the plugins output can do something meaningful. Without cocoa libraries this seems the way to go, though if you use C there are a couple of other options available there as well. Hope this helps.