Swift AlamoFire 5 won't execute Request - ios

My problem is, when I execute the request it will happen nothing.
What I made:
I made an Api with .net core Api on a another PC and now I want to make a IOS App, that has access to the Api. (The API works I tested it with swagger and Insomnia) On the IOS app I made a put request with AlamoFire. And when I execute the request it's happening nothing. I watched the Network with Wireshark and there comes Nothing too.
When I Debug, it will step through the request Methode, but it goes nothing out to the API. And there are no errors.
// buttonClick will call this Methode
func requst() {
// testing parameters
let params: Parameters = [
"nfcId": 1,
"date" : "2021-04-12T09:47:12.053Z"
]
//
session.request("http://MyAPI/api/Guard/AddGuard", method: .put, parameters: params, headers: nil).validate(statusCode: 200 ..< 299)
}
I made an own Session wehere I pinned a certificate
private let certificates = [
"https://MyAPI:5001":
PinnedCertificatesTrustEvaluator(certificates: [Certificates.certificate], acceptSelfSignedCertificates: false, performDefaultValidation: true, validateHost: true)
]
private let session: Session
init(allHostsMustBeEvaluated: Bool) {
let serverTrustPolicy = ServerTrustManager(allHostsMustBeEvaluated: allHostsMustBeEvaluated, evaluators: certificates)
let config = URLSessionConfiguration.af.default
session = Session(configuration: config, serverTrustManager: serverTrustPolicy)
}

You haven't attached a response handler or called resume(), so no, the request isn't going to do anything. Adding something like responseDecodable will start the request automatically.

Related

Save and resend Alamofire request

I am using Alamofire and I want to send a get request. If this fails, I want to retry it again after some other operations.
I save the request in a variable:
let myRequest = Alamofire.request("myurl", method: .get)
and in another function:
func retry(request:DataRequest) {
request.responseSwiftyJSON { response in
// ... code to handle the response ...
}
}
In this way, I can call multiple times the retry function, passing the same object myRequest.
However, the request is sent correctly only the first time, then I think it is cached (or in some way the Alamofire object realizes that the response field of the request object is already valid) and the request is not sent again to the server.
To solve this problem, I tried to change a little bit the retry function in this way:
func retry2(request:DataRequest) {
Alamofire.request(request.request!).responseSwiftyJSON { response in
// ... code to handle the response ...
}
}
This should initialize a new Alamofire request using only the URLRequest field of the saved request, but now the request is called twice every time! (I check it in my backend and I am sure this is caused by using this approach).
Is there any way to resend the original saved request? Does Alamofire have some way to initialize a new request from a saved one?
Solution
I ended up by doing something like #JonShier and #DuncanC suggested me:
struct APIRequest {
let url: URLConvertible
let method: HTTPMethod
let parameters: Parameters?
let encoding: ParameterEncoding
let headers: HTTPHeaders?
init(_ url:URLConvertible, method:HTTPMethod = .get, parameters:Parameters? = nil, encoding:ParameterEncoding = URLEncoding.default, headers:HTTPHeaders? = nil) {
self.url = url
self.method = method
self.parameters = parameters
self.encoding = encoding
self.headers = headers
}
func make() -> DataRequest {
return Alamofire.request(url, method: method, parameters: parameters, encoding: encoding, headers: headers)
}
}
I haven't used AlamoFire much, and that minimal use was a couple of years ago.
Looking around in the AlamoFire code, it appears that the request() function returns a DataRequest object. Further, it looks like a DataRequest is a reference type that is meant to track a specific request to a server and it's responses.
It looks to make like once you create one, you use it until it completes or fails, and then discard it. They do not appear to be intended to be reused for subsequent requests of the same type.
To summarize:
Question: "How do I reuse AlamoFire DataRequest objects."
Answer: "Don't do that."

Alamofire request to twilio swift

I'm trying to get an SMS from twilio.com but get nil in response. Can anyone say what I'm doing wrong?
class SMSVerificationService: NSObject {
static let sharedInstance = SMSVerificationService()
func SMSRequest(countryCode:String, phoneNumber: String) {
let accountSid = "ACc4d9785419f144412823ff20as34660c3d"
let authToken = "4wqecx41f8999caa23735da214" // changed :)
let url = URL(string: "https://\(accountSid):\(authToken)#api.twilio.com/2010-04-01/Accounts\(accountSid)/Messages")
print("url", url!)
let parameters = [
"To": "+37378847884",
"From" : "+14243960339",
"Body": "Hi daddy"
]
Alamofire.request(url!, method: .post, parameters: parameters,
encoding: JSONEncoding.default, headers: [:]).responseJSON { response in
let response = String(describing: response.result.value)
print(response)
}
}
}
Twilio developer evangelist here.
We do not recommend that you make requests to the Twilio API directly from your native application. To do so, you would need to store or retrieve your account SID and auth token in the application somewhere. If you do this, then a malicious attacker could get access to your credentials and abuse your Twilio account.
This is actually known as the Eavesdropper vulnerability and was written about earlier this year.
Instead we recommend that you create a web application and send the API requests from there. There is a blog post on how to do that here: https://www.twilio.com/blog/2016/11/how-to-send-an-sms-from-ios-in-swift.html
I notice that your class is called SMSVerificationService too. If you are looking to build phone verification, then I recommend you take a look at the Twilio Verify API that does a lot of the work for you.

Alamofire 3: cancel request with custom error

(Using iOS 9, Swift 2, Alamofire 3)
My app interacts with a REST service that requires a session token in the header. If the session token isn't already available before manager.request() is called, there's no reason to send the request only to have it fail. So I'd like to abort the request with my own error, and have the request's chained response handler take care of it - i.e. the caller wouldn't know that the request was actually never sent to the server.
What's the best way to do this with Alamofire 3?
Any way to have the same effect as Request.cancel() but with custom error, as below?:
static func request(method: Alamofire.Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil) -> Request {
let api = apiManager.sharedInstance
guard let sessionToken = api.sessionToken else {
let req = api.alamofireManager.request(method, URLString, parameters)
req.cancel() // ***I'd like to do: req.cancelWithError(.NoSessionToken)
return req
}
// Create request and add session token to header
// ...
}

Alamofire Background Service, Global Manager? Global Authorisation Header?

Can anyone tell me how to use the Alamofire background service correctly?
My attempts are:
Login View Controller
// Create a global variable for the manager
var manager = Alamofire.Manager()
var configuration = NSURLSessionConfiguration()
class LoginViewController: UIViewController {
// set the configuration for the manager
configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("com.xxx.app.backgroundService")
configuration.sharedContainerIdentifier = "com.xxx.app.backgroundService"
}
Now creating my login attempt, when successful go to the MainViewController and do here some more Requests.
manager.request(.POST, "\(serviceUrl)public/service/authenticate", parameters: ["email": txtEmail.text!, "password": txtPassword.text!]) {
......
}
Here ill get my token for all requests, so ill add the to my global configuration:
class MainViewController: UIViewController {
if let token: AnyObject = NSUserDefaults.standardUserDefaults().objectForKey("token") {
configuration.HTTPAdditionalHeaders = ["Authorization": "Bearer \(token)"]
manager = Alamofire.Manager(configuration: configuration)
}
}
But now when ill logout and login again - ill get the following errors:
Warning: A background URLSession with identifier xxx.xxx.app.backgroundService already exists!
And the app crashes with:
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Task created in a session that has been invalidated'
*** First throw call stack:
So i really dont know how do use the background-service to get the following result:
At login attempt, i dont need to add an authentication header. But then, i would like to add it to every request. All requests should run as background service.
Can anyone help me out, how to solve that in a correct way? Thanks in advance!
You have multiple problems. The first is that you need to pass the configuration object into the Manager initializer. Otherwise you are not using the configuration for the underlying URL session in the Manager instance.
The second issue is that you should NOT set header values on a configuration after it has been applied to a Manager instance. The Apple docs specifically call this out. We also call this out in our README. If you need to deal with authorization tokens, you need to pass them as a header attached to the actual request.
Update #1
Here's a small example showing how you could create a Manager instance that could be used globally.
class NetworkManager {
var authToken = "1234" // You'll need to update this as necessary
let manager: Manager = {
let configuration: NSURLSessionConfiguration = {
let identifier = "com.company.app.background-session"
let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(identifier)
return configuration
}()
return Manager(configuration: configuration)
}()
static let sharedInstance = NetworkManager()
}
class ExampleClass {
func startNetworkRequest() {
let headers = ["Authorization": NetworkManager.sharedInstance.authToken]
NetworkManager.sharedInstance.manager.request(.GET, "https://httpbin.org/get", headers: headers)
.responseJSON { response in
debugPrint(response)
}
}
}
You'll still need to work out how to update the authToken when it expires, but this shows one way how you could use your manager instance throughout your codebase.

Authenticated http request swift Alamofire

I'm struggling with getting this to work to make request to my API. Without a token works, but when I try to add additional headers, things turn to be complicated, for me.
First, the structure.
one class called: APIAsyncTask that makes the requests
one class called APIParams, just a data holder to send parameters to the APIAsyncTask class.
one class called DatabaseAPI that makes that builds the parameters, and send that to the APIAsyncTask class.
DatabaseAPI
func someMethod()
{
let task = APIAsyncTasks()
task.registerCallback { (error, result) -> Void in
print("Finished task, back at DatabaseAPI")
}
let params2 = APIParams(request: .GET, apiPath: "Posts/1", apiToken: "4iTX-56w")
task.APIrequest(params2)
}
APIAsyncTask
This part is for fixing another error, because manager was not global, the task got cancelled quickly.
var manager : Manager!
init(authenticatedRequest : Bool, token: String?)
{
manager = Alamofire.Manager()
print("Pre \(manager.session.configuration.HTTPAdditionalHeaders?.count)")
if(authenticatedRequest && token != nil)
{
var defaultHeaders = Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders!
defaultHeaders["Authorization"] = "bearer \(token)"
let configuration = Manager.sharedInstance.session.configuration
configuration.HTTPAdditionalHeaders = defaultHeaders
manager = Alamofire.Manager(configuration: configuration)
}
print("Post \(manager.session.configuration.HTTPAdditionalHeaders?.count)")
}
After some decision making, it comes down to this part.
private func GetRequest(url: String!,token : String?, completionHandler: (JSON?, NSURLRequest?, NSHTTPURLResponse?, NSError?) -> () ) -> ()
{
print("Begin Get Request")
if(token != nil)//if token is not nil, make authenticated request
{
print("just before request: \(manager.session.configuration.HTTPAdditionalHeaders?.count)")
manager.request(.GET, url, parameters: nil, encoding: .JSON).responseJSON { (request, response, json, error) in
print("Get Request (authenticated), inside alamofire request")
var resultJson : JSON?
if(json != nil)
{
resultJson = JSON(json!)
}
completionHandler(resultJson, request, response, error)
}
}
else
{
//working part without token
So as the code is now, I get an error on completing:
Mattt himself gives the answer of using Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders
, so that should be fine...
I suspect it has something to do with the multiple threads, according to this blog. Or, since it is something about CFNetwork, it could be because my API does not use SSL? I disabled NSAppTransportSecurity
I'm kind of new to swift, so examples would be really appreciated! Thankyou!
So the majority of your code looks solid.
The error leads me to believe that CFNetwork is having difficulty figuring out how to compute the protection space for the challenge. I would also assume you are getting a basic auth challenge since you are attaching an Authorization header.
Digging through your logic a bit more with this in mind led me to see that your not attaching your token to the string properly inside the Authorization header. You need to do the following instead.
defaultHeaders["Authorization"] = "bearer \(token!)"
Otherwise your Authorization header value is going to include Optional(value) instead of just value.
That's the only issue I can see at the moment. If you could give that a try and comment back that would be great. I'll update my answer accordingly if that doesn't actually solve your problem.
Best of luck!
You can add your headers in your request with Alamofire 2 and Swift 2.
For an example: go to example

Resources