Is there anyone who can please help me adding a widget to an existing app with Xcode 15.4?
Post
Replies
Boosts
Views
Activity
Ohhh so easy - once you know how to do it!
Thank you very much for your help, really appreciated!
Thank you @eskimo for your researches.
I will study very carefully the link you provided in your answer. I didn't know that NSUrlSession sends a default user string, specific to your application, like in https://stackoverflow.com/questions/36379347/does-nsurlsession-send-user-agent-automatically and https://www.whatismybrowser.com/guides/the-latest-user-agent/safari.
Meanwhile I've added this to my code:
config.httpAdditionalHeaders = ["User-Agent": ""]
and in fact my app works!
So the reason why in .NET this code was working, is that there no user-agent is added automatically.
Only, I can't find any reference in the official Apple docs that a default user agent is sent with the requests. Maybe they could add docs about it, and how to disable or customize it.
(https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1411532-httpadditionalheaders says that you could eventually ADD your custom agent).
Netherdeless, thank you very much for your support.
I will learn a lot studying your answer.
Please take this as a very hearted appreciation email for your manager!
Here is the Info.plist I am usign to ignore https errors (I hope):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>
P.S: in case someone wants to help me, here is the ViewModel that I use now for the download (clearly it's just POC code):
//
// DownloadForegroundViewModel.swift
// MacDownloader
//
// Created by Damiano Curia on 27.05.23.
//
import Foundation
@MainActor
class DownloadForegroundViewModel: NSObject, ObservableObject {
//let fileToDownload = "https://example.com/"
//let fileToDownload = "https://speed.hetzner.de/100MB.bin"
//let fileToDownload = "https://nbg1-speed.hetzner.com/100MB.bin"
let fileToDownload = "https://www.backade.com/22Z9H358/SZCPXRW/?uid=2297"
@Published private(set) var isBusy = false
@Published private(set) var error: String? = nil
@Published private(set) var percentage: Int? = nil
@Published private(set) var fileName: String? = nil
@Published private(set) var downloadedSize: UInt64? = nil
// https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_from_websites
private lazy var urlSession: URLSession = {
let config = URLSessionConfiguration.default
config.waitsForConnectivity = true
config.allowsCellularAccess = true
config.allowsConstrainedNetworkAccess = true
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
//private lazy var urlSession = URLSession(configuration: .default,
// delegate: self,
// delegateQueue: nil)
@Published private var downloadTask: URLSessionDownloadTask? = nil
func downloadToFileWithProgress() async {
self.isBusy = true
self.error = nil
self.percentage = 0
self.fileName = nil
self.downloadedSize = nil
let downloadTask = urlSession.downloadTask(with: URL(string: fileToDownload)!)
downloadTask.resume()
self.downloadTask = downloadTask
}
// https://developer.apple.com/documentation/foundation/url_loading_system/pausing_and_resuming_downloads
@Published private var resumeData: Data? = nil
var canPauseDownload: Bool {
get { return self.downloadTask != nil && self.resumeData == nil }
}
func pauseDownload() {
guard let downloadTask = self.downloadTask else {
return
}
downloadTask.cancel { resumeDataOrNil in
guard let resumeData = resumeDataOrNil else {
// download can't be resumed; remove from UI if necessary
return
}
Task { @MainActor in self.resumeData = resumeData }
}
}
// https://developer.apple.com/documentation/foundation/url_loading_system/pausing_and_resuming_downloads
var canResumeDownload: Bool {
get { return self.resumeData != nil}
}
func resumeDownload() {
guard let resumeData = self.resumeData else {
return
}
let downloadTask = urlSession.downloadTask(withResumeData: resumeData)
downloadTask.resume()
self.error = nil
self.downloadTask = downloadTask
self.resumeData = nil
}
}
extension DownloadForegroundViewModel: URLSessionDownloadDelegate
{
// https://stackoverflow.com/questions/59483557/disable-https-get-certificate-check-in-swift-5
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
//Trust the certificate even if not valid
let urlCredential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
completionHandler(.useCredential, urlCredential)
}
// https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_from_websites
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didWriteData bytesWritten: Int64,
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64) {
if downloadTask != self.downloadTask {
return
}
let percentage = Int(totalBytesWritten * 100 / totalBytesExpectedToWrite)
Task { @MainActor in self.percentage = percentage }
}
// https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_from_websites
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
if downloadTask != self.downloadTask {
return
}
defer {
Task { @MainActor in self.isBusy = false }
}
guard let httpResponse = downloadTask.response as? HTTPURLResponse else {
Task { @MainActor in self.error = "No HTTP Result" }
return
}
guard (200...299).contains(httpResponse.statusCode) else {
Task { @MainActor in self.error = "Http Result: \(httpResponse.statusCode)" }
return
}
let fileName = location.path
let attributes = try? FileManager.default.attributesOfItem(atPath: fileName)
let fileSize = attributes?[.size] as? UInt64
Task { @MainActor in
self.error = nil
self.percentage = 100
self.fileName = fileName
self.downloadedSize = fileSize
self.downloadTask = nil
}
}
// https://developer.apple.com/documentation/foundation/url_loading_system/pausing_and_resuming_downloads
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
guard let error = error else {
return
}
Task { @MainActor in self.error = error.localizedDescription }
let userInfo = (error as NSError).userInfo
if let resumeData = userInfo[NSURLSessionDownloadTaskResumeData] as? Data {
Task { @MainActor in self.resumeData = resumeData }
} else {
Task { @MainActor in
self.isBusy = false
self.downloadTask = nil
}
}
}
}
And this is the minimal UI (that sets the button text to "100" when download is complete, otherwise error is shown in Xcode):
import SwiftUI
struct ContentView: View {
@ObservedObject var vm: DownloadForegroundViewModel
var body: some View {
Button(String(vm.percentage ?? 0)) {
Task { await vm.downloadToFileWithProgress() }
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(vm: DownloadForegroundViewModel())
}
}
Hi,
I still have this issue and I have done the following tests:
on a Mac app, it has the same identical issue (so I am testing on the Mac app now)
I have developed a MAUI app (.NET) and deployed to the iPhone. It works fine and can download the file
If I use other urls, my code works fine
I have tried disabling NSAppTransportSecurity allowing NSAllowsArbitraryLoads. I can download http content, but this content is still wrong
I have tested on this url, that sends a redirect, and it also works fine: https://www.backade.com/22Z9H358/SZCPXRW/?uid=2297
So the issue is just between the Swift libraries and this website.
Other websites (even in Swift) or other languages (.NET on both Windows and iOS) work fine
Clearly both Chrome and Safari can download this file.
Can anyone give me additional tips trying to fix this issue?
Regards, Damiano
This code works:
// Add handler for Play Command
commandCenter.playCommand.addTarget { [unowned self] event in
if !self.isPlaying {
self.player.play()
self.isPlaying = true
return .success
}
return .commandFailed
}
My code was working in the past, and it still works on other URLs
Still, for this particular URL, it doesn't work from iOS and MacOS. But on the same machine works for example from .NET.
Have you been able to verify if the GitHub sample works for you?
A couple of updates:
the download works from Chrome and .NET (created a sample app)
old installation of the app, deployed to old iPhones, manifest now the same issue
tried to run the app from MacOS - same error
I have found this thread https://developer.apple.com/forums/thread/84608 where the server was blocking downloads from unknows networks - but still it works with WiFi.
To me, it doesn't work in both 5G and wifi, and even from computers - but the .NET code works fine.
So I would still appeciate some help from the community.
Same here with latest iOS, Apple Watch OS and Xcode 13.1.
What is funny is that in my app I need to test with AirPods, so I can't disable Bluetooth.
And like everyone, I don't need to deploy anything on my Apple Watch...
I have the same issue with XCode 12.3