I'm testing Alamofire with ssl certificate.
using swift and swiftUI I wrote this 2 functions to load the ssl from a bundle and make the request using Alamofire.
func getCertificate() -> [SecCertificate] {
let url = Bundle.main.url(forResource: "ssl", withExtension: "cer")
let localCer = try! Data(contentsOf: url!) as CFData
guard let certificate = SecCertificateCreateWithData(nil, localCer) else {
return []
}
return [certificate]
}
func loginUserIcrew(userName: String, password: String){
let evaluators: [String: ServerTrustEvaluating] = [
linkIcrew: PinnedCertificatesTrustEvaluator(certificates:getCertificate())
]
let manager = ServerTrustManager(evaluators: evaluators)
let session = Session(serverTrustManager: manager)
session.request (linkIcrew,method: .get, encoding: URLEncoding.default)
.response { response in
print(response)
}
}
and I using it in a simple button like this
struct SalaryStart: View {
#ObservedObject var ss = SalaryManager()
var body: some View {
Button {
ss.loginUserIcrew(userName: "user", password: "pass")
} label: {
Text("test")
}
}
}
I'm getting the error : Alamofire.AFError.sessionDeinitialized
any help how to solve tis issue? reading online looks like the session need to keep alive, but I don't understand what does it mean??
thanks for the help
sessionDeinitialized means what it says: your Session was deinitialized while the request was in progress and so it was cancelled. You need to keep the Session alive at least long enough to complete the request. Usually you want to use a single Session for all of your requests, so I suggest keeping it as a singleton.
Related
I have a problem when trying to fetch some data with swift on apple watch. When i try this code in Swift Playground it works well but when I put it in an WatchOS app it shows me this error :
Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “kholle.fr” which could put your confidential information at risk." UserInfo={NSErrorClientCertificateStateKey=0, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataPDTask <8B22FE2A-DFA9-4655-82A5-357F5A732CCD>.<1>, NSErrorFailingURLKey=https://kholle.fr/backend_saved, _NSURLErrorRelatedURLSessionTaskErrorKey=(
Here is the code I use in my WatchOS App
class ViewModel: ObservableObject {
#Published var kholles: [Kholle] = []
func fetch(group: String) {
let url = URL(string: "https://kholle.fr/backend_saved")! //Here is my https server link
let task = URLSession.shared.dataTask(with: url) { data, _,
error in
guard let data = data, error == nil else {
return
}
//Convert to JSON
do {
let kholle = try JSONDecoder().decode([String: [Kholle]].self, from: data)
DispatchQueue.main.async {
print(kholle["B 16"])
}
} catch {
print(error)
}
}
task.resume()
}
}
Thanks in advance for your help
I found out the solution by adding to my node server an Intermediate Certificate.
struct User: Codable{
var id: String
var username: String
var password: String
var profilepic: String
var email: String
}
func getusers(userId: String) {
let url = URL(string: "http://127.0.0.1:8080/users/\(userId)")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
if let user = try? JSONDecoder().decode(User.self, from: data) {
print(user)
} else {
print("Invalid Response")
}
} else if let error = error {
print("HTTP Request Failed \(error)")
}
}
task.resume()
}
cell.name.text = "\(user.username)"
As the error mentions, it is because user is out of scope when you are trying to use it.
In your code snippet, user is defined in an if-let block. It will only be available within that block of code. If you want to use it elsewhere, you will need to save it somewhere else.
Here is the official Swift Programming Language Guide, I highly recommend that you read over it and familiarize yourself with basic coding principles Scope and Contexts.
I have created a simple SwiftUI app, which should display a list of songs fetched from iTunes url link:
import SwiftUI
struct Response: Codable {
var results: [Result]
}
struct Result: Codable {
var trackId: Int
var trackName: String
var collectionName: String
}
struct ContentView: View {
#State private var results = [Result]()
var body: some View {
List(results, id: \.trackId) { item in
VStack(alignment: .leading) {
Text(item.trackName)
.font(.headline)
Text(item.collectionName)
}
}
.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=song") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
DispatchQueue.main.async {
self.results = decodedResponse.results
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
When the internet connection on my Mac is On, I am able to fetch the data and load it to the list. I decided to turn it off, then I completely closed/deleted the app from memory then opened it app again and the data was still there. This is very confusing for me, can somebody explain me what is happening, because the code should load the data only when the Wi-Fi is ON, why I am still able to see it when it is OFF?
Short video of my simulator
My question is, why here is gone?
Just try to use .reloadIgnoringLocalCacheData. It worked for me.
URLSession.shared caches data by default if you don't change the configuration. Just edit the request to
let request = URLRequest(url: url, cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalCacheData)
I'm trying to do the unit tests for my app.
I've this function preparing the request
func getWeatherDataAtLocation() {
let WEATHER_URL = "http://api.openweathermap.org/data/2.5/weather"
let weatherAPI = valueForAPIKey(named:"weatherAPI")
let lat = String(locationService.latitude)
let lon = String(locationService.longitude)
do {
try networkService.networking(url: "\(WEATHER_URL)?APPID=\(weatherAPI)&lon=\(lon)&lat=\(lat)", requestType: "weather")
} catch let error {
print(error)
}
}
I've a service class networkservice processing the network request :
class NetworkService {
var weatherDataDelegate: WeatherData?
var session: URLSession
init(session: URLSession = URLSession(configuration: .default)) {
self.session = session
}
func networking(url: String, requestType: String) {
var request = URLRequest(url: requestUrl)
request.httpMethod = "GET"
var task: URLSessionDataTask
task = session.dataTask(with: request) { (data, response, error) in
switch requestType {
case "weather":
do {
let weatherJSON = try JSONDecoder().decode(WeatherJSON.self, from: data)
self.weatherDataDelegate?.receiveWeatherData(weatherJSON)
} catch let jsonErr {
print(jsonErr)
}
case // Other cases
default:
print("error")
}
}
task.resume()
}
}
Then i've the delegate running this function to update the JSON received
func receiveWeatherData(_ data: WeatherJSON) {
self.dataWeather = data
do {
try updateWeatherDataOnScreen()
} catch let error {
print(error)
}
}
The issue is I've no idea how I can write some code to test this and all the ressources I find is to test with a callback, any idea?
So there are mutliple steps in this.
1: Create a mocked version of the response of exactly this request. And save it in a json file. Named like weather.json
2: Once you have done that you want to add an #ifdef testSchemeName when executing request. And tell it to tell your function called networking() to read from a file named "\(requestType).json" instead of making the request.
Optional, more advanced way:
This actually intercepts your request and send you the file data instead. A bit more advanced, but your testing gets 1 level deeper.
I am using Alamofire for my requests and I get cookies in some of them, everything works fine when I launch the app and use it but when I kill the app and reopen the cookies are not there anymore. I searched a lot and found this but none of the answers helped.
I try to save the cookies after each request and load them before sending request as below:
func saveCookies(response: DataResponse<Any>) {
let headerFields = response.response?.allHeaderFields as! [String: String]
let url = response.response?.url
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url!)
var cookieArray = [[HTTPCookiePropertyKey: Any]]()
for cookie in cookies {
cookieArray.append(cookie.properties!)
}
UserDefaults.standard.set(cookieArray, forKey: "savedCookies")
UserDefaults.standard.synchronize()
}
func loadCookies() {
guard let cookieArray = UserDefaults.standard.array(forKey: "savedCookies") as? [[HTTPCookiePropertyKey: Any]] else { return }
for cookieProperties in cookieArray {
if let cookie = HTTPCookie(properties: cookieProperties) {
HTTPCookieStorage.shared.setCookie(cookie)
}
}
}
But still when I kill the app, I can't get the data.
Try to save your cookies in UserDefaults like this:
var authToken: String {
get {
return defaults.value(forKey: TOKEN_KEY) as! String
}
set {
defaults.set(newValue, forKey: TOKEN_KEY)
}
}