I am trying to convert a simple URLSession request in Swift to using NWConnection. This is because I want to make the request using a Proxy that requires Authentication. I posted this SO Question about using a proxy with URLSession. Unfortunately no one answered it but I found a fix by using NWConnection instead.
Working Request
func updateOrderStatus(completion: @escaping (Bool) -> Void) {
let orderLink = "https://shop.ccs.com/51913883831/orders/f3ef2745f2b06c6b410e2aa8a6135847"
guard let url = URL(string: orderLink) else {
completion(true)
return
}
let cookieStorage = HTTPCookieStorage.shared
let config = URLSessionConfiguration.default
config.httpCookieStorage = cookieStorage
config.httpCookieAcceptPolicy = .always
let session = URLSession(configuration: config)
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", forHTTPHeaderField: "Accept")
request.setValue("none", forHTTPHeaderField: "Sec-Fetch-Site")
request.setValue("navigate", forHTTPHeaderField: "Sec-Fetch-Mode")
request.setValue("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0.1 Safari/605.1.15", forHTTPHeaderField: "User-Agent")
request.setValue("en-US,en;q=0.9", forHTTPHeaderField: "Accept-Language")
request.setValue("gzip, deflate, br", forHTTPHeaderField: "Accept-Encoding")
request.setValue("document", forHTTPHeaderField: "Sec-Fetch-Dest")
request.setValue("u=0, i", forHTTPHeaderField: "Priority")
// make the request
}
Attempted Conversion
func updateOrderStatusProxy(completion: @escaping (Bool) -> Void) {
let orderLink = "https://shop.ccs.com/51913883831/orders/f3ef2745f2b06c6b410e2aa8a6135847"
guard let url = URL(string: orderLink) else {
completion(true)
return
}
let proxy = "resi.wealthproxies.com:8000:akzaidan:x0if46jo-country-US-session-7cz6bpzy-duration-60"
let proxyDetails = proxy.split(separator: ":").map(String.init)
guard proxyDetails.count == 4, let port = UInt16(proxyDetails[1]) else {
print("Invalid proxy format")
completion(false)
return
}
let proxyEndpoint = NWEndpoint.hostPort(host: .init(proxyDetails[0]),
port: NWEndpoint.Port(integerLiteral: port))
let proxyConfig = ProxyConfiguration(httpCONNECTProxy: proxyEndpoint, tlsOptions: nil)
proxyConfig.applyCredential(username: proxyDetails[2], password: proxyDetails[3])
let parameters = NWParameters.tcp
let privacyContext = NWParameters.PrivacyContext(description: "ProxyConfig")
privacyContext.proxyConfigurations = [proxyConfig]
parameters.setPrivacyContext(privacyContext)
let host = url.host ?? ""
let path = url.path.isEmpty ? "/" : url.path
let query = url.query ?? ""
let fullPath = query.isEmpty ? path : "\(path)?\(query)"
let connection = NWConnection(
to: .hostPort(
host: .init(host),
port: .init(integerLiteral: UInt16(url.port ?? 80))
),
using: parameters
)
connection.stateUpdateHandler = { state in
switch state {
case .ready:
print("Connected to proxy: \(proxyDetails[0])")
let httpRequest = """
GET \(fullPath) HTTP/1.1\r
Host: \(host)\r
Connection: close\r
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0.1 Safari/605.1.15\r
Accept-Language: en-US,en;q=0.9\r
Accept-Encoding: gzip, deflate, br\r
Sec-Fetch-Dest: document\r
Sec-Fetch-Mode: navigate\r
Sec-Fetch-Site: none\r
Priority: u=0, i\r
\r
"""
connection.send(content: httpRequest.data(using: .utf8), completion: .contentProcessed({ error in
if let error = error {
print("Failed to send request: \(error)")
completion(false)
return
}
// Read data until the connection is complete
self.readAllData(connection: connection) { finalData, readError in
if let readError = readError {
print("Failed to receive response: \(readError)")
completion(false)
return
}
guard let data = finalData else {
print("No data received or unable to read data.")
completion(false)
return
}
if let body = String(data: data, encoding: .utf8) {
print("Received \(data.count) bytes")
print("\n\nBody is \(body)")
completion(true)
} else {
print("Unable to decode response body.")
completion(false)
}
}
}))
case .failed(let error):
print("Connection failed for proxy \(proxyDetails[0]): \(error)")
completion(false)
case .cancelled:
print("Connection cancelled for proxy \(proxyDetails[0])")
completion(false)
case .waiting(let error):
print("Connection waiting for proxy \(proxyDetails[0]): \(error)")
completion(false)
default:
break
}
}
connection.start(queue: .global())
}
private func readAllData(connection: NWConnection,
accumulatedData: Data = Data(),
completion: @escaping (Data?, Error?) -> Void) {
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { data, context, isComplete, error in
if let error = error {
completion(nil, error)
return
}
// Append newly received data to what's been accumulated so far
let newAccumulatedData = accumulatedData + (data ?? Data())
if isComplete {
// If isComplete is true, the server closed the connection or ended the stream
completion(newAccumulatedData, nil)
} else {
// Still more data to read, so keep calling receive
self.readAllData(connection: connection,
accumulatedData: newAccumulatedData,
completion: completion)
}
}
}
Post
Replies
Boosts
Views
Activity
I'm simply trying to use a proxy to route a http request in Swift to measure the average round trip time of a list of proxies. I've went through multiple Stack Overflow threads on this topic but they are all super old / outdated.
format:host:port:username:password
I also added the info.plist entry:
NSAllowsArbitraryLoads -> NSExceptionDomains
When I call the function below I am prompted with a menu that says
"Proxy authentication required. Enter the password for HTTP proxy ... in settings"
I closed this menu inside my app and tried the function below again and it worked without giving me the menu a second time. However even though the function works without throwing any errors, it does NOT use the proxies to route the request.
Why does the request work (throws no errors) but does not use the proxies? I'm assuming it's because the password isn't entered in the settings as the alert said. My users will want to test proxy speeds for many different Hosts/Ports, it doesn't make sense to enter the password in settings every time. How can I fix this issue?
func averageProxyGroupSpeed(proxies: [String], completion: @escaping (Int, String) -> Void) {
let numProxies = proxies.count
if numProxies == 0 {
completion(0, "No proxies")
return
}
var totalTime: Int64 = 0
var successCount = 0
let group = DispatchGroup()
let queue = DispatchQueue(label: "proxyQueue", attributes: .concurrent)
let lock = NSLock()
let shuffledProxies = proxies.shuffled()
let selectedProxies = Array(shuffledProxies.prefix(25))
for proxy in selectedProxies {
group.enter()
queue.async {
let proxyDetails = proxy.split(separator: ":").map(String.init)
guard proxyDetails.count == 4,
let port = Int(proxyDetails[1]),
let url = URL(string: "http://httpbin.org/get") else {
completion(0, "Invalid proxy format")
group.leave()
return
}
var request = URLRequest(url: url)
request.timeoutInterval = 15
let configuration = URLSessionConfiguration.default
configuration.connectionProxyDictionary = [
AnyHashable("HTTPEnable"): true,
AnyHashable("HTTPProxy"): proxyDetails[0],
AnyHashable("HTTPPort"): port,
AnyHashable("HTTPSEnable"): false,
AnyHashable("HTTPUser"): proxyDetails[2],
AnyHashable("HTTPPassword"): proxyDetails[3]
]
let session = URLSession(configuration: configuration)
let start = Date()
let task = session.dataTask(with: request) { _, _, error in
defer { group.leave() }
if let error = error {
print("Error: \(error.localizedDescription)")
} else {
let duration = Date().timeIntervalSince(start) * 1000
lock.lock()
totalTime += Int64(duration)
successCount += 1
lock.unlock()
}
}
task.resume()
}
}
group.notify(queue: DispatchQueue.main) {
if successCount == 0 {
completion(0, "Proxies Failed")
} else {
let averageTime = Int(Double(totalTime) / Double(successCount))
completion(averageTime, "")
}
}
}
Hello my Xcode app fetches the current user from my database and stores their data in a User struct. And this data is needed for other portions of the app. I use an EnvirnmentObject to translate this data to other views. But the problem Im running in is: if I modify a variable in the struct of the current user in one view then navigate to another view this change is not observed in the new view. I don't think you can make @Published vars static, but what's another approach so that if I modify the variable "followers" for example in one view then all the others views will also have this change. I want to do this so I don't have to fetch the current user from the database every time I make changes to the current user.
getting the current user:
import SwiftUI
import Firebase
class AuthViewModel: ObservableObject{
private let service = UserService()
@Published var currentUser: User?
init(){
self.fetchUser()
}
func fetchUser(){
service.fetchUser() { user in
self.currentUser = user
}
}
}
view 1
import SwiftUI
struct viewOne: View {
@EnvironmentObject var authViewModel: AuthViewModel
var body: some View {
VStack(alignment: .center, spacing: 2){
AuthViewModel.currentUser.followers = 25
}
}
}
view 2 - change in followers var is not seen
import SwiftUI
struct viewOne: View {
@EnvironmentObject var authViewModel: AuthViewModel
var body: some View {
VStack(alignment: .center, spacing: 2){
Text("\(AuthViewModel.currentUser.followers)") // not 25
}
}
}
Hello, I have an Xcode app with a few thousand users and I use firestore to store my data. Every time a user registers firestore creates a random uid for them. Right now I have all my users in one big collection called "users." I was thinking about splitting up this collection into sub collections for each letter in the alphabet and then putting each user, based on the first letter of their name, in the corresponding sub collection. But the only problem with this is when I want to fetch the current user. When a user signs into the app I only have their uid from the firestore authentication. So if I want to fetch the current user I wouldn't really have any pointer to which sub collection to look into. I thought of maybe putting a pointer letter at the beginning of the UID but I don't think firestore allows me to change Uids. Does anyone know a better approach to this or how I can make this work. Id appreciate the help.
Hello, I have been playing around with preference keys and geometry readers but I can't figure out a way to actually get the real size of a scrollview or vstack inside a scroll view. I have 2 different approaches and they both semi work, the problem with them is that when I scroll through the scroll view the size fluctuates. For example if the size of the vstack in the scroll view is 5000, then the size I get from my methods will fluctuate between 4200-5900 as I scroll through the view. Im not sure why this is, can anyone show me a solid way to get the height of a vstack inside a scrollview.
method 1
ScrollView {
LazyVStack {
ForEach(viewModel.new.) { i in
someView(i)
}
}
.background(GeometryReader { proxy in
Color.clear
.onChange(of: offset) { _ in
print("Height of VStack:", proxy.size.height)
}
})
}
method 2
ScrollView {
ChildSizeReader(size: $scrollViewSize) {
LazyVStack {
ForEach(viewModel.new) { i in
someView(i)
}
}
}
}
}
struct ChildSizeReader<Content: View>: View {
@Binding var size: CGSize
let content: () -> Content
var body: some View {
ZStack {
content().background(
GeometryReader { proxy in
Color.clear.preference(
key: SizePreferenceKey.self,
value: proxy.size
}
}
}
}
.onPreferenceChange(SizePreferenceKey.self) { preferences in
self.size = preferences
}
}
}
struct SizePreferenceKey: PreferenceKey {
typealias Value = CGSize
static var defaultValue: Value = .zero
static func reduce(value _: inout Value, nextValue: () -> Value) {
_ = nextValue()
}
}
Hello, my Xcode app has worked just fine for the last few weeks, I have not changed anything and when I run it now, the tab bar disappears when the associated tab is selected, also stuff like the cursor, navigationbarbackbutton are all invisible or white. How can I fix this, not sure if it's an Xcode bug.
tab view:
init() {
let tabBarAppearance = UITabBarAppearance()
tabBarAppearance.configureWithOpaqueBackground()
UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
}
content view:
TabView(selection: $selectedTab){
NavigationView(){
FeedView()
}
.tabItem {
Image(systemName: "h.circle")
}.tag(1)
NavigationView(){
JobsView()
}
.tabItem {
Image(systemName: "j.circle")
.tint(.gray)
}.tag(2)
}
so I have two functions the first gets an array of structs and the second func gets another struct object and appends it to the first array. In my view I have a scroll view that displays all these elements in the array. When I run the code with the getAd function commented out (does not execute at all) the view displays the correct array of elements in the scroll view. but when I uncomment the getAd function the view does not display any elements at all. But I added a button to print the viewModel.new array, and when I click it, the correct array is printed out WITH the final element appended. Since the app works fine when the second function is commented out, it leads me to believe that the second func is causing the problem, but when I print the array with the button, the result looks just fine how I want it to look. Im not sure what the problem is, maybe the asynchronous nature of the functions.
view
import SwiftUI
struct FeedView: View {
@StateObject var viewModel = FeedViewModel()
var body: some View {
mainInterFaceView
}
}
extension FeedView{
var mainInterFaceView: some View{
VStack(){
ZStack(){
ScrollView {
LazyVStack {
ForEach(viewModel.new) { tweet in
TweetRowView(tweet: tweet, hasDivider: true)
}
}
}
}
}
}
}
}
viewModel:
class FeedViewModel: ObservableObject{
@Published var new = [Tweet]()
var ads = [Tweet]()
let service = TweetService()
let userService = UserService()
init(){
fetchNew()
}
func fetchNew(){
service.fetchNew { tweets in
self.new = tweets
for i in 0 ..< tweets.count {
let uid = tweets[i].uid
self.userService.fetchUser(withUid: uid) { user in
self.new[i].user = user
}
}
self.getAd() //here
}
}
func getAd() {
service.fetchAd { ads in
self.ads = ads
for i in 0 ..< ads.count {
let uid = ads[i].uid
self.userService.fetchUser(withUid: uid) { user in
self.ads[i].user = user
}
}
self.new.append(self.ads[0])
}
}