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"
CFNetwork
RSS for tagAccess network services and handle changes in network configurations using CFNetwork.
Posts under CFNetwork tag
120 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
There are several crash logs
Crashed: com.apple.root.default-qos
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000038
0 CFNetwork 0x1e98 CFURLRequestSetHTTPRequestBody + 36
1 *** 0x104f7d0 -[XXXRequest getURLRequest] + 66 (XXXRequest.m:66)
2 *** 0x1051328 -[XXXRequestManager processHTTPRequest:] + 152 (XXXRequestManager.m:152)
3 *** 0x79748c __47-[XXXLog __submit:]_block_invoke + 277 (XXXLog.m:277)
4 FBLPromises 0x5138 __56-[FBLPromise chainOnQueue:chainedFulfill:chainedReject:]_block_invoke.18 + 52
5 libdispatch.dylib 0x63094 _dispatch_call_block_and_release + 24
6 libdispatch.dylib 0x64094 _dispatch_client_callout + 16
7 libdispatch.dylib 0x6924 _dispatch_queue_override_invoke + 924
8 libdispatch.dylib 0x13b94 _dispatch_root_queue_drain + 340
9 libdispatch.dylib 0x1439c _dispatch_worker_thread2 + 172
10 libsystem_pthread.dylib 0x1dc4 _pthread_wqthread + 224
11 libsystem_pthread.dylib 0x192c start_wqthread + 8
I can't get NSURLSession background tasks to use client certificates.I have a simple app that creates an NSURLSessionDownloadTask from an NSURLSession that uses NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(...).I have a custom delegate that implements:"func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void)". For NSURLAuthenticationMethodServerTrust, I accept any server. For NSURLAuthenticationMethodClientCertificate, I have a hardcoded PKCS12 bundle with a single certificate and key from which I successfully create an NSURLCredential (using SecPKCS12Import), then pass that credential to the completion handler with .UseCredential.At runtime, I get the didReceiveChallenge callback for ClientCertificate, then another for NSURLAuthenticationMethodServerTrust, then URLSessionDidFinishEventsForBackgroundURLSession right away without completing the TLS handshake.If I change the NSURLSessionConfiguration to use NSURLSessionConfiguration.defaultSessionConfiguration(), the client cert is presented correctly and the download proceeds.Both tests are done with the app in the foreground.
If I set the capacity of the disk cache to less than 5MB, It doesn't work.
Through the print statement, I checked that the value of the currentDiskUsage did not rise at all, and I also checked that the image has been making network requests every time because there is no cached data even if I shut down and run the app again. I'm simply wondering why this is happening.
Also, I wonder what kind of eviction policy the disk cache follows.
I was so curious that I tried to find out through the link [here], but there seems to be no implementation of disk cache at all.
Below is the code I used. I'm attaching it together just in case.
import UIKit
protocol Cacheable {
func getCachedResponse(
for path: String,
completion: @escaping (Result<Data, CacheError>) -> Void
)
func save(
for path: String,
data: Data
)
}
final class CacheManager {
static let shared = CacheManager()
private let imageCache: URLCache
init() {
imageCache = URLCache(
memoryCapacity: 4 * 1024 * 1024, // 4MB
diskCapacity: 4 * 1024 * 1024 // 4MB
)
}
}
extension CacheManager: Cacheable {
func getCachedResponse(
for path: String,
completion: @escaping (Result<Data, CacheError>) -> Void
) {
if let url = URL(string: path),
let cachedResponse = imageCache.cachedResponse(for: URLRequest(url: url)) {
completion(.success(cachedResponse.data))
return
}
completion(.failure(.noCachedResponse))
}
func save(
for path: String,
data: Data
) {
guard let url = URL(string: path) else { return }
let response = URLResponse(
url: url,
mimeType: nil,
expectedContentLength: 0,
textEncodingName: nil
)
if let uiImage = UIImage(data: data),
let compressedData = uiImage.jpegData(compressionQuality: 0.8) {
#if DEBUG
let formmatter = ByteCountFormatter()
formmatter.allowedUnits = [.useMB]
formmatter.countStyle = .file
print("""
=== Original size: \(formmatter.string(fromByteCount: Int64(data.count)))
=== Cached size: \(formmatter.string(fromByteCount: Int64(compressedData.count)))
""")
#endif
let cachedResponse = CachedURLResponse(
response: response,
data: compressedData
)
imageCache.storeCachedResponse(
cachedResponse,
for: URLRequest(url: url)
)
}
}
}
I have an app with which users take photos and upload them in batches. It's used often on older devices, in areas with less than ideal network, and for durations of a full workday - so often the device has low power.
The current implementation of uploads uses an NSURLSession configured for the foreground, and as a result my users are used to having to keep the app in the foreground while an upload completes. However, these uploads are big and connectivity is often low, so this takes a long time - often users are stuck waiting with the app foregrounded for 15 minutes or so while the upload completes.
So, I created a build which uses an NSURLSession configured for the background. In the ideal case, users could start the upload, put the device in their pocket and continue their workday, and the next time they open their device it will be complete.
For some users this ideal case has come true. However, for others, the uploads sit in progress for an indeterminate amount of time, making no progress. My suspicion is that this is because the OS is deferring them until a time when network and power is more available. However, my users are using work devices at a work location - reliable power and network might never be available. Being able to background the app and continue working is valuable for these users, but having the upload complete promptly is essential for them.
My questions are:
Is it true that background configured NSURLSessions will defer network requests when connectivity or power is low, even if discretionary = NO? Is the exact behavior for when requests will be attempted in the background documented?
Is there a way to reliably test background configured NSURLSessions in XCode? I've attempted throttling my connection with Charles Proxy, and using my device in Low Power Mode, but I'm unable to reproduce the request stalling behavior my users are experiencing in the wild.
Is there a way to create an NSURLSession that will muscle through difficult or inefficient uploads in the background, with the same reliability as a foreground session?
If not, what is Apple's recommended approach to situations like mine? I've considered queueing both a background and foreground upload, and cancelling the other once one completes, but this seems disrespectful to the user's resources.
Will setting timeoutIntervalForResource to a lower value cause the OS to more aggressively attempt uploads? Or simply to throw an error sooner? I want the OS to give the upload a long time to complete, but I also want it to attempt it right away.
Thanks for any information!
How to use Custom URLSession in MKMapView?
URLSession.shared.downloadTask doesn't work apple watch. I monitored network traffic but it doesn't. have any activity. Data(contentsOf: URL(string: url)!) works perfectly. Also, it works perfectly in preview. What do i do? Ask for more context if necessary.
Hi,
I am facing a strange issue in my app with iOS14 there is a intermittent crash, i am using NetServiceBrowser for MDNS discovery not sure if that is causing the problem crash log has below information:
Crashed: com.apple.main-thread
0 CoreFoundation 0x1a906c4c4 CFAssertMismatchedTypeID + 108
1 CoreFoundation 0x1a8f7db0c CFRunLoopSourceRemoveFromRunLoop + 298
2 CFNetwork 0x1a96255b0 CFNetServiceBrowserStopSearch + 460
3 CoreFoundation 0x1a8f81240 CFRUNLOOPISCALLINGOUTTOASOURCE0PERFORMFUNCTION + 24
4 CoreFoundation 0x1a8f81140 CFRunLoopDoSource0 + 204
5 CoreFoundation 0x1a8f80488 CFRunLoopDoSources0 + 256
6 CoreFoundation 0x1a8f7aa40 CFRunLoopRun + 776
7 CoreFoundation 0x1a8f7a200 CFRunLoopRunSpecific + 572
8 GraphicsServices 0x1bf075598 GSEventRunModal + 160
9 UIKitCore 0x1ab840004 -[UIApplication run] + 1052
10 UIKitCore 0x1ab8455d8 UIApplicationMain + 164
Is the timeout for session-level authentication challenge handling documented somewhere? For example, if I get the urlSession(_:didReceive:) callback for server trust authentication, how long do I have to invoke the completion handler (or return from the callback if using Swift Concurrency)?
Or is this completely dependent on the server's settings?
I have a customer who wants to protect the REST API of their app with a private certificate. They would then distribute the client certificate to the authorized users. Their app would not work unless the client certificate is already installed on the user's phone before they run the app.
I have never done this before. Is it possible to install a client certificate on an iPhone without running an app, for example if it were sent in an email message?
And if it is possible, is App Review going to let such an app into the app store?
Thanks,
Frank
Hi
I Download my app from test flight, when i click submit button to a backend call. App expects to get back with the response from the backend, to take to next pages.
But the app seems to be stuck waiting for the backend response. No error messages seen. i am sure the backend call is blocked from the test flight version. Same code works well from emulator and the physical device from local and from Google PlayStore. Only the test flight is the problem.
I am sure i messed up some settings , My Info.plist has as in below, can anyone please help.
NSAppTransportSecurity
NSPinnedNetworkSecurityItems
MyBundleName
NSIncludesSubdomains
NSAllowsArbitraryLoads
NSPinnedCAIdentities
SPKI-SHA256-BASE64
THEKEY
Hello,
I have some networking code that checks whether a proxy is configured via:
`CFStringRef host = (CFStringRef)CFDictionaryGetValue(globalSettings, kCFNetworkProxiesHTTPProxy);`
`CFNumberRef port = (CFNumberRef)CFDictionaryGetValue(globalSettings, kCFNetworkProxiesHTTPPort);`
I need to do this fairly frequently, so I am wondering if there is an announcer I can subscribe to instead?
Currently I am trying to create some shortcuts for my iOS 17 app. The AppIntent looks like this:
class PostClass{
public init() {
let url = URL(string: "http://myurl")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
struct Message: Encodable {
let device_type: String
}
let message = Message(
device_type: "device"
)
let data = try! JSONEncoder().encode(message)
request.httpBody = data
request.setValue(
"application/json",
forHTTPHeaderField: "Content-Type"
)
self.request = request
}
}
var activateDevice = PostClass()
@available(iOS 17, *)
struct ActivateIntent: AppIntent {
static let title: LocalizedStringResource = "Activate"
func perform() async throws -> some IntentResult {
let task = URLSession.shared.dataTask(with: activateDevice.request) { data, response, error in
let statusCode = (response as! HTTPURLResponse).statusCode
if statusCode == 200 {
print("SUCCESS")
} else {
print("FAILURE")
}
}
task.resume()
return .result()
}
}
Unfortunately, the shortcut throws an error after every second execution. I looked into the ips-file and saw the following traceback:
Thread 2 Crashed:
0 Runner 0x1028ddd30 closure #1 in ActivateIntent.perform() + 388
1 CFNetwork 0x1a6f39248 0x1a6f2a000 + 62024
2 CFNetwork 0x1a6f57210 0x1a6f2a000 + 184848
3 libdispatch.dylib 0x1adced13c _dispatch_call_block_and_release + 32
4 libdispatch.dylib 0x1adceedd4 _dispatch_client_callout + 20
5 libdispatch.dylib 0x1adcf6400 _dispatch_lane_serial_drain + 748
6 libdispatch.dylib 0x1adcf6f64 _dispatch_lane_invoke + 432
7 libdispatch.dylib 0x1add01cb4 _dispatch_root_queue_drain_deferred_wlh + 288
8 libdispatch.dylib 0x1add01528 _dispatch_workloop_worker_thread + 404
9 libsystem_pthread.dylib 0x201dd4f20 _pthread_wqthread + 288
10 libsystem_pthread.dylib 0x201dd4fc0 start_wqthread + 8
Is there any way to locate the error with these information? Has it something to do with the fact that my code is not thread-safe? Or is there any internal bug?
Grateful for any help, thanks in advance!
Hello,
Our app has an internal job processing queue. All jobs are built as a NSOperation and involve a network request, and they are added to NSOperationQueue. When the app is closed while a request is being sent, the app sometimes crashes, but it also keeps crashing whenever we build the operation again and retry it. This happens rarely, but we can systematically reproduce it after a few tries with many jobs.
This issue blocks the queue in our app. I understand if this is an issue deep within the framework, but it would be very useful to at least find a way to work around this issue so the queue can continue processing other jobs.
The full crash report is attached. I also submitted a bug report: FB13734737
There seems to be an internal assertion fired in CFNetwork:
Assertion failed: (CFReadStreamGetStatus(_stream.get()) == kCFStreamStatusNotOpen) function _onqueue_setupStream_block_invoke file HTTPRequestBody.cpp line 878.
Crashed: com.apple.NSURLConnectionLoader
0 libsystem_kernel.dylib 0xa974 __pthread_kill + 8
1 libsystem_pthread.dylib 0x60ec pthread_kill + 268
2 libsystem_c.dylib 0x75b80 abort + 180
3 libsystem_c.dylib 0x74e70 err + 282
4 CFNetwork 0x1f73b8 CFHTTPCookieStorageUnscheduleFromRunLoop + 278252
5 libdispatch.dylib 0x3dd4 _dispatch_client_callout + 20
6 libdispatch.dylib 0x786c _dispatch_block_invoke_direct + 288
7 CFNetwork 0x259ab0 estimatedPropertyListSize + 33724
8 CoreFoundation 0x24b34 CFArrayApplyFunction + 72
9 CFNetwork 0x2599a0 estimatedPropertyListSize + 33452
10 CFNetwork 0x25c084 estimatedPropertyListSize + 43408
11 CoreFoundation 0x3762c __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28
12 CoreFoundation 0x368a8 __CFRunLoopDoSource0 + 176
13 CoreFoundation 0x35058 __CFRunLoopDoSources0 + 244
14 CoreFoundation 0x33d88 __CFRunLoopRun + 828
15 CoreFoundation 0x33968 CFRunLoopRunSpecific + 608
16 CFNetwork 0x25ac48 estimatedPropertyListSize + 38228
17 Foundation 0x9ca9c __NSThread__start__ + 732
18 libsystem_pthread.dylib 0x2a90 _pthread_start + 136
19 libsystem_pthread.dylib 0x1fcc thread_start + 8
This is how we build the operation:
-(NSOperation*)operationForRequest:(Job*)job
{
NSURL *url = [NSURL URLWithString:job.url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:@"application/json, application/xml, text/plain" forHTTPHeaderField:@"Accept"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"no-cache" forHTTPHeaderField:@"Cache-Control"];
[request setValue:[NSString stringWithFormat:@"Bearer %@", [self getToken]] forHTTPHeaderField:@"Authorization"];
[request setHTTPMethod:job.method];
NSData *bodyData = [job.payload dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:bodyData];
return [[NetworkOperation alloc] initWithRequest:request uuid:job.jobId completionHandler:^(NSString* jobId, NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
@autoreleasepool {
RLMRealm *realm = [RLMRealm defaultRealm];
Job *opJob = [Job objectInRealm:realm forPrimaryKey:jobId];
[self processJobResponse:opJob response:response data:data error:error realm:realm];
}
});
}];
}
This is how the NetworkOperation executes the request:
- (void)main {
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionTask *task = [session dataTaskWithRequest:self.request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (self.networkOperationCompletionBlock) {
self.networkOperationCompletionBlock(self.uuid, data, response, error);
self.networkOperationCompletionBlock = nil;
}
[self completeOperation];
}];
[task resume];
self.task = task;
}
crashlog3.crash
Im using Notions API to print out some data from one of my own pages in notion and im using URLSession to make the request then parsing the unwrapped data but nothing is being returned to my console and I know my endpoint and API key is correct. I've gone through the notion API documentation can't can't seem to find anything in it that I am not doing or doing wrong. Ill provide my code as well as the documentation I've been consulting: https://developers.notion.com/reference/intro
import Foundation
struct Page: Codable {
let id: String
let title: String
}
let endpoint = URL(string: "https://api.notion.com/v1/pages/8efc0ca3d9cc44fbb1f34383b794b817")
let apiKey = "… redacted …"
let session = URLSession.shared
func makeRequest() {
if let endpoint = endpoint {
let task = URLSession.shared.dataTask(with: endpoint) { data, response, error in
if let taskError = error {
print("could not establish url request:\(taskError)")
return
}
if let unwrapData = data { //safely unwrapping the data value using if let
do {
let decoder = JSONDecoder() //JSONDecoder method to decode api data,
let codeUnwrappedData = try decoder.decode(Page.self,from: unwrapData) //type: specifies its a struct, from: passes the data parmeter that contains the api data to be decoded
} catch {
print("could not parse json data")
}
}
if let httpResponse = response as? HTTPURLResponse {
if httpResponse.statusCode == 200 {
if let apiData = data {
print(String(data: apiData, encoding: .utf8)!)
}
} else {
print("unsuccessful http response:\(httpResponse)")
}
makeRequest()
}
}
task.resume()
}
}
I recently re-read Performing manual server trust authentication and noticed that it does not mention having to call SecTrustEvaluate (or its replacements) in client code (anymore). Is that implicitly taken care of by ATS?
Questions about FTP crop up from time-to-time here on DevForums. In most cases I write a general “don’t use FTP” response, but I don’t have time to go into all the details. I’ve created this post as a place to collect all of those details, so I can reference them in other threads.
IMPORTANT Apple’s official position on FTP is:
All our FTP APIs have been deprecated, and you should avoid using deprecated APIs.
Apple has been slowly removing FTP support from the user-facing parts of our system. The most recent example of this is that we removed the ftp command-line tool in macOS 10.13.
You should avoid the FTP protocol and look to adopt more modern alternatives.
The rest of this post is an informational explanation of the overall FTP picture.
This post is locked so I can keep it focused. If you have questions or comments, please do create a new thread with the Network tag and I’ll respond there.
Don’t Use FTP
FTP is a very old and very crufty protocol. Certain things that seem obvious to us now — like being able to create a GUI client that reliably shows a directory listing in a platform-independent manner — are not possible to do in FTP. However, by far the biggest problem with FTP is that it provides no security [1]. Specifically, the FTP protocol:
Provides no on-the-wire privacy, so anyone can see the data you transfer
Provides no client-authenticates-server authentication, so you have no idea whether you’re talking to the right server
Provides no data integrity, allowing an attacker to munge your data in transit
Transfers user names and passwords in the clear
Using FTP for anonymous downloads may be acceptable (see the note below) but most other uses of FTP are completely inappropriate for the modern Internet.
IMPORTANT You should only use FTP for anonymous downloads if you have an independent way to check the integrity of the data you’ve downloaded. For example, if you’re downloading a software update, you could use code signing to check its integrity. If you don’t check the integrity of the data you’ve downloaded, an attacker could substitute a malicious download instead. This would be especially bad in, say, the software update case.
These fundamental problems with the FTP protocol mean that it’s not a priority for Apple. This is reflected in the available APIs, which is the subject of the next section.
FTP APIs
Apple provides two FTP APIs:
All Apple platforms provide FTP downloads via NSURLSession
Most Apple platforms (everything except watchOS) support CFFTPStream, which allows for directory listings, downloads, uploads, and directory creation.
All of these FTP APIs are now deprecated:
NSURLSession was deprecated for the purposes of FTP in the 2022 SDKs (macOS 13, {i{,Pad},tv}OS 16, watchOS 9) [2].
CFFTPStream was deprecated in the 2016 SDKs (macOS 10.11, {i{,Pad},tv}OS 9).
CFFTPStream still works about as well as it ever did, which is not particularly well. Specifically:
There is at least one known crashing bug (r. 35745763), albeit one that occurs quite infrequently.
There are clear implementation limitations — like the fact that CFFTPCreateParsedResourceListing assumes a MacRoman text encoding (r. 7420589) — that will not be fixed.
If you’re looking for an example of how to use these APIs, check out SimpleFTPSample.
Note This sample has not been updated since 2013 and is unlikely to ever be updated given Apple’s position on FTP.
The FTP support in NSURLSession has significant limitations:
NSURLSession only supports FTP downloads; there is no support for uploads or any other FTP operations
NSURLSession does not support resumable FTP downloads [3]
NSURLSession background sessions only support HTTP and HTTPS, so you can’t run FTP downloads in the background on iOS
If Apple’s FTP APIs are insufficient for your needs, you’ll need to write or acquire your own FTP library. Before you do that, however, consider switching to an alternative protocol. After all, if you’re going to go to the trouble of importing a large FTP library into your code base, you might as well import a library for a better protocol. The next section discusses some options in this space.
Alternative Protocols
There are numerous better alternatives to FTP:
HTTPS is by far the best alternative to FTP, offering good security, good APIs on Apple platforms, good server support, and good network compatibility. Implementing traditional FTP operations over HTTPS can be a bit tricky. One possible way forward is to enable DAV extensions on the server.
FTPS is FTP over TLS (aka SSL). While FTPS adds security to the protocol, which is very important, it still inherits many of FTP’s other problems. Personally I try to avoid this protocol.
SFTP is a file transfer protocol that’s completely unrelated to FTP. It runs over SSH, making it a great alternative in many of the ad hoc setups that traditionally use FTP.
Apple does not have an API for either FTPS or SFTP, although on macOS you may be able to make some headway by invoking the sftp command-line tool.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] In another thread someone asked me about FTP’s other problems, those not related to security, so let’s talk about that.
One of FTP’s implicit design goals was to provide cross-platform support that exposes the target platform. You can think of FTP as being kinda like telnet. When you telnet from Unix to VMS, it doesn’t aim to abstract away VMS commands, so that you can type Unix commands at the VMS prompt. Rather, you’re expected to run VMS commands. FTP is (a bit) like that.
This choice made sense back when the FTP protocol was invented. Folks were expecting to use FTP via a command-line client, so there was a human in the loop. If they ran a command and it produced VMS-like output, that was fine because they knew that they were FTPing into a VMS machine.
However, most users today are using GUI clients, and this design choice makes it very hard to create a general GUI client for FTP. Let’s consider the simple problem of getting the contents of a directory. When you send an FTP LIST command, the server would historically run the platform native directory list command and pipe the results back to you. To create a GUI client you have to parse that data to extract the file names. Doing that is a serious challenge. Indeed, just the first step, working out the text encoding, is a challenge. Many FTP servers use UTF-8, but some use ISO-Latin-1, some use other standard encodings, some use Windows code pages, and so on.
I say “historically” above because there have been various efforts to standardise this stuff, both in the RFCs and in individual server implementations. However, if you’re building a general client you can’t rely on these efforts. After all, the reason why folks continue to use FTP is because of it widespread support.
[2] To quote the macOS 13 Ventura Release Notes:
FTP is deprecated for URLSession and related APIs. Please adopt
modern secure networking protocols such as HTTPS. (92623659)
[3] Although you can implement resumable downloads using the lower-level CFFTPStream API, courtesy of the kCFStreamPropertyFTPFileTransferOffset property.
Revision History
2024-04-15 Added a footnote about FTP’s other problems. Made other minor editorial changes.
2022-08-09 Noted that the FTP support in NSURLSession is now deprecated. Made other minor editorial changes.
2021-04-06 Fixed the formatting. Fixed some links.
2018-02-23 First posted.
I have an issue where performing a 'POST' request fails with a 400 when done on a device running iOS 16, but succeeds with a 200 on devices running iOS 17. I have not been able to find any explanations for this behavior. I've checked the request on both versions and it's identical in both versions of iOS, as far as I can tell.
BodyStream : JSON object data
Headers : Content-Type:application/json
TimeoutInterval: 900
I have an app with IAP which uses a URLSession object to download files from a server. The download part of the code is:
let request = URLRequest(url: fromURL, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: timeoutInterval)
let (data, response) = try await downloadSession.data(for: request)
This code has been working without trouble for over a year with thousands of downloads.
Now I have a user with a new iPhone (iOS 17.3.1) which refuses to download, failing at the above code (judging by the high level logs).
My question is this: What sort of things should we be looking at in order to diagnose this issue?
So far we have done the following:
He has no general download issue (eg Safari works fine)
His network access is 'normal' and the problem persists when the network is changed (4G, wifi etc)
He runs a VPN (Nord) but the problem persists when this is off
He has no 3rd party AV software
His phone is not in lockdown mode
Any pointers would be appreciated! NB I have no physical access to his device (yet!)
Hi,
Is there any way to return cached URLSession response and then reload and return?
I want show cached response while load last version of API call, and if reload works fine, show the latest version of response and in case os failure, show cached response (if exists)
Thanks!
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
}
}