I keep getting the nw_socket_handle_socket_event [C1.1.1:2] Socket SO_ERROR [61: Connection refused] when I am trying to enter the HabitDetailView and UserDetailView. The server gives the information for the Habit/User Collection View (/habits and /users), but it does not give any of the images, UserStats or Habit Stats. I've posted below how the APIRequest and APIService code looks like. It just has me stumped that it gives some of the info, but blocks other parts.
API Request
import UIKit
protocol APIRequest { associatedtype Response
var path: String { get }
var queryItems: [URLQueryItem]? { get }
var request: URLRequest { get }
var postData: Data? { get }
}
extension APIRequest { var host: String { "localhost" } var port: Int { 8080 } }
extension APIRequest { var queryItems: [URLQueryItem]? { nil } var postData: Data? { nil } }
extension APIRequest { var request: URLRequest { var components = URLComponents()
components.scheme = "http"
components.host = host
components.port = port
components.path = path
components.queryItems = queryItems
var request = URLRequest(url: components.url!)
if let data = postData {
request.httpBody = data
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
}
return request
}
}
API Service
import UIKit
struct HabitRequest: APIRequest { typealias Response = [String: Habit]
var habitName: String
var path: String { "/habits" }
}
struct UserRequest: APIRequest { typealias Response = [String: User]
var path: String { "/users"}
}
struct HabitStatisticsRequest: APIRequest { typealias Response = [HabitStatistics]
var habitNames: [String]?
var path: String { "/habitStats"}
var queryItems: [URLQueryItem]? {
if let habitNames = habitNames {
return [URLQueryItem(name: "names", value: habitNames.joined(separator: ","))]
} else {
return nil
}
}
}
struct UserStatisticsRequest: APIRequest { typealias Response = [UserStatistics]
var userIDs: [String]?
var path: String { "/userStats"}
var queryItems: [URLQueryItem]? {
if let userIDs = userIDs {
return [URLQueryItem(name: "ids", value: userIDs.joined(separator: ","))]
} else {
return nil
}
}
}
struct HabitLeadStatisticsRequest: APIRequest { typealias Response = UserStatistics
var userID: String
var path: String { "/userLeadingStats" + userID}
}
struct ImageRequest: APIRequest { typealias Response = UIImage
var imageID: String
var path: String { "/images/" + imageID }
}
enum APIRequestError: Error { case itemsNotFound case requestFailed(HTTPURLResponse) }
extension APIRequest where Response: Decodable { func send() async throws -> Response { let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw APIRequestError.requestFailed(HTTPURLResponse())
}
guard httpResponse.statusCode == 200 else {
throw APIRequestError.itemsNotFound
}
let decoder = JSONDecoder()
let decoded = try decoder.decode(Response.self, from: data)
return decoded
}
}
enum ImageRequestError: Error { case couldNotIntializeFromData case imageDataMissing }
extension APIRequest where Response == UIImage { func send() async throws -> UIImage { let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw ImageRequestError.imageDataMissing
}
guard let image = UIImage(data: data) else {
throw ImageRequestError.couldNotIntializeFromData
}
return image
}
}