@@ -511,20 +511,6 @@ struct HeartbeatApp: App {
511511 @StateObject private var themeExpansionManager = ThemeExpansionManager ( )
512512 @Environment ( \. scenePhase) private var scenePhase // Observe scene lifecycle
513513
514- let urls : [ String ] = [
515- " https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/BuildManifest.plist " ,
516- " https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg " ,
517- " https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg.trustcache "
518- ]
519-
520- let outputDir : String = " DDI "
521-
522- let outputFiles : [ String ] = [
523- " DDI/BuildManifest.plist " ,
524- " DDI/Image.dmg " ,
525- " DDI/Image.dmg.trustcache "
526- ]
527-
528514 init ( ) {
529515 registerAdvancedOptionsDefault ( )
530516 ContinuedProcessingManager . shared. configureIfNeeded ( )
@@ -583,16 +569,20 @@ struct HeartbeatApp: App {
583569 BackgroundContainer {
584570 MainTabView ( )
585571 . onAppear {
586- let fileManager = FileManager . default
587- for (index, urlString) in urls. enumerated ( ) {
588- let destinationURL = URL . documentsDirectory. appendingPathComponent ( outputFiles [ index] )
589- if !fileManager. fileExists ( atPath: destinationURL. path) {
590- downloadFile ( from: urlString, to: destinationURL) { result in
591- if ( result != " " ) {
572+ Task {
573+ let fileManager = FileManager . default
574+ for item in ddiDownloadItems {
575+ let destinationURL = URL . documentsDirectory. appendingPathComponent ( item. relativePath)
576+ if fileManager. fileExists ( atPath: destinationURL. path) { continue }
577+ do {
578+ try await downloadFile ( from: item. urlString, to: destinationURL)
579+ } catch {
580+ await MainActor . run {
592581 alert_title = " An Error has Occurred "
593- alert_string = " [Download DDI Error]: " + result
582+ alert_string = " [Download DDI Error]: \( error . localizedDescription ) "
594583 show_alert = true
595584 }
585+ break
596586 }
597587 }
598588 }
@@ -961,75 +951,76 @@ public func showAlert(title: String, message: String, showOk: Bool, showTryAgain
961951 }
962952}
963953
964- func downloadFile( from urlString: String , to destinationURL: URL , completion: @escaping ( String ) -> Void ) {
965- let fileManager = FileManager . default
966- let documentsDirectory = URL . documentsDirectory
967-
968- guard let url = URL ( string: urlString) else {
969- print ( " Invalid URL: \( urlString) " )
970- completion ( " [Internal Invalid URL error] " )
971- return
972- }
954+ private struct DDIDownloadItem {
955+ let name : String
956+ let relativePath : String
957+ let urlString : String
958+ }
959+
960+ private let ddiDownloadItems : [ DDIDownloadItem ] = [
961+ . init(
962+ name: " Build Manifest " ,
963+ relativePath: " DDI/BuildManifest.plist " ,
964+ urlString: " https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/BuildManifest.plist "
965+ ) ,
966+ . init(
967+ name: " Image " ,
968+ relativePath: " DDI/Image.dmg " ,
969+ urlString: " https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg "
970+ ) ,
971+ . init(
972+ name: " TrustCache " ,
973+ relativePath: " DDI/Image.dmg.trustcache " ,
974+ urlString: " https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg.trustcache "
975+ )
976+ ]
977+
978+ enum DDIDownloadError : LocalizedError {
979+ case invalidURL( String )
973980
974- let task = URLSession . shared. downloadTask ( with: url) { ( tempLocalUrl, response, error) in
975- guard let tempLocalUrl = tempLocalUrl, error == nil else {
976- print ( " Error downloading file from \( urlString) : \( String ( describing: error) ) " )
977- completion ( " Are you connected to the internet? [Download Failed] " )
978- return
979- }
980-
981- do {
982- try fileManager. createDirectory ( at: destinationURL. deletingLastPathComponent ( ) , withIntermediateDirectories: true , attributes: nil )
983- try fileManager. moveItem ( at: tempLocalUrl, to: destinationURL)
984- print ( " Downloaded \( urlString) to \( destinationURL. path) " )
985- } catch {
986- print ( " Error saving file: \( error) " )
981+ var errorDescription : String ? {
982+ switch self {
983+ case . invalidURL( let string) :
984+ return " Invalid download URL: \( string) "
987985 }
988986 }
989- task. resume ( )
990- completion ( " " )
991987}
992988
993- func redownloadDDI( ) {
989+ func downloadFile( from urlString: String , to destinationURL: URL ) async throws {
990+ guard let url = URL ( string: urlString) else {
991+ throw DDIDownloadError . invalidURL ( urlString)
992+ }
993+ let ( tempLocalUrl, _) = try await URLSession . shared. download ( from: url)
994994 let fileManager = FileManager . default
995- let ddiFiles = [
996- " DDI/BuildManifest.plist " ,
997- " DDI/Image.dmg " ,
998- " DDI/Image.dmg.trustcache "
999- ]
1000-
1001- for file in ddiFiles {
1002- let fileURL = URL . documentsDirectory. appendingPathComponent ( file)
1003- if fileManager. fileExists ( atPath: fileURL. path) {
1004- do {
1005- try fileManager. removeItem ( at: fileURL)
1006- print ( " Deleted \( file) " )
1007- } catch {
1008- print ( " Error deleting \( file) : \( error) " )
1009- }
1010- }
995+ try fileManager. createDirectory ( at: destinationURL. deletingLastPathComponent ( ) ,
996+ withIntermediateDirectories: true )
997+ if fileManager. fileExists ( atPath: destinationURL. path) {
998+ try fileManager. removeItem ( at: destinationURL)
1011999 }
1000+ try fileManager. moveItem ( at: tempLocalUrl, to: destinationURL)
1001+ }
10121002
1013- let urls = [
1014- " https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/BuildManifest.plist " ,
1015- " https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg " ,
1016- " https://github.com/doronz88/DeveloperDiskImage/raw/refs/heads/main/PersonalizedImages/Xcode_iOS_DDI_Personalized/Image.dmg.trustcache "
1017- ]
1018-
1019- let group = DispatchGroup ( )
1020-
1021- for (index, urlString) in urls. enumerated ( ) {
1022- let destinationURL = URL . documentsDirectory. appendingPathComponent ( ddiFiles [ index] )
1023- group. enter ( )
1024- downloadFile ( from: urlString, to: destinationURL) { result in
1025- if ( result != " " ) {
1026- showAlert ( title: " An Error has Occurred " , message: " [Download DDI Error]: " + result, showOk: true )
1027- }
1028- group. leave ( )
1003+ func redownloadDDI( progressHandler: ( ( Double , String ) -> Void ) ? = nil ) async throws {
1004+ let fileManager = FileManager . default
1005+ let totalStages = Double ( ddiDownloadItems. count + 1 )
1006+ var completedStages = 0.0
1007+
1008+ progressHandler ? ( 0.0 , " Removing existing DDI files… " )
1009+ for item in ddiDownloadItems {
1010+ let fileURL = URL . documentsDirectory. appendingPathComponent ( item. relativePath)
1011+ if fileManager. fileExists ( atPath: fileURL. path) {
1012+ try fileManager. removeItem ( at: fileURL)
10291013 }
10301014 }
1031-
1032- group. notify ( queue: . main) {
1033- showAlert ( title: " Success " , message: " DDI files have been redownloaded. " , showOk: true )
1015+ completedStages += 1.0
1016+ progressHandler ? ( completedStages / totalStages, " Starting downloads… " )
1017+
1018+ for item in ddiDownloadItems {
1019+ progressHandler ? ( completedStages / totalStages, " Downloading \( item. name) … " )
1020+ let destinationURL = URL . documentsDirectory. appendingPathComponent ( item. relativePath)
1021+ try await downloadFile ( from: item. urlString, to: destinationURL)
1022+ completedStages += 1.0
1023+ progressHandler ? ( completedStages / totalStages, " \( item. name) ready " )
10341024 }
1025+ progressHandler ? ( 1.0 , " DDI download complete. " )
10351026}
0 commit comments