We want to make HTTP(S) requests (GET) to call an API. The Problem is, NSURLRequest is (at the moment) not implemented in the Foundation for Linux (https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/NSURLRequest.swift).
Are there any other possibilities to create an HTTP request?
There is a nice project called HTTPMiddleware that can be useful, it's just a middleware framework but it works well.
Here's a code example:
import HTTP
import HTTPMiddleware
struct Middleware: HTTPRequestMiddlewareType {
func respond(request: HTTPRequest) -> HTTPRequestMiddlewareResult {
// You can change the request and pass it forward
return .Next(request)
// Or you can respond early and bypass the chain
return .Respond(HTTPResponse(statusCode: 404, reasonPhrase: "Not Found"))
}
}
struct Responder: HTTPResponderType {
func respond(request: HTTPRequest) -> HTTPResponse {
// May or may not be called depending on the middleware
return HTTPResponse(statusCode: 200, reasonPhrase: "OK")
}
}
let request = HTTPRequest(method: .GET, uri: URI(path: "/"))
let chain = Middleware() >>> Responder()
let response = chain.respond(request)
Here is the official page, you can also find a JSON parser in the sources that can be useful for common requests.
The installation requires only the uri_parser.
Consider following solution based on ccurl library:
https://dl.dropboxusercontent.com/u/2504173/CurlSampleApp.tar.gz
I did it on Ubuntu 14.04
Swift 3.0 - latest development version:
https://swift.org/builds/development/ubuntu1404/swift-DEVELOPMENT-SNAPSHOT-2016-06-06-a/swift-DEVELOPMENT-SNAPSHOT-2016-06-06-a-ubuntu14.04.tar.gz
Following package and instructions worked fine in general:
https://swiftpkgs.ng.bluemix.net/package/SwiftOnTheServer/CCurl
But in order to fix compilation errors I had to modify sots_curl.h in CCurl package. Following line was added:
#include <stdint.h>
For the app building you need to perform following in the app folder:
swift build
For app running you need to perform following command in the app folder:
.build/debug/app
Hope it helps. Ask questions if any :)
Considering that Swift on Linux is fast-evolving work in progress, the best approach, I think, is to hold off doing any serious development until the situation is more stable. Even if you overcome the obstacle of making HTTP requests, there will likely be other unpleasant surprises due to the technology being immature on Linux.
However, if this is just a matter of curiosity and learning, here are some ideas, even though I haven't tried them myself:
Maybe calling CoreFoundation C APIs directly will work.
Write a C wrapper, that provides the interface you need, using good old C network APIs. This approach would probably require more effort than the others, as you will be re-inventing a lot of wheels.
Write a C wrapper, but wrap a higher-level C library that works with URLs; libcurl comes to mind.
Regardless of which of these approaches you take, you will need to interface with C code. One way to do this is to use system modules, see Importing a Swift module using a C library as a possible starting point.
Since mid-2017 there is IBM's SwiftyRequest library, which works on both Linux and iOS. I've tried it on Linux and seems to work fine.
Making a request looks like this (adapted an example from docs):
import SwiftyRequest
let request = RestRequest(method: .get, url: "http://myApiCall/hello")
request.responseString() { response in
switch response.result {
case .success(let result):
print("Success")
case .failure(let error):
print("Failure")
}
}
If you're using JSON for the responses, it can even construct you an object based on a "schema" you provide (have a look at .responseObject and JSONDecodable in the tests for examples).
Not a full answer, but maybe useful to someone:
I only needed NSData(contentsOfURL: NSURL) so I ended up writing a wrapper for KituraNet that does this with a closure. I just check if there is data and return that.
Because NSURL is far from fully implemented I also decided to pass the url as a String for now.
It's not a replacement for NSURLRequest, just the good old convenience of NSData(contentsOfURL: NSURL)
.Package(url: "https://github.com/IBM-Swift/Kitura-net.git", majorVersion: 0, minor: 15),
import KituraNet
extension NSData {
public static func contents(ofUrl urlString:String, completionHandler:((data:NSData?) -> Void)) {
let request = HTTP.request(urlString) { (response) in
guard let response = response else {
completionHandler(data: nil)
return
}
let responseData = NSMutableData()
do {
try response.readAllData(into: responseData)
completionHandler(data: responseData)
return
} catch {
completionHandler(data: nil)
return
}
}
request.end()
}
}
Related
I need to make a function that will receive an HTML page from the link. Since I use the yew library, WASM does not allow me to use many custom libraries and functions. For example, the library Tokio, future and the function reqwest::blocking::get().
I had something like this code that works in the Rust test file:
pub fn get_response() {
let link = "url";
let response = reqwest::blocking::get(link).unwrap();
let res = response.text().unwrap();
}
But as I said, the yew library does not allow me to use blocking::get() how do I make the same function but without using blocking::get()?
If you are using Yew, then your Rust code will always be running within the context of a component or in service there-of. So you should have access to a Context or at least a Scope and then can issue async tasks like so:
ctx.link().send_future(async move {
// do your reqwest call here
});
There are slightly different but similar functions like .callback_future(), .callback_future_once(), and .send_future_batch() but are really only different in terms of how they are called or how they interact with Yew. And with almost all scope functions, these will expect to return some message to the component, since you're likely to want to update some state after receiving the response.
If you really don't want to use Yew's scope functions, or don't have a reasonable way to access them, you can use spawn_local from the wasm-bindgen-futures crate that Yew uses internally:
wasm_bindgen_futures::spawn_local(async move {
// do your reqwest call here
})
I am trying to build a custom Apollo extension to capture some performance metrics (duration of execution) of resolvers and logging them to an APM tool. From the Apollo documentation, Github issue here and an example published by Apollo, I found that the method willResolveField when overridden, receives GraphQLResolveInfo (which in turn has the parent type parentType and field name fieldName). If one can notice, the fields are already resolved when this method is called by the Apollo server. Does someone know where this field resolution actually takes place before sending it to willResolveField?
On the other note, unless my understanding is wrong - the name willResolveField seems to be quite misleading. Can someone kindly shed some light on this?
Sample code of what I'm trying to achieve
class GraphQLAPMExtension implements GraphQLExtension<TContext> {
requestDidStart(options:{ request, operationName, ... }) {
// perform APM specifics to log the request and other info
return (...errors) => {
if(errors.length) {
// some more custom APM stuff!
}
}
}
willResolveField(source, args, context: TContext, info: GraphQLResolveInfo) {
// info contains parentType and fieldName
// and it seems to be that fields are already resolved and passed to this function
}
}
After some amount of digging into the graphql package. It is looking like the function resolveFieldValueOrError does the resolution. It can be found under the ./execution section. Looks like I will have to fork the graphQl project and make the modifications that I wish.
Another, more practical direction, was to trace the Apollo server creation's tracing parameter. After some quick digging, found that it is using the apollo-engine-reporting package.
There is already an extension for tracing requests that's built into ApolloServer. The source code can be found here. It sounds like you could just fork that.
I trying to convert a nodejs project to TypeScript and while mostly I did not faced really difficult obstacles during this process, the codebase has few gotchas like this, mostly in startup code:
function prepareConfiguration() {
let cloudConfigLoader = require('../utils/cloud-config');
return cloudConfigLoader.ensureForFreshConfig().then(function() {
//do some stuff
});
}
I may be need just an advice on which approach for refactoring this has less code changes to be made to make it work in TypeScript fashion.
In response to comments, more details:
That require loads the node module, not a JSON file. From that module the ensureForFreshConfig function contacts with a cloud service to load a list of values to rebuild a configuration state object.
Problem is that mdule was made in standard node approach of "module is isngleton object" and its independencies include auth component that will be ready only when the shown require call is made. I know it is not best a way to do so..
Typesript does not allow "mport that module later" except with dynamyc import which is problematic as mentiond in comment.
The "good" approach is to refactor that part of startup and make the ensureForFreshConfig and its dependency to initiate its ntenras on demand via cnstructors.. I just hoped ofr some soluiton to reduce things to be remade durng this transition to the TypeScript
import { cloudConfigLoader } from '../utils/cloud-config'
async function prepareConfiguration() {
await cloudConfigLoader.ensureForFreshConfig()
// do some stuff
// return some-stuff
}
The function is to be used as follows.
await prepareConfiguration()
I am writing a command line application in Swift using a third-party framework that (if I understand the code correctly) relies on GCD callbacks to complete certain actions when a socket receives data. In order to better understand the framework, I have been playing around with a sample Cocoa application the framework's author wrote to go along with the framework.
Because the sample application is a Cocoa application, the run loops are handled automatically. I'm including snippets of code from the sample application (MIT license) to give an idea of how it works:
class AppDelegate: NSObject, NSApplicationDelegate {
var httpd : Connect!
func startServer() {
httpd = Connect()
.onLog {
[weak self] in // unowned makes this crash
self!.log($0)
}
.useQueue(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))
...
httpd.listen(1337)
}
...
func applicationDidFinishLaunching(aNotification: NSNotification?) {
startServer()
...
}
}
I'd like to modify the sample application to run from the command line. When I put the startServer() function into a command line application, it runs, but the socket is immediately closed after it is opened, and the program finishes executing with an exit code 0. This is expected behavior, as there are no run loops in an Xcode command line project, and thus the program doesn't know to wait for the socket to receive data.
I believe the correct way to get the socket to stay open and the program to continuously run would be to put the main thread in a CFRunLoop. I have looked over Apple's documentation and, except for the basic API reference, there is nothing on threading in Swift. I have looked at third party resources, but they all involve alternate threads in iOS and Cocoa applications. How do I properly implement a CFRunLoop for the main thread?
It seems like Martin R's answer should work, however I was able to get the socket to stay open with a single function call. At the end of the startServer() function, I put the line:
CFRunLoopRun()
Which worked.
The NSRunLoop Class Reference
has an example for a simple runloop:
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
which can be translated to Swift:
var shouldKeepRunning = true // global
let theRL = NSRunLoop.currentRunLoop()
while shouldKeepRunning && theRL.runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture()) { }
Alternatively, it might be sufficient to just call
dispatch_main()
Update for Swift 3.1:
let theRL = RunLoop.current
while shouldKeepRunning && theRL.run(mode: .defaultRunLoopMode, before: .distantFuture) { }
or
dispatchMain()
I decided to start a new project to get into hacklang, and after fixing some if the problems I initially ran into transitioning from php habits, I ran into the following errors:
Unbound name: str_replace
Unbound name: empty
Doing some research I found that this is due to using 'legacy' php which isn't typechecked, and will error with //strict.
That's fine and all, empty() was easy enough to replace, however str_replace() is a bit more difficult.
Is there an equivalent function that will work with //strict? Or at least something similar.
I'm aware that I could use //decl but I feel like that defeats the purpose in my case.
Is there at least any way to tell which functions are implemented in hack and which are not in the documentation as I couldn't find one?
For reference (though it isn't too relevant to the question itself), here is the code:
<?hh //strict
class HackMarkdown {
public function parse(string $content) : string {
if($content===null){
throw new RuntimeException('Empty Content');
}
$prepared = $this->prepare($content);
}
private function prepare(string $contentpre) : Vector<string>{
$contentpre = str_replace(array("\r\n","\r"),"\n",$contentpre);
//probably need more in here
$prepared = Vector::fromArray(explode($contentpre,"\n"));
//and here
return $prepared;
}
}
You don't need to change your code at all. You just need to tell the Hack tools about all the inbuilt PHP functions.
The easiest way to do this is to download this folder and put it somewhere in your project. I put it in a hhi folder in the base of my project. The files in there tell Hack about all the inbuilt PHP functions.
Most of them don't have type hints, which can lead to Hack thinking the return type of everything is mixed instead of the actual return, that is actually correct in most cases as, for example, str_replace can return either a string or a bool. However, it does stop the "unbound name" errors, which is the main reason for adding them.