CFNetwork

RSS for tag

Access network services and handle changes in network configurations using CFNetwork.

CFNetwork Documentation

Pinned Posts

Posts under CFNetwork tag

128 Posts
Sort by:
Post not yet marked as solved
10 Replies
1.5k Views
Hello, I am getting crashes on iOS 16 devices only regarding CFNetwork. Below is the full crash report. I am not able to reproduce it on my end. I've attached the .txt crash log below and also posted it below. CFNetworkCrashLog.txt Any help is appreciated, thank you! Crashed: com.apple.NSXPCConnection.m-user.com.apple.nsurlsessiond EXC_BREAKPOINT 0x00000001cfbbecec 7 Foundation 0x42054 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212 8 Foundation 0x41f3c -[NSRunLoop(NSRunLoop) runUntilDate:] + 64 9 UIKitCore 0x4d66a4 -[UIEventFetcher threadMain] + 436 10 Foundation 0x5b518 __NSThread__start__ + 716 11 libsystem_pthread.dylib 0x16cc _pthread_start + 148 12 libsystem_pthread.dylib 0xba4 thread_start + 8 com.google.firebase.crashlytics.MachExceptionServer 0 App 0x387cfc FIRCLSProcessRecordAllThreads + 393 (FIRCLSProcess.c:393) 1 App 0x3880dc FIRCLSProcessRecordAllThreads + 424 (FIRCLSProcess.c:424) 2 App 0x395be0 FIRCLSHandler + 34 (FIRCLSHandler.m:34) 3 App 0x396400 FIRCLSMachExceptionServer + 521 (FIRCLSMachException.c:521) 4 libsystem_pthread.dylib 0x16cc _pthread_start + 148 5 libsystem_pthread.dylib 0xba4 thread_start + 8 GAIThread 0 libsystem_kernel.dylib 0xda8 mach_msg2_trap + 8 1 libsystem_kernel.dylib 0x13a1c mach_msg2_internal + 80 2 libsystem_kernel.dylib 0x13c5c mach_msg_overwrite + 388 3 libsystem_kernel.dylib 0x12ec mach_msg + 24 4 CoreFoundation 0x7aac4 __CFRunLoopServiceMachPort + 160 5 CoreFoundation 0x7bd08 __CFRunLoopRun + 1232 6 CoreFoundation 0x80eb0 CFRunLoopRunSpecific + 612 7 Foundation 0x42054 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212 8 Foundation 0x41ee8 -[NSRunLoop(NSRunLoop) run] + 64 9 App 0x563d00 +[GAI threadMain:] + 64 10 Foundation 0x5b518 __NSThread__start__ + 716 11 libsystem_pthread.dylib 0x16cc _pthread_start + 148 12 libsystem_pthread.dylib 0xba4 thread_start + 8 com.apple.NSURLConnectionLoader 0 libsystem_kernel.dylib 0xda8 mach_msg2_trap + 8 1 libsystem_kernel.dylib 0x13a1c mach_msg2_internal + 80 2 libsystem_kernel.dylib 0x13c5c mach_msg_overwrite + 388 3 libsystem_kernel.dylib 0x12ec mach_msg + 24 4 CoreFoundation 0x7aac4 __CFRunLoopServiceMachPort + 160 5 CoreFoundation 0x7bd08 __CFRunLoopRun + 1232 6 CoreFoundation 0x80eb0 CFRunLoopRunSpecific + 612 7 CFNetwork 0x257ff0 _CFURLStorageSessionDisableCache + 61088 8 Foundation 0x5b518 __NSThread__start__ + 716 9 libsystem_pthread.dylib 0x16cc _pthread_start + 148 10 libsystem_pthread.dylib 0xba4 thread_start + 8 CommandHandler 0 libsystem_kernel.dylib 0xda8 mach_msg2_trap + 8 1 libsystem_kernel.dylib 0x13a1c mach_msg2_internal + 80 2 libsystem_kernel.dylib 0x13c5c mach_msg_overwrite + 388 3 libsystem_kernel.dylib 0x12ec mach_msg + 24 4 CaptiveNetwork 0x9d78 ConnectionGetCommandInfo + 160 5 CaptiveNetwork 0x7c54 __add_signal_port_source_block_invoke_2 + 244 6 libdispatch.dylib 0x3f88 _dispatch_client_callout + 20 7 libdispatch.dylib 0x7418 _dispatch_continuation_pop + 504 8 libdispatch.dylib 0x1aa58 _dispatch_source_invoke + 1588 9 libdispatch.dylib 0xb518 _dispatch_lane_serial_drain + 376 10 libdispatch.dylib 0xc18c _dispatch_lane_invoke + 384 11 libdispatch.dylib 0x16e10 _dispatch_workloop_worker_thread + 652 12 libsystem_pthread.dylib 0xdf8 _pthread_wqthread + 288 13 libsystem_pthread.dylib 0xb98 start_wqthread + 8 Thread 0 libsystem_kernel.dylib 0x12b0 __workq_kernreturn + 8 1 libsystem_pthread.dylib 0xe44 _pthread_wqthread + 364 2 libsystem_pthread.dylib 0xb98 start_wqthread + 8 Thread 0 libsystem_pthread.dylib 0xb90 start_wqthread + 254 Crashed: com.apple.NSXPCConnection.m-user.com.apple.nsurlsessiond 0 libobjc.A.dylib 0x6cec objc_opt_respondsToSelector + 48 1 libsystem_trace.dylib 0x6480 _os_log_fmt_flatten_object + 248 2 libsystem_trace.dylib 0x41a0 _os_log_impl_flatten_and_send + 1864 3 libsystem_trace.dylib 0x21bc _os_log + 152 4 libsystem_trace.dylib 0x7840 _os_log_impl + 24 5 CFNetwork 0x10dc08 _CFURLConnectionCopyTimingData + 34880 6 Foundation 0x64b620 message_handler_error + 360 7 libxpc.dylib 0x1179c _xpc_connection_call_event_handler + 152 8 libxpc.dylib 0x11be8 _xpc_connection_mach_event + 1020 9 libdispatch.dylib 0x4048 _dispatch_client_callout4 + 20 10 libdispatch.dylib 0x24104 _dispatch_mach_cancel_invoke + 128 11 libdispatch.dylib 0x21720 _dispatch_mach_invoke + 916 12 libdispatch.dylib 0xb518 _dispatch_lane_serial_drain + 376 13 libdispatch.dylib 0xc1c0 _dispatch_lane_invoke + 436 14 libdispatch.dylib 0x16e10 _dispatch_workloop_worker_thread + 652 15 libsystem_pthread.dylib 0xdf8 _pthread_wqthread + 288 16 libsystem_pthread.dylib 0xb98 start_wqthread + 8
Posted Last updated
.
Post not yet marked as solved
0 Replies
6.3k Views
Transport Layer Security (TLS) is the most important security protocol on the Internet today. Most notably, TLS puts the S into HTTPS, adding security to the otherwise insecure HTTP protocol. IMPORTANT TLS is the successor to the Secure Sockets Layer (SSL) protocol. SSL is no longer considered secure and it’s now rarely used in practice, although many folks still say SSL when they mean TLS. TLS is a complex protocol. Much of that complexity is hidden from app developers but there are places where it’s important to understand specific details of the protocol in order to meet your requirements. This post explains the fundamentals of TLS, concentrating on the issues that most often confuse app developers. Note If you’re working on TLS in the local environment, for example, to talk to a Wi-Fi based accessory, see TLS For Accessory Developers. Server Certificates For standard TLS to work the server must have a digital identity, that is, the combination of a certificate and the private key matching the public key embedded in that certificate. TLS Crypto Magic™ ensures that: The client gets a copy of the server’s certificate. The client knows that the server holds the private key matching the public key in that certificate. In a typical TLS handshake the server passes the client a list of certificates, where item 0 is the server’s certificate (the leaf certificate), item N is (optionally) the certificate of the certificate authority that ultimately issued that certificate (the root certificate), and items 1 through N-1 are any intermediate certificates required to build a cryptographic chain of trust from 0 to N. Note The cryptographic chain of trust is established by means of digital signatures. Certificate X in the chain is issued by certificate X+1. The owner of certificate X+1 uses their private key to digitally sign certificate X. The client verifies this signature using the public key embedded in certificate X+1. Eventually this chain terminates in a trusted anchor, that is, a certificate that the client trusts by default. Typically this anchor is a self-signed root certificate from a certificate authority. Note Item N is optional for reasons I’ll explain below. Also, the list of intermediate certificates may be empty (in the case where the root certificate directly issued the leaf certificate) but that’s uncommon for servers in the real world. Once the client gets the server’s certificate, it evaluates trust on that certificate to confirm that it’s talking to the right server. There are three levels of trust evaluation here: Basic X.509 trust evaluation checks that there’s a cryptographic chain of trust from the leaf through the intermediates to a trusted root certificate. The client has a set of trusted root certificates built in (these are from well-known certificate authorities, or CAs), and a site admin can add more via a configuration profile. This step also checks that none of the certificates have expired, and various other more technical criteria (like the Basic Constraints extension). Note This explains why the server does not have to include the root certificate in the list of certificates it passes to the client; the client has to have the root certificate installed if trust evaluation is to succeed. In addition, TLS trust evaluation (per RFC 2818) checks that the DNS name that you connected to matches the DNS name in the certificate. Specifically, the DNS name must be listed in the Subject Alternative Name extension. Note The Subject Alternative Name extension can also contain IP addresses, although that’s a much less well-trodden path. Also, historically it was common to accept DNS names in the Common Name element of the Subject but that is no longer the case on Apple platforms. App Transport Security (ATS) adds its own security checks. Basic X.509 and TLS trust evaluation are done for all TLS connections. ATS is only done on TLS connections made by URLSession and things layered on top URLSession (like WKWebView). In many situations you can override trust evaluation; for details, see Technote 2232 HTTPS Server Trust Evaluation). Such overrides can either tighten or loosen security. For example: You might tighten security by checking that the server certificate was issued by a specific CA. That way, if someone manages to convince a poorly-managed CA to issue them a certificate for your server, you can detect that and fail. You might loosen security by adding your own CA’s root certificate as a trusted anchor. IMPORTANT If you rely on loosened security you have to disable ATS. If you leave ATS enabled, it requires that the default server trust evaluation succeeds regardless of any customisations you do. Mutual TLS The previous section discusses server trust evaluation, which is required for all standard TLS connections. That process describes how the client decides whether to trust the server. Mutual TLS (mTLS) is the opposite of that, that is, it’s the process by which the server decides whether to trust the client. Note mTLS is commonly called client certificate authentication. I avoid that term because of the ongoing confusion between certificates and digital identities. While it’s true that, in mTLS, the server authenticates the client certificate, to set this up on the client you need a digital identity, not a certificate. mTLS authentication is optional. The server must request a certificate from the client and the client may choose to supply one or not (although if the server requests a certificate and the client doesn’t supply one it’s likely that the server will then fail the connection). At the TLS protocol level this works much like it does with the server certificate. For the client to provide this certificate it must apply a digital identity, known as the client identity, to the connection. TLS Crypto Magic™ assures the server that, if it gets a certificate from the client, the client holds the private key associated with that certificate. Where things diverge is in trust evaluation. Trust evaluation of the client certificate is done on the server, and the server uses its own rules to decided whether to trust a specific client certificate. For example: Some servers do basic X.509 trust evaluation and then check that the chain of trust leads to one specific root certificate; that is, a client is trusted if it holds a digital identity whose certificate was issued by a specific CA. Some servers just check the certificate against a list of known trusted client certificates. When the client sends its certificate to the server it actually sends a list of certificates, much as I’ve described above for the server’s certificates. In many cases the client only needs to send item 0, that is, its leaf certificate. That’s because: The server already has the intermediate certificates required to build a chain of trust from that leaf to its root. There’s no point sending the root, as I discussed above in the context of server trust evaluation. However, there are no hard and fast rules here; the server does its client trust evaluation using its own internal logic, and it’s possible that this logic might require the client to present intermediates, or indeed present the root certificate even though it’s typically redundant. If you have problems with this, you’ll have to ask the folks running the server to explain its requirements. Note If you need to send additional certificates to the server, pass them to the certificates parameter of the method you use to create your URLCredential (typically init(identity:certificates:persistence:)). One thing that bears repeating is that trust evaluation of the client certificate is done on the server, not the client. The client doesn’t care whether the client certificate is trusted or not. Rather, it simply passes that certificate the server and it’s up to the server to make that decision. When a server requests a certificate from the client, it may supply a list of acceptable certificate authorities [1]. Safari uses this to filter the list of client identities it presents to the user. If you are building an HTTPS server and find that Safari doesn’t show the expected client identity, make sure you have this configured correctly. If you’re building an iOS app and want to implement a filter like Safari’s, get this list using: The distinguishedNames property, if you’re using URLSession The sec_protocol_metadata_access_distinguished_names routine, if you’re using Network framework [1] See the certificate_authorities field in Section 7.4.4 of RFC 5246, and equivalent features in other TLS versions. Self-Signed Certificates Self-signed certificates are an ongoing source of problems with TLS. There’s only one unequivocally correct place to use a self-signed certificate: the trusted anchor provided by a certificate authority. One place where a self-signed certificate might make sense is in a local environment, that is, securing a connection between peers without any centralised infrastructure. However, depending on the specific circumstances there may be a better option. TLS For Accessory Developers discusses this topic in detail. Finally, it’s common for folks to use self-signed certificates for testing. I’m not a fan of that approach. Rather, I recommend the approach described in QA1948 HTTPS and Test Servers. For advice on how to set that up using just your Mac, see TN2326 Creating Certificates for TLS Testing. TLS Standards RFC 6101 The Secure Sockets Layer (SSL) Protocol Version 3.0 (historic) RFC 2246 The TLS Protocol Version 1.0 RFC 4346 The Transport Layer Security (TLS) Protocol Version 1.1 RFC 5246 The Transport Layer Security (TLS) Protocol Version 1.2 RFC 8446 The Transport Layer Security (TLS) Protocol Version 1.3 RFC 4347 Datagram Transport Layer Security RFC 6347 Datagram Transport Layer Security Version 1.2 RFC 9147 The Datagram Transport Layer Security (DTLS) Protocol Version 1.3 Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Revision History: 2024-03-19 Adopted the term mutual TLS in preference to client certificate authentication throughout, because the latter feeds into the ongoing certificate versus digital identity confusion. Defined the term client identity. Added the Self-Signed Certificates section. Made other minor editorial changes. 2023-02-28 Added an explanation mTLS acceptable certificate authorities. 2022-12-02 Added links to the DTLS RFCs. 2022-08-24 Added links to the TLS RFCs. Made other minor editorial changes. 2022-06-03 Added a link to TLS For Accessory Developers. 2021-02-26 Fixed the formatting. Clarified that ATS only applies to URLSession. Minor editorial changes. 2020-04-17 Updated the discussion of Subject Alternative Name to account for changes in the 2019 OS releases. Minor editorial updates. 2018-10-29 Minor editorial updates. 2016-11-11 First posted.
Posted
by eskimo.
Last updated
.
Post not yet marked as solved
2 Replies
299 Views
Ever since I've updated to Xcode 15.2 (I'm currently using Xcode 15.3) my console gets flooded with logs from CFNetwork and DataDetectorsUI. I've never seen those logs before. Is there a way to suppress them? I can't even find my own logs anymore without filters. CFNetwork Task <50C80E0E-9DA5-428F-A473-9D0228088022>.<4> resuming, timeouts(25.0, 604800.0) QOS(0x15) Voucher (null) [Telemetry]: Activity <nw_activity 12:2[78557FD1-54F3-4B77-8C5C-57F500D67286] (reporting strategy default)> on Task <50C80E0E-9DA5-428F-A473-9D0228088022>.<4> was not selected for reporting Connection 29: set is idle false Task <50C80E0E-9DA5-428F-A473-9D0228088022>.<4> now using Connection 29 Task <50C80E0E-9DA5-428F-A473-9D0228088022>.<4> sent request, body S 535 Task <50C80E0E-9DA5-428F-A473-9D0228088022>.<4> received response, status 201 content U Task <50C80E0E-9DA5-428F-A473-9D0228088022>.<4> done using Connection 29 Connection 29: set is idle true HTTP/2 Connection 29 Stream 7 ended successfully true Task <50C80E0E-9DA5-428F-A473-9D0228088022>.<4> request *** is NOT allowed to set HSTS for main doc (null) Task <50C80E0E-9DA5-428F-A473-9D0228088022>.<4> response ended Task <50C80E0E-9DA5-428F-A473-9D0228088022>.<4> finished successfully Connection 19: cleaning up Connection 19: done Applying proxy auth: response=(null), proxyURL=(null), request=(null), credentials=(null), handle=0x11c2f7d30 Connection 21: cleaning up Connection 23: cleaning up Connection 22: cleaning up DataDetectorsUI operation 0x10ff7e560 operation 0x10ff7e560 is discarded Calling the completion block for 0x10ff7e560 dispatchScanQueryCreationWithCompletionBlock of operation <DDTextKitOperation: 0x10ff7e560> completion block: success: 0 operation 0x10ff7e560 operation 0x10ff7e560 operation 0x10fb2f850 really creating scan query in operation 0x10fb2f850! operation 0x10fb2f850
Posted Last updated
.
Post not yet marked as solved
1 Replies
814 Views
I’m working on an independent watchOS app which is primarily designed to to collect and periodically send location updates to a server. The UI features a toggle that allows the user to turn this capability on or off at their discretion. The typical use case scenario would be for the user to turn the toggle on in the morning, put the app in the background and then go about their day. Given the limitations and restrictions regarding background execution on watchOS, in an ideal situation, I would be able to upload the stored location updates about every 15-20 minutes. With an active complication on the watch face, it’s my understanding that this should be possible. I’ve implemented background app refresh and indeed, I do see this reliably being triggered every 15-20 minutes or so. In my handle(_:) method, I process the WKApplicationRefreshBackgroundTask like this: func handle(_ backgroundTasks: Set&lt;WKRefreshBackgroundTask&gt;) { backgroundTasks.forEach { task in switch task { case let appRefreshBackgroundTask as WKApplicationRefreshBackgroundTask: // start background URL session to upload data; watchOS will perform the request in a separate process so that it will continue to run even if our app gets // terminated; when the system is done transferring data, it will call this method again and backgroundTasks will contain an instance of // WKURLSessionRefreshBackgroundTask which will be processed below startBackgroundURLSessionUploadTask() scheduleNextBackgroundAppRefresh() appRefreshBackgroundTask.setTaskCompletedWithSnapshot(false) case let urlSessionTask as WKURLSessionRefreshBackgroundTask: // add urlSessionTask to the pendingURLSessionRefreshBackgroundTasks array so we keep a reference to it; when the system completes the upload and // informs us via a URL session delegate method callback, then we will retrieve urlSessionTask from the pendingURLSessionRefreshBackgroundTasks array // and call .setTaskCompletedWithSnapshot(_:) on it pendingURLSessionRefreshBackgroundTasks.append(urlSessionTask) // create another background URL session using the background task’s sessionIdentifier and specify our extension as the session’s delegate; using the same // identifier to create a second URL session allows the system to connect the session to the upload that it performed for us in another process let configuration = URLSessionConfiguration.background(withIdentifier: urlSessionTask.sessionIdentifier) let _ = URLSession(configuration: configuration, delegate: self, delegateQueue: nil) default: task.setTaskCompletedWithSnapshot(false) } } } And here is how I'm creating and starting the background URL session upload task: func startBackgroundURLSessionUploadTask() { // 1. check to see that we have locations to report; otherwise, just return // 2. serialize the locations into a temporary file // 3. create the background upload task let configuration = URLSessionConfiguration.background(withIdentifier: Constants.backgroundUploadIdentifier) configuration.isDiscretionary = false configuration.sessionSendsLaunchEvents = true let backgroundUrlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: nil) let request: URLRequest = createURLRequest() // this is a POST request let backgroundUrlSessionUploadTask = backgroundUrlSession.uploadTask(with: request, fromFile: tempFileUrl) backgroundUrlSessionUploadTask.countOfBytesClientExpectsToSend = Int64(serializedData.count) // on average, this is ~1.5 KB backgroundUrlSessionUploadTask.countOfBytesClientExpectsToReceive = Int64(50) // approximate size of server response backgroundUrlSessionUploadTask.resume() } Note that I'm not setting the .earliestBeginDate property on the backgroundUrlSessionUploadTask because I'd like the upload to start as soon as possible without any delay. Also, this same class (my WatchKit application delegate) conforms to URLSessionTaskDelegate and I have implemented urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:) and urlSession(_:task:didCompleteWithError:). In my testing (on an actual Apple Watch Ultra running watchOS 9.3.1), I've observed that when the system performs the background app refresh, I always receive a callback to myhandle(_:) method. But when I start the background URL session upload task (in startBackgroundURLSessionUploadTask()), I was expecting that when the upload completes, I'd receive another call to myhandle(_:) method with an instance of WKURLSessionRefreshBackgroundTask but this doesn't seem to happen consistently. Sometimes I do see it but other times, I don't and when I don't, the data doesn't seem to be getting uploaded. On a side note, most of the time, startBackgroundURLSessionUploadTask() gets called as a result of my code handling a background app refresh task. But when the user turns off the toggle in the UI and I stop the location updates, I need to report any stored locations at that time and so I call startBackgroundURLSessionUploadTask() to do that. In that specific case, the upload seems to work 100% of the time but I definitely don't see a callback to my handle(_:) method when this occurs. Am I wrong in expecting that I should always be getting a callback to handle(_:) when a background URL session upload task completes? If so, under what circumstances should this occur? Thanks very much!
Posted
by bmt22033.
Last updated
.
Post marked as solved
2 Replies
481 Views
Are there any drawbacks (e.g. slower request) by setting assumesHTTP3Capable to true if the server doesn't support it? Is it OK to set it globally in the app for all URLRequests or it should be set depending on to which server the app makes the request? Thanks!
Posted
by bastawa.
Last updated
.
Post not yet marked as solved
1 Replies
514 Views
I'm using Firebase Auth and Firestore on my app. When I already signed in, my code in init() function workes fine and my screens automatically updated when data load. But when I first sign in, I get The operation couldn’t be completed. (NSURLErrorDomain error -1011.) error and the rest of the code dont work as it should be. What is the issue and how can I solve it?
Posted
by sloutales.
Last updated
.
Post not yet marked as solved
1 Replies
345 Views
I'm unable to set the "secure" property of HTTPCookieProperty (for initializing HTTPCookie) to false. tried: .secure: "FALSE" .secure: "false" .secure: false but all of above resulted in "cookie.isSecure" being true. The only thing that worked was not providing the field at all (which made the property default to false). Seems like a niche bug?
Posted
by jcp_jcp.
Last updated
.
Post not yet marked as solved
3 Replies
1.3k Views
I have an app which uses URLSession-based networking and URLCache for storing network requests on disk. I noticed that when the storage size of URLCache reaches the diskCapacity, the eviction strategy seems to be to remove all entries, which is a problem in my use case. So I decided to write an URLCache subclass which would provide a custom storage for cached responses and which would implement LRU eviction strategy with better control. As URLCache's documentation states, subclassing for this purpose should be supported: The URLCache class is meant to be used as-is, but you can subclass it when you have specific needs. For example, you might want to screen which responses are cached, or reimplement the storage mechanism for security or other reasons. However, I ran into problems with trying to use this new URLCache subclass with URLSession networking. I have a test resource which I fetch using HTTP GET. The response headers contain: Cache-Control: public, max-age=30 Etag: &lt;some-value&gt; When using the standard, non-subclassed URLCache, the first request loads the data from network as expected (verified with HTTP proxy). The second request doesn't go to the network, if done within first 30 seconds, as expected. Subsequent requests after 30 seconds cause conditional GETs with Etag, as expected. When using a URLCache subclass, all requests load the data from network - max-age doesn't seem to matter, and no conditional GETs are made. It seems that the URLCache does something special to the CachedURLResponse instances after they're loaded from its internal storage, and this something affects how URLSession handles the HTTP caching logic. What am I missing here? I've written a very minimal URLCache implementation to demonstrate this problem. This class stores and loads CachedURLResponse instances using NSKeyedArchiver / NSKeyedUnarchiver, and it supports only zero or one response. Note that there are no calls to super - this is by design, since I want to use my own storage. Here's the implementation: class CustomURLCache: URLCache { let cachedResponseFileURL = URL(filePath: NSTemporaryDirectory().appending("entry.data")) // MARK: Internal storage func read() -&gt; CachedURLResponse? { guard let data = try? Data(contentsOf: cachedResponseFileURL) else { return nil } return try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! CachedURLResponse } func store(_ cachedResponse: CachedURLResponse) { try! (try! NSKeyedArchiver.archivedData(withRootObject: cachedResponse, requiringSecureCoding: false)).write(to: cachedResponseFileURL) } // MARK: URLCache Overrides override func cachedResponse(for request: URLRequest) -&gt; CachedURLResponse? { read() } override func getCachedResponse(for dataTask: URLSessionDataTask, completionHandler: @escaping (CachedURLResponse?) -&gt; Void) { guard let response = read() else { completionHandler(nil) return } completionHandler(response) } override func storeCachedResponse(_ cachedResponse: CachedURLResponse, for request: URLRequest) { store(cachedResponse) } override func storeCachedResponse(_ cachedResponse: CachedURLResponse, for dataTask: URLSessionDataTask) { store(cachedResponse) } } My test case: func test() { let useEvictingCache = false let config = URLSessionConfiguration.default if useEvictingCache { config.urlCache = CustomURLCache() } else { config.urlCache = URLCache(memoryCapacity: 0, diskCapacity: 1024 * 1024 * 100) } self.urlSession = URLSession(configuration: config) let url = URL(string: "https://example.com/my-test-resource")! self.urlSession?.dataTask(with: URLRequest(url: url), completionHandler: { data, response, error in if let data { print("GOT DATA with \(data.count) bytes") } else if let error { print("GOT ERROR \(error)") } }).resume() }
Posted
by nzhuk.
Last updated
.
Post not yet marked as solved
1 Replies
312 Views
I am currently implementing an upload flow that utilizes a URLSession with a background configuration to allow the upload to continue running when the application is suspended or terminated by the system. When the upload has completed, and the app is launched/woken up in the backend to respond to the upload task result, I need to make an additional data request to inform the backend that the upload has completed to trigger additional work. I am attempting to do this by making the data request and waiting for it to finish before calling the background events completion handler delivered to the AppDelegate. However, the data request never completes while in the background, but will receive a result when the app is brought to the foreground. Often the result of this data request will be a failure: Error Domain=NSURLErrorDomain Code=-997 "Lost connection to background transfer service" or Error Domain=NSURLErrorDomain Code=-999 "canceled" I understand that a URLSession with a background configuration will reject data tasks when the app is suspended or terminated. However, I am attempting to use a non-background configured network session for the data request while the application is running in the background, before the application is suspended again. Is it not possible to make additional data requests when the app is launched/woken up in the background after a background upload is completed?
Posted Last updated
.
Post not yet marked as solved
6 Replies
905 Views
To download files, we have two NSURLSession objects. One configured for foreground downloads and one for background downloads. Initially, we download user-requested files in the foreground using downloadTaskWithRequest, because foreground downloads are faster than background downloads. We then also start a background task for each download using beginBackgroundTaskWithName:expirationHandler:. This background task's expiration handler starts a background download if the download didn't complete in the foreground. It cancels the foreground download with resume data using cancelByProducingResumeData. A background download task is then started using downloadTaskWithResumeData. Now, testing has shown that background download tasks resume correctly (the urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:) callback is called by iOS) if the background task is started immediately. However, iOS can decide to start these background download tasks later. As a sidenote, the isDiscretionary property of our background download session is set to the default, which should be false. The issue we have is that background downloads that are resumed several minutes (6,5 minutes in the session I'm currently looking at) after their original foreground download was cancelled, are not resumed properly. They download the remaining part of the file, but the first part that was downloaded in the foreground is lost. What could have happened? Perhaps the temporary file has been deleted in the meantime? Is there some way to maintain this file or detect this case?
Posted
by p_b_omta.
Last updated
.
Post not yet marked as solved
1 Replies
472 Views
Hello! I'm working on VLC, that is a multimedia playback app available for any platform. Among many things, we support discovery of servers on the local network using Bonjour, UPnP and NETBIOS with consecutive connections to those servers for media playback purposes. Additionally, we allow connections to unicast and multicast streams based on any domain name or IP (i.e. "rtsp://207.254.***.***"). Discovery of the mentioned services works very well with the Multicast entitlement along with NSLocalNetworkUsageDescription also on iOS 17. According to documentation, iOS 17 prohibits any IP based connections by default, which breaks the entire functionality mentioned above that was previously enabled by including the NSAllowsArbitraryLoads key with the value TRUE in Info.plist. We amended the Info.plist with the following configuration and still fail to connect to hosts in that IP range. <key>NSAllowsLocalNetworking</key> <true/> <key>NSExceptionDomains</key> <dict> <key>192.168.0.0/24</key> <dict> <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSIncludesSubdomains</key> <true/> <key>NSExceptionRequiresForwardSecrecy</key> <false/> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSExceptionAllowsLocalNetworking</key> <true/> </dict> </dict> Additionally, there does not seem to be a viable, publicly documented solution to connect to any server on the internet based on an IP address. Further, the process for IPv6 seems unclear. Any help how to solve this so we can transition to the iOS/tvOS 17 SDK in time would be very much appreciated.
Posted Last updated
.
Post marked as solved
23 Replies
2.7k Views
We're seeing server trust failures with iOS 17 that we don't see with iOS 16, particularly in debugging, when we build with Xcode 15. We handle func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -&gt; Void) and choose to deal with recoverable server trust failures of particular kinds ourselves. After our checks we end up calling the completion handler: let credential = URLCredential(trust: serverTrust) completionHandler(.useCredential, credential) And everything continues. This is working on macOS Venture and earlier and iOS 16 and earlier. It also works if we install our current release build on iOS 17. If we build with Xcode 15 and test on iOS 17 then calling the completion handler with .useCredential ends up failing with a -1200 error, and a message about a recoverable trust failure. Has anyone else seen this behavior? Does anyone know if this is related to just Xcode 15, or to Xcode 15 + an interaction with iOS 17? Maybe the SDKs used with Xcode 15 are being stricter? In any case it would seem that saying .useCredential should cause it to .use the credential...
Posted
by kbrock.
Last updated
.
Post not yet marked as solved
9 Replies
915 Views
One of our client has contacted us with the following error : Task .&amp;lt;3&amp;gt; request https:URL is NOT allowed to set HSTS for main doc (null) Th request is sent from our SDK. According to the client it happens only on Vision Pro. All our requests to the server on the SDK side are https. The serve has the following header: X-Content-Type-Options X-Frame-Options Strict-Transport-Security Can somebody share some insight?
Posted Last updated
.
Post not yet marked as solved
0 Replies
2k Views
General: TN3151 Choosing the right networking API Networking Overview document — Despite the fact that this is in the archive, this is still really useful. TLS for App Developers DevForums post Choosing a Network Debugging Tool documentation WWDC 2019 Session 712 Advances in Networking, Part 1 — This explains the concept of constrained networking, which is Apple’s preferred solution to questions like How do I check whether I’m on Wi-Fi? TN3135 Low-level networking on watchOS Adapt to changing network conditions tech talk Foundation networking: DevForums tags: Foundation, CFNetwork URL Loading System documentation — NSURLSession, or URLSession in Swift, is the recommended API for HTTP[S] on Apple platforms. Network framework: DevForums tag: Network Network framework documentation — Network framework is the recommended API for TCP, UDP, and QUIC on Apple platforms. Network Extension (including Wi-Fi on iOS): See Network Extension Resources Wi-Fi Fundamentals Wi-Fi on macOS: DevForums tag: Core WLAN Core WLAN framework documentation Wi-Fi Fundamentals Secure networking: DevForums tags: Security Apple Platform Security support document Preventing Insecure Network Connections documentation — This is all about App Transport Security (ATS). Available trusted root certificates for Apple operating systems support article Requirements for trusted certificates in iOS 13 and macOS 10.15 support article About upcoming limits on trusted certificates support article Apple’s Certificate Transparency policy support article Technote 2232 HTTPS Server Trust Evaluation Technote 2326 Creating Certificates for TLS Testing QA1948 HTTPS and Test Servers Miscellaneous: More network-related DevForums tags: 5G, QUIC, Bonjour On FTP DevForums post Using the Multicast Networking Additional Capability DevForums post Investigating Network Latency Problems DevForums post Local Network Privacy FAQ DevForums post Extra-ordinary Networking DevForums post Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com"
Posted
by eskimo.
Last updated
.
Post not yet marked as solved
4 Replies
412 Views
I'm having an issue with showing progress in a upload task. I have created a local express app to test it's behavior. But progress / delegate always throughs the same results. I'm also conforming to URLSessionTaskDelegate. This is the way I configure URLSession it's always .default private lazy var session: URLSession = { .init( configuration: self.configuration, delegate: self, delegateQueue: OperationQueue.main ) }() private var observation: NSKeyValueObservation? Then I call it using this func and convert it into a publisher func loadData1(path: String, method: RequestMethod, params: [String: String]) -&gt; AnyPublisher&lt;Data, Error&gt; { let publisher = PassthroughSubject&lt;Data, Error&gt;() guard let imageUrl = Bundle.main.url(forResource: "G0056773", withExtension: "JPG"), let imageData = try? Data(contentsOf: imageUrl) else { return Fail(error: NSError(domain: "com.test.main", code: 1000)).eraseToAnyPublisher() } var request = URLRequest(url: URL(string: "http://localhost:3000/")!) request.httpMethod = "POST" let data = request.createFileUploadBody(parameters: [:], boundary: UUID().uuidString, data: imageData, mimeType: "image/jpeg", fileName: "video") let task = session.uploadTask(with: request, from: data) { data, response, error in if let error = error { return publisher.send(completion: .failure(error)) } guard let response = response as? HTTPURLResponse else { return publisher.send(completion: .failure(ServiceError.other)) } guard 200..&lt;300 ~= response.statusCode else { return publisher.send(completion: .failure(ServiceError.other)) } guard let data = data else { return publisher.send(completion: .failure(ServiceError.custom("No data"))) } return publisher.send(data) } observation = task.progress.observe(\.fractionCompleted) { progress, _ in print("progress: ", progress.totalUnitCount) print("progress: ", progress.completedUnitCount) print("progress: ", progress.fractionCompleted) print("***********************************************") } task.resume() return publisher.eraseToAnyPublisher() } request.createFileUploadBody is just an extension of URLRequest that I have created to create the form data's body. Delegate func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { print(bytesSent) print(totalBytesSent) print(totalBytesExpectedToSend) print("*********************************************") } No matter how big or small is the file that I'm uploading I always get the same results. The delegate is only called once. And the progress is also always the same. progress: 100 progress: 0 progress: 0.0095 *********************************************** progress: 100 progress: 0 progress: 4.18053577746524e-07 *********************************************** 2272436 2272436 2272436 ********************************************* progress: 100 progress: 95 progress: 0.95 *********************************************** progress: 100 progress: 100 progress: 1.0 *********************************************** ^^^^^^ Output from prints Does anyone knows what might I be doing wrong? Thanks in advance
Posted
by MostaZa.
Last updated
.
Post not yet marked as solved
0 Replies
179 Views
I regularly see folks confused as to which URLSession task types are supported in which session types. Here’s a handy reference: Task Type Standard Background --------- -------- ---------- data, convenience yes no data, delegate yes yes [1] download, convenience yes no download, delegate yes yes upload, convenience yes no upload, delegate, data yes no upload, delegate, stream yes no upload, delegate, file yes yes stream yes no WebSocket yes no In this table: A background session is one created from a configuration that was created with the background(withIdentifier:) method. A standard session is one created from some other configuration, namely default or ephemeral. A convenience task is one created by a Swift async method or a method that takes a completion handler. A delegate task is one created otherwise; these signal their completion by calling the urlSession(_:task:didCompleteWithError:) delegate method. For an upload task, the data, stream, and file modifiers indicate how you specify the HTTP request body. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] While it’s possible to run a data task in a background session, this only works if your app avoids suspension while the request is in flight. This is useful in very rare circumstances [2] that most developers never encounter. [2] The specific situation I’m aware of is when you don’t know whether a request’s response will be big (like a file to download) or small (like a status response). To handle this, you start a data task in the background session and, if the response indicates that it’s large, transform it to a download task in your urlSession(_:dataTask:didReceive:completionHandler:) method. (r. 123084713)
Posted
by eskimo.
Last updated
.
Post not yet marked as solved
0 Replies
278 Views
I am using Relay to connect to nginx server which supports http3 connections for which I get quic_stream_send_create [C1.1.1.1:5] [24e7c30b6b4eb7ba-0000000000001002e2c0e6cb5099cd86ef1f0f7c] unable to create DATAGRAM flow since the peer did not provide max_datagram_frame_size in transport parameters [C1.1.1.1:3] Unable to send 1200 early bytes quic_stream_send_create [C1.1.1.1:5] [24e7c30b6b4eb7ba-0000000000001002e2c0e6cb5099cd86ef1f0f7c] unable to create DATAGRAM flow since the peer did not provide max_datagram_frame_size in transport parameters This is something very simple I have written : ` let relayURL = URL(string: "https://192.168.64.2:8443")! let relayEndpoint = NWEndpoint.url(relayURL) if #available(iOS 17.0, *) { var tlsOptions = NWProtocolTLS.Options() let relayServer = ProxyConfiguration.RelayHop(http3RelayEndpoint: relayEndpoint, tlsOptions: NWProtocolTLS.Options = .init()) let relayConfig = ProxyConfiguration(relayHops: [relayServer]) let config = URLSessionConfiguration.default` config.connectionProxyDictionary = [kCFNetworkProxiesHTTPEnable as AnyHashable: true, kCFNetworkProxiesHTTPProxy as AnyHashable: "192.168.64.2", kCFNetworkProxiesHTTPPort as AnyHashable: 8443, kCFStreamPropertySSLSettings as AnyHashable: sslSettings] config.proxyConfigurations = [relayConfig] // Call addCert to add the certificate to the trust store //addCert(cert: "cert") // Example usage of the custom session let session = makeInsecureURLSession(config: config) let url = URL(string: "https://google.com")! // Example URL let task = session.dataTask(with: url) { data, response, error in // Handle response print("response is \(response)") if let httpResponse = response as? HTTPURLResponse { let protocolUsed = httpResponse.url?.scheme ?? "Unknown" print("Protocol used: \(protocolUsed)") } else { print("Response is not an HTTPURLResponse") } } task.resume()
Posted
by Ankita11.
Last updated
.
Post not yet marked as solved
0 Replies
250 Views
I recently handled a couple of questions about this for DTS, so I thought I’d write it up for the benefit of all. If you have any questions or comments, please put them in a new thread here on DevForums. Tag it with Foundation and CFNetwork so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" HTTP authentication challenges with Swift concurrency URLSession has always made it easy to access HTTP resources, but things get even better when you combine that with Swift concurrency. For example: import Foundation func main() async throws { // Set up the request. let url = URL(string: "https://example.com")! let request = URLRequest(url: url) // Run it; this will throw if there’s a transport error. let (body, response) = try await URLSession.shared.data(for: request) // Check for a server-side error. guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { … handle the error … } // Success. … HTTP response headers are in `response.allHeaderFields` … … HTTP response body is in `body` … } try await main() But what happens if you need to handle an HTTP authentication challenge? The current documentation for authentication challenges is very good, but it assumes that you have a session delegate. That doesn’t really mesh well with Swift concurrency. Fortunately there’s an easier way: Pass in a per-task delegate. Consider this slightly updated snippet: let url = URL(string: "https://postman-echo.com/basic-auth")! let request = URLRequest(url: url) let (body, response) = try await URLSession.shared.data(for: request) This fetches a resource, https://postman-echo.com/basic-auth, that requires HTTP Basic authentication. This request fails with a 401 status code because nothing handles that authentication. To fix that, replace the last line with this: let delegate = BasicAuthDelegate(user: "postman", password: "password") let (body, response) = try await URLSession.shared.data(for: request, delegate: delegate) This creates an instance of the BasicAuthDelegate, passing in the user name and password required to handle the challenge. That type implements the URLSessionTaskDelegate protocol like so: final class BasicAuthDelegate: NSObject, URLSessionTaskDelegate { init(user: String, password: String) { self.user = user self.password = password } let user: String let password: String func urlSession( _ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge ) async -> (URLSession.AuthChallengeDisposition, URLCredential?) { // We only care about Basic authentication challenges. guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic else { return (.performDefaultHandling, nil) } // Limit the authentication attempts. guard challenge.previousFailureCount < 2 else { return (.cancelAuthenticationChallenge, nil) } // If all is well, return a credential. let credential = URLCredential(user: user, password: password, persistence: .forSession) return (.useCredential, credential) } } This delegate handles the NSURLAuthenticationMethodHTTPBasic authentication challenge, allowing the overall request to succeed. Now you have the best of both worlds: An easy to use HTTP API With the flexibility to handle authentication challenges This approach isn’t right for all programs, but if you’re just coming up to speed on URLSession it’s a great place to start.
Posted
by eskimo.
Last updated
.
Post not yet marked as solved
0 Replies
351 Views
Hi, I am implementing the HTTP3 Relay server, and testing by the URLSession, let relayEndpoint = NWEndpoint.url(URL(string:"https://localhost:8002")!) let relayServer = ProxyConfiguration.RelayHop( http3RelayEndpoint: relayEndpoint, additionalHTTPHeaderFields: ["Proxy-Authorization":"Basic ***"] ) let relayConfig = ProxyConfiguration(relayHops: [relayServer]) let config = URLSessionConfiguration.default config.proxyConfigurations = [relayConfig] let s = URLSession(configuration: config) I have implemented both the CONNECT method and CONNECT method with :protocol of CONNECT-UDP over HTTP/3. If I enabled CONNECT-UDP only, when I try to connect some HTTP/3 URL like https://quic.aiortc.org/4, it works without problem. And URLSession will use the CONNECT-UDP to connect the UDP channel, throw which the URLSession will do the HTTP/3 request. However, if i enabled both the CONNECT-UDP and CONNECT (for TCP tunnel), the URLSession seems wired. It first try to establish the tunnel for UDP using the CONNECT-UDP method, and then closed it. And finally, it created the TCP tunnel using the CONNECT method. So is there any detailed document for the behavior of the behavior of URLSession with http3RelayEndpoint? Thanks you.
Posted
by lvht.
Last updated
.
Post not yet marked as solved
2 Replies
368 Views
Code snippet below: NSString *host = @"time.google.com"; CFHostRef cfHostRef = CFHostCreateWithName(nil, (__bridge CFStringRef) host); Boolean didLookup = CFHostStartInfoResolution(cfHostRef, kCFHostAddresses, nil); NSLog(@"didLookup=%d", didLookup); CFArrayRef addresses = CFHostGetAddressing(cfHostRef, &didLookup); struct sockaddr *remoteAddr; long count = CFArrayGetCount(addresses); NSLog(@"count=%ld (this should include both ip4 and ip6)", count); for(int i = 0; i < count; i++) { CFDataRef saData = (CFDataRef)CFArrayGetValueAtIndex(addresses, i); remoteAddr = (struct sockaddr*)CFDataGetBytePtr(saData); NSLog(@"family=%d (AF_INET=%d, AF_INET6=%d)",remoteAddr->sa_family, AF_INET, AF_INET6); NSString* addrPretty = nil; switch (remoteAddr->sa_family) { case AF_INET: { char dest[INET_ADDRSTRLEN]; struct sockaddr_in *ip4 = (struct sockaddr_in *) remoteAddr; addrPretty = [NSString stringWithFormat: @"%s", inet_ntop(AF_INET, &ip4->sin_addr, dest, INET_ADDRSTRLEN)]; break; } case AF_INET6: { char dest[INET6_ADDRSTRLEN]; struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *) remoteAddr; addrPretty = [NSString stringWithFormat: @"%s", inet_ntop(AF_INET6, &ip6->sin6_addr, dest, INET6_ADDRSTRLEN)]; break; } default: break; } NSLog(@"addrPretty=%@", addrPretty); } As far as I understand this should print out both IPv4 and IPv6 addresses, but it only does the former. This is as tested on both a simulator and a real device, on different networks. Note that I can traceroute6 -I time.google.com and see IPv6 addresses just fine, and I can also do set q=AAAA in the nslookup prompt and get the expected addresses when performing the query for time.google.com in the same prompt.
Posted
by jantonio.
Last updated
.