├── README.md ├── XZDownloadTask.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── XZDownloadTask.xccheckout │ └── xcuserdata │ │ └── xiazer.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ └── xiazer.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── XZDownloadTask.xcscheme │ └── xcschememanagement.plist ├── XZDownloadTask ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── MultDownloadViewController.h ├── MultDownloadViewController.m ├── SingleDownloadViewController.h ├── SingleDownloadViewController.m ├── ViewController.h ├── ViewController.m ├── XZDownload-Prefix.pch ├── XZDownloadElement.h ├── XZDownloadElement.m ├── XZDownloadGroupManager.h ├── XZDownloadGroupManager.m ├── XZDownloadManager.h ├── XZDownloadManager.m ├── XZDownloadResponse.h ├── XZDownloadResponse.m ├── XZDownloadView.h ├── XZDownloadView.m └── main.m ├── XZDownloadTaskTests ├── Info.plist └── XZDownloadTaskTests.m └── XZDownlod.gif /README.md: -------------------------------------------------------------------------------- 1 | ###先上效果图 2 | ![XZDownloadTask](https://raw.githubusercontent.com/kingundertree/XZDownloadTask/master/XZDownlod.gif) 3 | 4 | ###github地址 5 | 6 | https://github.com/kingundertree/XZDownloadTask 7 | 8 | ###说明 9 | 之前坐过几版下载的demo,要么不支持多任务、要么不支持后台下载或者对设计不满意。 10 | 11 | 这次重新设计新的模块,支持单任务、多任务、后台下载。 12 | 13 | 保留一个彩蛋,供下次优化。 14 | 15 | ###功能 16 | 1. 支持单个任务下载,实现下载、暂停、重新下载、取消等。 17 | 2. 单个任务支持后台下载,下载内容存储和下载信息回调,包括下载存储url和下载进度 18 | 3. 支持多任务下载,包括批量下载、批量暂停、批量取消、批量重启。支持单个任务设置是否后台下载。同样支持单个任务的进度等信息回调。 19 | 20 | ###实现机制 21 | 1. 下载基于iOS7 NSURLSessionDownloadTask 实现,通过配置NSUrlSession实现 22 | 2. 通过NSURLSession配置backgroundSessionConfigurationWithIdentifier,实现后台下载 23 | 3. 通过NSURLSession配置defaultSessionConfiguration,实现普通下载 24 | 4. 通过NSURLSessionDownloadDelegate的代理方法,获取下载进度进度、下载成功失败以及后台下载完成信息 25 | 26 | ###设计模式 27 | 28 | XZDownloadTask.........................下载类 29 | XZDownloadManager..................下载主功能实现区 30 | XZDownloadGroupManager.............多人下载管理类 31 | XZDownloadElement..................每个下载任务的辅助类 32 | XZDownloadResponse.................下载成功失败进度的响应类 33 | 34 | ###单任务下载实现 35 | 1.创建下载任务 36 | 通过isDownloadBackground分别创建常规下载任务或后台下载任务。 37 | 38 | - (void)configDownloadInfo:(NSString *) downloadStr isDownloadBackground:(BOOL)isDownloadBackground identifier:(NSString *)identifier succuss:(void (^)(XZDownloadResponse *response)) succuss fail:(void(^)(XZDownloadResponse *response)) fail progress:(void(^)(XZDownloadResponse *response)) progress cancle:(void(^)(XZDownloadResponse *response)) cancle pause:(void(^)(XZDownloadResponse *response)) pause resume:(void(^)(XZDownloadResponse *response)) resume{ 39 | self.downloadSuccuss = succuss; 40 | self.downloadFail = fail; 41 | self.downloadProgress = progress; 42 | self.downloadCancle = cancle; 43 | self.downloadPause = pause; 44 | self.downloadResume = resume; 45 | 46 | self.identifier = identifier ? identifier : [[NSProcessInfo processInfo] globallyUniqueString]; 47 | 48 | if (isDownloadBackground) { 49 | [self startBackgroundDownload:downloadStr identifier:self.identifier]; 50 | } else { 51 | [self startNormalDownload:downloadStr]; 52 | } 53 | } 54 | 55 | 2.常规下载任务 56 | 57 | - (void)startNormalDownload:(NSString *)downloadStr { 58 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadStr]]; 59 | self.normalSessionTask = [self.normalSession downloadTaskWithRequest:request]; 60 | [self.normalSessionTask resume]; 61 | } 62 | 63 | - (NSURLSession *)normalSession { 64 | if (!_normalSession) { 65 | NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; 66 | _normalSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil]; 67 | _normalSession.sessionDescription = @"normal NSURLSession"; 68 | } 69 | 70 | return _normalSession; 71 | } 72 | 73 | 3.后台下载任务 74 | 75 | - (void)startBackgroundDownload:(NSString *)downloadStr identifier:(NSString *)identifier { 76 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadStr]]; 77 | self.backgroundSession = [self getBackgroundSession:identifier]; 78 | self.backgroundSessionTask = [self.backgroundSession downloadTaskWithRequest:request]; 79 | [self.backgroundSessionTask resume]; 80 | } 81 | 82 | - (NSURLSession *)getBackgroundSession:(NSString *)identifier { 83 | NSURLSession *backgroundSession = nil; 84 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:[NSString stringWithFormat:@"background-NSURLSession-%@",identifier]]; 85 | config.HTTPMaximumConnectionsPerHost = 5; 86 | backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; 87 | 88 | return backgroundSession; 89 | } 90 | 91 | 4.暂停下载任务 92 | 93 | 核心方法cancelByProducingResumeData 94 | 95 | - (void)pauseDownload { 96 | __weak typeof(self) this = self; 97 | if (self.normalSessionTask) { 98 | [self.normalSessionTask cancelByProducingResumeData:^(NSData *resumeData) { 99 | this.partialData = resumeData; 100 | this.normalSessionTask = nil; 101 | }]; 102 | } else if (self.backgroundSessionTask) { 103 | [self.backgroundSessionTask cancelByProducingResumeData:^(NSData *resumeData) { 104 | this.partialData = resumeData; 105 | }]; 106 | } 107 | } 108 | 109 | 4.重启下载任务 110 | 111 | 核心方法downloadTaskWithResumeData 112 | 113 | - (void)resumeDownload { 114 | if (!self.resumeSessionTask) { 115 | if (self.partialData) { 116 | self.resumeSessionTask = [self.normalSession downloadTaskWithResumeData:self.partialData]; 117 | 118 | [self.resumeSessionTask resume]; 119 | } 120 | } 121 | } 122 | 123 | 5.取消下载任务 124 | 125 | 核心方法cancel 126 | 127 | - (void)cancleDownload { 128 | if (self.normalSessionTask) { 129 | [self.normalSessionTask cancel]; 130 | self.normalSessionTask = nil; 131 | } else if (self.resumeSessionTask) { 132 | self.partialData = nil; 133 | [self.resumeSessionTask cancel]; 134 | self.resumeSessionTask = nil; 135 | } else if (self.backgroundSessionTask) { 136 | [self.backgroundSessionTask cancel]; 137 | self.backgroundSessionTask = nil; 138 | } 139 | } 140 | 141 | 6.后台下载成功后回调 142 | 143 | - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler 144 | { 145 | self.backgroundURLSessionCompletionHandler = completionHandler; 146 | } 147 | 148 | 7.后台下载成功后回调 149 | 150 | - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler 151 | { 152 | self.backgroundURLSessionCompletionHandler = completionHandler; 153 | } 154 | 155 | 8.后台下载成功后回调NSURLSessionDownloadDelegate 156 | 157 | 下载中,处理下载进度 158 | 159 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 160 | { 161 | double currentProgress = totalBytesWritten / (double)totalBytesExpectedToWrite; 162 | NSLog(@"%@---%0.2f",self.identifier,currentProgress); 163 | } 164 | 165 | 下载失败 166 | 167 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes 168 | { 169 | // 下载失败 170 | } 171 | 172 | 下载成功 173 | 174 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location 175 | { 176 | // 下载成功后文件处理 177 | NSFileManager *fileManager = [NSFileManager defaultManager]; 178 | 179 | NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; 180 | NSURL *documentsDirectory = URLs[0]; 181 | 182 | NSURL *destinationPath = [documentsDirectory URLByAppendingPathComponent:self.identifier]; 183 | NSError *error; 184 | 185 | [fileManager removeItemAtURL:destinationPath error:NULL]; 186 | BOOL success = [fileManager copyItemAtURL:location toURL:destinationPath error:&error]; 187 | 188 | if (success) { 189 | dispatch_async(dispatch_get_main_queue(), ^{ 190 | // 此处可更新UI 191 | }); 192 | } else { 193 | } 194 | 195 | // 下载成功后,下载任务处理,包括后台任务和普通任务区别,以及重启任务 196 | if(downloadTask == self.normalSessionTask) { 197 | self.normalSessionTask = nil; 198 | } else if (downloadTask == self.resumeSessionTask) { 199 | self.resumeSessionTask = nil; 200 | self.partialData = nil; 201 | } else if (session == self.backgroundSession) { 202 | self.backgroundSessionTask = nil; 203 | 204 | AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 205 | if(appDelegate.backgroundURLSessionCompletionHandler) { 206 | void (^handler)() = appDelegate.backgroundURLSessionCompletionHandler; 207 | appDelegate.backgroundURLSessionCompletionHandler = nil; 208 | handler(); 209 | 210 | NSLog(@"后台下载完成"); 211 | } 212 | } 213 | } 214 | 215 | 9.后台下载完成,本地通知 216 | 217 | - (void)showLocalNotification:(BOOL)downloadSuc { 218 | UILocalNotification *notification = [[UILocalNotification alloc] init]; 219 | if (notification!=nil) { 220 | 221 | NSDate *now=[NSDate new]; 222 | notification.fireDate=[now dateByAddingTimeInterval:6]; 223 | notification.repeatInterval = 0; 224 | 225 | notification.timeZone = [NSTimeZone defaultTimeZone]; 226 | notification.soundName = UILocalNotificationDefaultSoundName; 227 | notification.alertBody = downloadSuc ? @"后台下载成功啦" : @"下载失败"; 228 | notification.alertAction = @"打开"; 229 | notification.hasAction = YES; 230 | notification.applicationIconBadgeNumber =+ 1; 231 | 232 | NSDictionary* infoDic = [NSDictionary dictionaryWithObject:@"value" forKey:@"key"]; 233 | notification.userInfo = infoDic; 234 | [[UIApplication sharedApplication] scheduleLocalNotification:notification]; 235 | } 236 | } 237 | 238 | 239 | ###多任务下载 240 | 241 | 多任务下载,基于单独任务下载实现。只是提供了统一的方法进行管理。 242 | 243 | 多任务下载采用单例管理 244 | 245 | 1.调用多任务下载,需要手动传入下载请求 246 | 247 | 需要手动添加identifier,并通过identifier作为唯一标识,处理后续下载任务。 248 | 249 | NSString *identifier = [[NSProcessInfo processInfo] globallyUniqueString]; 250 | 251 | [[XZDownloadGroupManager shareInstance] addDownloadRequest:[musicUrlArr objectAtIndex:index] identifier:identifier targetSelf:self showProgress:YES isDownloadBackground:YES downloadResponse:^(XZDownloadResponse *response) { 252 | [this handleResponse:response]; 253 | }]; 254 | 255 | 2.下载任务处理 256 | 257 | 这是下载模块处理最频繁的方法 258 | 259 | - (void)handleResponse:(XZDownloadResponse *)response { 260 | if (response.downloadStatus == XZDownloading) { 261 | NSLog(@"下载任务ing%@",response.identifier); 262 | XZDownloadView *downloadView = [self getDownloadView:response.identifier]; 263 | dispatch_async(dispatch_get_main_queue(), ^{ 264 | downloadView.progressV = response.progress; 265 | }); 266 | } else if (response.downloadStatus == XZDownloadSuccuss) { 267 | NSLog(@"下载任务成功%@",response.identifier); 268 | XZDownloadView *downloadView = [self getDownloadView:response.identifier]; 269 | downloadView.progressV = 1.0; 270 | } else if (response.downloadStatus == XZDownloadBackgroudSuccuss) { 271 | NSLog(@"后台下载任务成功%@",response.identifier); 272 | [self showLocalNotification:YES]; 273 | XZDownloadView *downloadView = [self getDownloadView:response.identifier]; 274 | downloadView.progressV = 1.0; 275 | } else if (response.downloadStatus == XZDownloadFail) { 276 | NSLog(@"下载任务失败%@",response.identifier); 277 | [self showLocalNotification:NO]; 278 | } else if (response.downloadStatus == XZDownloadCancle) { 279 | NSLog(@"下载任务取消%@",response.identifier); 280 | } else if (response.downloadStatus == XZDownloadPause) { 281 | NSLog(@"下载任务暂停%@",response.identifier); 282 | } else if (response.downloadStatus == XZDownloadResume) { 283 | NSLog(@"下载任务重启%@",response.identifier); 284 | } 285 | } 286 | 287 | 288 | 3.多任务的暂停、重启、取消 289 | 290 | 暂停 291 | 292 | [[XZDownloadGroupManager shareInstance] pauseAllDownloadRequest]; 293 | 294 | 重启 295 | 296 | [[XZDownloadGroupManager shareInstance] resumeAllDownloadRequest]; 297 | 298 | 取消 299 | 300 | [[XZDownloadGroupManager shareInstance] cancleAllDownloadRequest]; -------------------------------------------------------------------------------- /XZDownloadTask.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F887D7901AE5EE9600231B16 /* XZDownloadElement.m in Sources */ = {isa = PBXBuildFile; fileRef = F887D78F1AE5EE9600231B16 /* XZDownloadElement.m */; }; 11 | F887D7931AE6322600231B16 /* XZDownloadView.m in Sources */ = {isa = PBXBuildFile; fileRef = F887D7921AE6322600231B16 /* XZDownloadView.m */; }; 12 | F8A6A96A1AE105A00038BC7F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A6A9691AE105A00038BC7F /* main.m */; }; 13 | F8A6A96D1AE105A00038BC7F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A6A96C1AE105A00038BC7F /* AppDelegate.m */; }; 14 | F8A6A9701AE105A00038BC7F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A6A96F1AE105A00038BC7F /* ViewController.m */; }; 15 | F8A6A9731AE105A00038BC7F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F8A6A9711AE105A00038BC7F /* Main.storyboard */; }; 16 | F8A6A9751AE105A00038BC7F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F8A6A9741AE105A00038BC7F /* Images.xcassets */; }; 17 | F8A6A9781AE105A00038BC7F /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = F8A6A9761AE105A00038BC7F /* LaunchScreen.xib */; }; 18 | F8A6A9841AE105A00038BC7F /* XZDownloadTaskTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A6A9831AE105A00038BC7F /* XZDownloadTaskTests.m */; }; 19 | F8A6A9901AE49D540038BC7F /* XZDownloadManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A6A98F1AE49D540038BC7F /* XZDownloadManager.m */; }; 20 | F8A6A9931AE4FE670038BC7F /* XZDownloadGroupManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A6A9921AE4FE670038BC7F /* XZDownloadGroupManager.m */; }; 21 | F8A6A9961AE508A50038BC7F /* XZDownloadResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A6A9951AE508A50038BC7F /* XZDownloadResponse.m */; }; 22 | F8CDC0981AEE26D700B351F7 /* SingleDownloadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8CDC0971AEE26D700B351F7 /* SingleDownloadViewController.m */; }; 23 | F8CDC09B1AEE26EE00B351F7 /* MultDownloadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8CDC09A1AEE26EE00B351F7 /* MultDownloadViewController.m */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | F8A6A97E1AE105A00038BC7F /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = F8A6A95C1AE105A00038BC7F /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = F8A6A9631AE105A00038BC7F; 32 | remoteInfo = XZDownloadTask; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | F887D78E1AE5EE9600231B16 /* XZDownloadElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XZDownloadElement.h; sourceTree = ""; }; 38 | F887D78F1AE5EE9600231B16 /* XZDownloadElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XZDownloadElement.m; sourceTree = ""; }; 39 | F887D7911AE6322600231B16 /* XZDownloadView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XZDownloadView.h; sourceTree = ""; }; 40 | F887D7921AE6322600231B16 /* XZDownloadView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XZDownloadView.m; sourceTree = ""; }; 41 | F887D7971AE6363400231B16 /* XZDownload-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XZDownload-Prefix.pch"; sourceTree = ""; }; 42 | F8A6A9641AE105A00038BC7F /* XZDownloadTask.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XZDownloadTask.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | F8A6A9681AE105A00038BC7F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | F8A6A9691AE105A00038BC7F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 45 | F8A6A96B1AE105A00038BC7F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 46 | F8A6A96C1AE105A00038BC7F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 47 | F8A6A96E1AE105A00038BC7F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 48 | F8A6A96F1AE105A00038BC7F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 49 | F8A6A9721AE105A00038BC7F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 50 | F8A6A9741AE105A00038BC7F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 51 | F8A6A9771AE105A00038BC7F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 52 | F8A6A97D1AE105A00038BC7F /* XZDownloadTaskTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XZDownloadTaskTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | F8A6A9821AE105A00038BC7F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | F8A6A9831AE105A00038BC7F /* XZDownloadTaskTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XZDownloadTaskTests.m; sourceTree = ""; }; 55 | F8A6A98E1AE49D540038BC7F /* XZDownloadManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XZDownloadManager.h; sourceTree = ""; }; 56 | F8A6A98F1AE49D540038BC7F /* XZDownloadManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XZDownloadManager.m; sourceTree = ""; }; 57 | F8A6A9911AE4FE670038BC7F /* XZDownloadGroupManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XZDownloadGroupManager.h; sourceTree = ""; }; 58 | F8A6A9921AE4FE670038BC7F /* XZDownloadGroupManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XZDownloadGroupManager.m; sourceTree = ""; }; 59 | F8A6A9941AE508A50038BC7F /* XZDownloadResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XZDownloadResponse.h; sourceTree = ""; }; 60 | F8A6A9951AE508A50038BC7F /* XZDownloadResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XZDownloadResponse.m; sourceTree = ""; }; 61 | F8CDC0961AEE26D700B351F7 /* SingleDownloadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SingleDownloadViewController.h; sourceTree = ""; }; 62 | F8CDC0971AEE26D700B351F7 /* SingleDownloadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SingleDownloadViewController.m; sourceTree = ""; }; 63 | F8CDC0991AEE26EE00B351F7 /* MultDownloadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultDownloadViewController.h; sourceTree = ""; }; 64 | F8CDC09A1AEE26EE00B351F7 /* MultDownloadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultDownloadViewController.m; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | F8A6A9611AE105A00038BC7F /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | F8A6A97A1AE105A00038BC7F /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | F8A6A95B1AE105A00038BC7F = { 86 | isa = PBXGroup; 87 | children = ( 88 | F8A6A9661AE105A00038BC7F /* XZDownloadTask */, 89 | F8A6A9801AE105A00038BC7F /* XZDownloadTaskTests */, 90 | F8A6A9651AE105A00038BC7F /* Products */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | F8A6A9651AE105A00038BC7F /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | F8A6A9641AE105A00038BC7F /* XZDownloadTask.app */, 98 | F8A6A97D1AE105A00038BC7F /* XZDownloadTaskTests.xctest */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | F8A6A9661AE105A00038BC7F /* XZDownloadTask */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | F8A6A98D1AE49C960038BC7F /* XZDownloadTask */, 107 | F8A6A96B1AE105A00038BC7F /* AppDelegate.h */, 108 | F8A6A96C1AE105A00038BC7F /* AppDelegate.m */, 109 | F8A6A96E1AE105A00038BC7F /* ViewController.h */, 110 | F8A6A96F1AE105A00038BC7F /* ViewController.m */, 111 | F8CDC0961AEE26D700B351F7 /* SingleDownloadViewController.h */, 112 | F8CDC0971AEE26D700B351F7 /* SingleDownloadViewController.m */, 113 | F8CDC0991AEE26EE00B351F7 /* MultDownloadViewController.h */, 114 | F8CDC09A1AEE26EE00B351F7 /* MultDownloadViewController.m */, 115 | F887D7911AE6322600231B16 /* XZDownloadView.h */, 116 | F887D7921AE6322600231B16 /* XZDownloadView.m */, 117 | F8A6A9711AE105A00038BC7F /* Main.storyboard */, 118 | F8A6A9741AE105A00038BC7F /* Images.xcassets */, 119 | F8A6A9761AE105A00038BC7F /* LaunchScreen.xib */, 120 | F8A6A9671AE105A00038BC7F /* Supporting Files */, 121 | ); 122 | path = XZDownloadTask; 123 | sourceTree = ""; 124 | }; 125 | F8A6A9671AE105A00038BC7F /* Supporting Files */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | F8A6A9681AE105A00038BC7F /* Info.plist */, 129 | F8A6A9691AE105A00038BC7F /* main.m */, 130 | F887D7971AE6363400231B16 /* XZDownload-Prefix.pch */, 131 | ); 132 | name = "Supporting Files"; 133 | sourceTree = ""; 134 | }; 135 | F8A6A9801AE105A00038BC7F /* XZDownloadTaskTests */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | F8A6A9831AE105A00038BC7F /* XZDownloadTaskTests.m */, 139 | F8A6A9811AE105A00038BC7F /* Supporting Files */, 140 | ); 141 | path = XZDownloadTaskTests; 142 | sourceTree = ""; 143 | }; 144 | F8A6A9811AE105A00038BC7F /* Supporting Files */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | F8A6A9821AE105A00038BC7F /* Info.plist */, 148 | ); 149 | name = "Supporting Files"; 150 | sourceTree = ""; 151 | }; 152 | F8A6A98D1AE49C960038BC7F /* XZDownloadTask */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | F8A6A98E1AE49D540038BC7F /* XZDownloadManager.h */, 156 | F8A6A98F1AE49D540038BC7F /* XZDownloadManager.m */, 157 | F8A6A9911AE4FE670038BC7F /* XZDownloadGroupManager.h */, 158 | F8A6A9921AE4FE670038BC7F /* XZDownloadGroupManager.m */, 159 | F887D78E1AE5EE9600231B16 /* XZDownloadElement.h */, 160 | F887D78F1AE5EE9600231B16 /* XZDownloadElement.m */, 161 | F8A6A9941AE508A50038BC7F /* XZDownloadResponse.h */, 162 | F8A6A9951AE508A50038BC7F /* XZDownloadResponse.m */, 163 | ); 164 | name = XZDownloadTask; 165 | sourceTree = ""; 166 | }; 167 | /* End PBXGroup section */ 168 | 169 | /* Begin PBXNativeTarget section */ 170 | F8A6A9631AE105A00038BC7F /* XZDownloadTask */ = { 171 | isa = PBXNativeTarget; 172 | buildConfigurationList = F8A6A9871AE105A00038BC7F /* Build configuration list for PBXNativeTarget "XZDownloadTask" */; 173 | buildPhases = ( 174 | F8A6A9601AE105A00038BC7F /* Sources */, 175 | F8A6A9611AE105A00038BC7F /* Frameworks */, 176 | F8A6A9621AE105A00038BC7F /* Resources */, 177 | ); 178 | buildRules = ( 179 | ); 180 | dependencies = ( 181 | ); 182 | name = XZDownloadTask; 183 | productName = XZDownloadTask; 184 | productReference = F8A6A9641AE105A00038BC7F /* XZDownloadTask.app */; 185 | productType = "com.apple.product-type.application"; 186 | }; 187 | F8A6A97C1AE105A00038BC7F /* XZDownloadTaskTests */ = { 188 | isa = PBXNativeTarget; 189 | buildConfigurationList = F8A6A98A1AE105A00038BC7F /* Build configuration list for PBXNativeTarget "XZDownloadTaskTests" */; 190 | buildPhases = ( 191 | F8A6A9791AE105A00038BC7F /* Sources */, 192 | F8A6A97A1AE105A00038BC7F /* Frameworks */, 193 | F8A6A97B1AE105A00038BC7F /* Resources */, 194 | ); 195 | buildRules = ( 196 | ); 197 | dependencies = ( 198 | F8A6A97F1AE105A00038BC7F /* PBXTargetDependency */, 199 | ); 200 | name = XZDownloadTaskTests; 201 | productName = XZDownloadTaskTests; 202 | productReference = F8A6A97D1AE105A00038BC7F /* XZDownloadTaskTests.xctest */; 203 | productType = "com.apple.product-type.bundle.unit-test"; 204 | }; 205 | /* End PBXNativeTarget section */ 206 | 207 | /* Begin PBXProject section */ 208 | F8A6A95C1AE105A00038BC7F /* Project object */ = { 209 | isa = PBXProject; 210 | attributes = { 211 | LastUpgradeCheck = 0630; 212 | ORGANIZATIONNAME = anjuke; 213 | TargetAttributes = { 214 | F8A6A9631AE105A00038BC7F = { 215 | CreatedOnToolsVersion = 6.3; 216 | SystemCapabilities = { 217 | com.apple.BackgroundModes = { 218 | enabled = 1; 219 | }; 220 | }; 221 | }; 222 | F8A6A97C1AE105A00038BC7F = { 223 | CreatedOnToolsVersion = 6.3; 224 | TestTargetID = F8A6A9631AE105A00038BC7F; 225 | }; 226 | }; 227 | }; 228 | buildConfigurationList = F8A6A95F1AE105A00038BC7F /* Build configuration list for PBXProject "XZDownloadTask" */; 229 | compatibilityVersion = "Xcode 3.2"; 230 | developmentRegion = English; 231 | hasScannedForEncodings = 0; 232 | knownRegions = ( 233 | en, 234 | Base, 235 | ); 236 | mainGroup = F8A6A95B1AE105A00038BC7F; 237 | productRefGroup = F8A6A9651AE105A00038BC7F /* Products */; 238 | projectDirPath = ""; 239 | projectRoot = ""; 240 | targets = ( 241 | F8A6A9631AE105A00038BC7F /* XZDownloadTask */, 242 | F8A6A97C1AE105A00038BC7F /* XZDownloadTaskTests */, 243 | ); 244 | }; 245 | /* End PBXProject section */ 246 | 247 | /* Begin PBXResourcesBuildPhase section */ 248 | F8A6A9621AE105A00038BC7F /* Resources */ = { 249 | isa = PBXResourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | F8A6A9731AE105A00038BC7F /* Main.storyboard in Resources */, 253 | F8A6A9781AE105A00038BC7F /* LaunchScreen.xib in Resources */, 254 | F8A6A9751AE105A00038BC7F /* Images.xcassets in Resources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | F8A6A97B1AE105A00038BC7F /* Resources */ = { 259 | isa = PBXResourcesBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | /* End PBXResourcesBuildPhase section */ 266 | 267 | /* Begin PBXSourcesBuildPhase section */ 268 | F8A6A9601AE105A00038BC7F /* Sources */ = { 269 | isa = PBXSourcesBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | F887D7931AE6322600231B16 /* XZDownloadView.m in Sources */, 273 | F8A6A9701AE105A00038BC7F /* ViewController.m in Sources */, 274 | F8CDC09B1AEE26EE00B351F7 /* MultDownloadViewController.m in Sources */, 275 | F8A6A96D1AE105A00038BC7F /* AppDelegate.m in Sources */, 276 | F8A6A9961AE508A50038BC7F /* XZDownloadResponse.m in Sources */, 277 | F887D7901AE5EE9600231B16 /* XZDownloadElement.m in Sources */, 278 | F8A6A96A1AE105A00038BC7F /* main.m in Sources */, 279 | F8A6A9931AE4FE670038BC7F /* XZDownloadGroupManager.m in Sources */, 280 | F8CDC0981AEE26D700B351F7 /* SingleDownloadViewController.m in Sources */, 281 | F8A6A9901AE49D540038BC7F /* XZDownloadManager.m in Sources */, 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | }; 285 | F8A6A9791AE105A00038BC7F /* Sources */ = { 286 | isa = PBXSourcesBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | F8A6A9841AE105A00038BC7F /* XZDownloadTaskTests.m in Sources */, 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | }; 293 | /* End PBXSourcesBuildPhase section */ 294 | 295 | /* Begin PBXTargetDependency section */ 296 | F8A6A97F1AE105A00038BC7F /* PBXTargetDependency */ = { 297 | isa = PBXTargetDependency; 298 | target = F8A6A9631AE105A00038BC7F /* XZDownloadTask */; 299 | targetProxy = F8A6A97E1AE105A00038BC7F /* PBXContainerItemProxy */; 300 | }; 301 | /* End PBXTargetDependency section */ 302 | 303 | /* Begin PBXVariantGroup section */ 304 | F8A6A9711AE105A00038BC7F /* Main.storyboard */ = { 305 | isa = PBXVariantGroup; 306 | children = ( 307 | F8A6A9721AE105A00038BC7F /* Base */, 308 | ); 309 | name = Main.storyboard; 310 | sourceTree = ""; 311 | }; 312 | F8A6A9761AE105A00038BC7F /* LaunchScreen.xib */ = { 313 | isa = PBXVariantGroup; 314 | children = ( 315 | F8A6A9771AE105A00038BC7F /* Base */, 316 | ); 317 | name = LaunchScreen.xib; 318 | sourceTree = ""; 319 | }; 320 | /* End PBXVariantGroup section */ 321 | 322 | /* Begin XCBuildConfiguration section */ 323 | F8A6A9851AE105A00038BC7F /* Debug */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 328 | CLANG_CXX_LIBRARY = "libc++"; 329 | CLANG_ENABLE_MODULES = YES; 330 | CLANG_ENABLE_OBJC_ARC = YES; 331 | CLANG_WARN_BOOL_CONVERSION = YES; 332 | CLANG_WARN_CONSTANT_CONVERSION = YES; 333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 338 | CLANG_WARN_UNREACHABLE_CODE = YES; 339 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 340 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 341 | COPY_PHASE_STRIP = NO; 342 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_DYNAMIC_NO_PIC = NO; 346 | GCC_NO_COMMON_BLOCKS = YES; 347 | GCC_OPTIMIZATION_LEVEL = 0; 348 | GCC_PREPROCESSOR_DEFINITIONS = ( 349 | "DEBUG=1", 350 | "$(inherited)", 351 | ); 352 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 353 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 354 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 355 | GCC_WARN_UNDECLARED_SELECTOR = YES; 356 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 357 | GCC_WARN_UNUSED_FUNCTION = YES; 358 | GCC_WARN_UNUSED_VARIABLE = YES; 359 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 360 | MTL_ENABLE_DEBUG_INFO = YES; 361 | ONLY_ACTIVE_ARCH = YES; 362 | SDKROOT = iphoneos; 363 | }; 364 | name = Debug; 365 | }; 366 | F8A6A9861AE105A00038BC7F /* Release */ = { 367 | isa = XCBuildConfiguration; 368 | buildSettings = { 369 | ALWAYS_SEARCH_USER_PATHS = NO; 370 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 371 | CLANG_CXX_LIBRARY = "libc++"; 372 | CLANG_ENABLE_MODULES = YES; 373 | CLANG_ENABLE_OBJC_ARC = YES; 374 | CLANG_WARN_BOOL_CONVERSION = YES; 375 | CLANG_WARN_CONSTANT_CONVERSION = YES; 376 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 377 | CLANG_WARN_EMPTY_BODY = YES; 378 | CLANG_WARN_ENUM_CONVERSION = YES; 379 | CLANG_WARN_INT_CONVERSION = YES; 380 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 381 | CLANG_WARN_UNREACHABLE_CODE = YES; 382 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 383 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 384 | COPY_PHASE_STRIP = NO; 385 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 386 | ENABLE_NS_ASSERTIONS = NO; 387 | ENABLE_STRICT_OBJC_MSGSEND = YES; 388 | GCC_C_LANGUAGE_STANDARD = gnu99; 389 | GCC_NO_COMMON_BLOCKS = YES; 390 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 391 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 392 | GCC_WARN_UNDECLARED_SELECTOR = YES; 393 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 394 | GCC_WARN_UNUSED_FUNCTION = YES; 395 | GCC_WARN_UNUSED_VARIABLE = YES; 396 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 397 | MTL_ENABLE_DEBUG_INFO = NO; 398 | SDKROOT = iphoneos; 399 | VALIDATE_PRODUCT = YES; 400 | }; 401 | name = Release; 402 | }; 403 | F8A6A9881AE105A00038BC7F /* Debug */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 407 | GCC_PREFIX_HEADER = "XZDownloadTask/XZDownload-Prefix.pch"; 408 | INFOPLIST_FILE = XZDownloadTask/Info.plist; 409 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 410 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 411 | PRODUCT_NAME = "$(TARGET_NAME)"; 412 | }; 413 | name = Debug; 414 | }; 415 | F8A6A9891AE105A00038BC7F /* Release */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 419 | GCC_PREFIX_HEADER = "XZDownloadTask/XZDownload-Prefix.pch"; 420 | INFOPLIST_FILE = XZDownloadTask/Info.plist; 421 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 422 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 423 | PRODUCT_NAME = "$(TARGET_NAME)"; 424 | }; 425 | name = Release; 426 | }; 427 | F8A6A98B1AE105A00038BC7F /* Debug */ = { 428 | isa = XCBuildConfiguration; 429 | buildSettings = { 430 | BUNDLE_LOADER = "$(TEST_HOST)"; 431 | FRAMEWORK_SEARCH_PATHS = ( 432 | "$(SDKROOT)/Developer/Library/Frameworks", 433 | "$(inherited)", 434 | ); 435 | GCC_PREPROCESSOR_DEFINITIONS = ( 436 | "DEBUG=1", 437 | "$(inherited)", 438 | ); 439 | INFOPLIST_FILE = XZDownloadTaskTests/Info.plist; 440 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 441 | PRODUCT_NAME = "$(TARGET_NAME)"; 442 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XZDownloadTask.app/XZDownloadTask"; 443 | }; 444 | name = Debug; 445 | }; 446 | F8A6A98C1AE105A00038BC7F /* Release */ = { 447 | isa = XCBuildConfiguration; 448 | buildSettings = { 449 | BUNDLE_LOADER = "$(TEST_HOST)"; 450 | FRAMEWORK_SEARCH_PATHS = ( 451 | "$(SDKROOT)/Developer/Library/Frameworks", 452 | "$(inherited)", 453 | ); 454 | INFOPLIST_FILE = XZDownloadTaskTests/Info.plist; 455 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 456 | PRODUCT_NAME = "$(TARGET_NAME)"; 457 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XZDownloadTask.app/XZDownloadTask"; 458 | }; 459 | name = Release; 460 | }; 461 | /* End XCBuildConfiguration section */ 462 | 463 | /* Begin XCConfigurationList section */ 464 | F8A6A95F1AE105A00038BC7F /* Build configuration list for PBXProject "XZDownloadTask" */ = { 465 | isa = XCConfigurationList; 466 | buildConfigurations = ( 467 | F8A6A9851AE105A00038BC7F /* Debug */, 468 | F8A6A9861AE105A00038BC7F /* Release */, 469 | ); 470 | defaultConfigurationIsVisible = 0; 471 | defaultConfigurationName = Release; 472 | }; 473 | F8A6A9871AE105A00038BC7F /* Build configuration list for PBXNativeTarget "XZDownloadTask" */ = { 474 | isa = XCConfigurationList; 475 | buildConfigurations = ( 476 | F8A6A9881AE105A00038BC7F /* Debug */, 477 | F8A6A9891AE105A00038BC7F /* Release */, 478 | ); 479 | defaultConfigurationIsVisible = 0; 480 | defaultConfigurationName = Release; 481 | }; 482 | F8A6A98A1AE105A00038BC7F /* Build configuration list for PBXNativeTarget "XZDownloadTaskTests" */ = { 483 | isa = XCConfigurationList; 484 | buildConfigurations = ( 485 | F8A6A98B1AE105A00038BC7F /* Debug */, 486 | F8A6A98C1AE105A00038BC7F /* Release */, 487 | ); 488 | defaultConfigurationIsVisible = 0; 489 | defaultConfigurationName = Release; 490 | }; 491 | /* End XCConfigurationList section */ 492 | }; 493 | rootObject = F8A6A95C1AE105A00038BC7F /* Project object */; 494 | } 495 | -------------------------------------------------------------------------------- /XZDownloadTask.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /XZDownloadTask.xcodeproj/project.xcworkspace/xcshareddata/XZDownloadTask.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 96A51C06-2278-49D4-80AF-1B76F23288D3 9 | IDESourceControlProjectName 10 | XZDownloadTask 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | CC863CF76724A8F09812BE326AA9189BE259733E 14 | https://github.com/kingundertree/XZDownloadTask.git 15 | 16 | IDESourceControlProjectPath 17 | XZDownloadTask.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | CC863CF76724A8F09812BE326AA9189BE259733E 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/kingundertree/XZDownloadTask.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | CC863CF76724A8F09812BE326AA9189BE259733E 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | CC863CF76724A8F09812BE326AA9189BE259733E 36 | IDESourceControlWCCName 37 | XZDownloadTask 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /XZDownloadTask.xcodeproj/project.xcworkspace/xcuserdata/xiazer.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingundertree/XZDownloadTask/28382bbe467ba8defc20b51279caebf648066b58/XZDownloadTask.xcodeproj/project.xcworkspace/xcuserdata/xiazer.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /XZDownloadTask.xcodeproj/project.xcworkspace/xcuserdata/xiazer.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges 6 | 7 | SnapshotAutomaticallyBeforeSignificantChanges 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /XZDownloadTask.xcodeproj/xcuserdata/xiazer.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 51 | 52 | 66 | 67 | 68 | 69 | 70 | 72 | 84 | 85 | 86 | 88 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /XZDownloadTask.xcodeproj/xcuserdata/xiazer.xcuserdatad/xcschemes/XZDownloadTask.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /XZDownloadTask.xcodeproj/xcuserdata/xiazer.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | XZDownloadTask.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F8A6A9631AE105A00038BC7F 16 | 17 | primary 18 | 19 | 20 | F8A6A97C1AE105A00038BC7F 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /XZDownloadTask/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/17. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | @property (copy) void (^backgroundURLSessionCompletionHandler)(); 15 | 16 | 17 | @end 18 | 19 | -------------------------------------------------------------------------------- /XZDownloadTask/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/17. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | 22 | // iOS 8 设置系统通知 23 | [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]]; 24 | [[UIApplication sharedApplication] registerForRemoteNotifications]; 25 | 26 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 27 | self.window.backgroundColor = [UIColor blackColor]; 28 | [self.window makeKeyAndVisible]; 29 | 30 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; 31 | self.window.rootViewController = nav; 32 | 33 | return YES; 34 | } 35 | 36 | - (void)applicationWillResignActive:(UIApplication *)application { 37 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 38 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 39 | application.applicationIconBadgeNumber -= 1; 40 | } 41 | 42 | - (void)applicationDidEnterBackground:(UIApplication *)application { 43 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 44 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 45 | } 46 | 47 | - (void)applicationWillEnterForeground:(UIApplication *)application { 48 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 49 | } 50 | 51 | - (void)applicationDidBecomeActive:(UIApplication *)application { 52 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 53 | } 54 | 55 | - (void)applicationWillTerminate:(UIApplication *)application { 56 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 57 | } 58 | 59 | - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification*)notification{ 60 | 61 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"LocalNotification" message:notification.alertBody delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; 62 | [alert show]; 63 | 64 | NSDictionary* dic = [[NSDictionary alloc]init]; 65 | //这里可以接受到本地通知中心发送的消息 66 | dic = notification.userInfo; 67 | NSLog(@"user info = %@",[dic objectForKey:@"key"]); 68 | 69 | // 图标上的数字减1 70 | application.applicationIconBadgeNumber -= 1; 71 | } 72 | 73 | - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler 74 | { 75 | self.backgroundURLSessionCompletionHandler = completionHandler; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /XZDownloadTask/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /XZDownloadTask/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /XZDownloadTask/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /XZDownloadTask/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | anjuke.com.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIBackgroundModes 26 | 27 | fetch 28 | 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /XZDownloadTask/MultDownloadViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultDownloadViewController.h 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/27. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MultDownloadViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /XZDownloadTask/MultDownloadViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultDownloadViewController.m 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/27. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import "MultDownloadViewController.h" 10 | #import "XZDownloadManager.h" 11 | #import "XZDownloadGroupManager.h" 12 | #import "XZDownloadView.h" 13 | 14 | @interface MultDownloadViewController () 15 | @property (nonatomic, strong) XZDownloadManager *downloadManager; 16 | @property (nonatomic, strong) UIProgressView *progressView1; 17 | @property (nonatomic, strong) UIProgressView *progressView2; 18 | @property (nonatomic, strong) UIProgressView *progressView3; 19 | @property (nonatomic, strong) UIProgressView *progressView4; 20 | @property (nonatomic, strong) NSMutableArray *musicIdentifierArr; 21 | @property (nonatomic, strong) NSMutableArray *downloadViewArr; 22 | @end 23 | 24 | @implementation MultDownloadViewController 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | // Do any additional setup after loading the view. 29 | self.title = @"多任务下载"; 30 | self.view.backgroundColor = [UIColor whiteColor]; 31 | 32 | self.musicIdentifierArr = [NSMutableArray array]; 33 | self.downloadViewArr = [NSMutableArray array]; 34 | 35 | for (NSInteger j = 0; j < 4; j++) { 36 | NSString *uniCode = [[NSProcessInfo processInfo] globallyUniqueString]; 37 | [self.musicIdentifierArr addObject:uniCode]; 38 | 39 | XZDownloadView *downloadView = [[XZDownloadView alloc] initWithFrame:CGRectMake(0, 100+60*j, ScreenWidth, 50)]; 40 | [downloadView displayUIWithIdentifier:uniCode 41 | startClick:^(NSString *identifier) { 42 | NSLog(@"start-->>%@",identifier); 43 | [self startClick:identifier]; 44 | } pauseClick:^(NSString *identifier) { 45 | NSLog(@"pause-->>%@",identifier); 46 | [self pauseClick:identifier]; 47 | } resumeClick:^(NSString *identifier) { 48 | NSLog(@"resume-->>%@",identifier); 49 | [self resumeClick:identifier]; 50 | } cancleClick:^(NSString *identifier) { 51 | NSLog(@"cancle-->>%@",identifier); 52 | [self cancleClick:identifier]; 53 | }]; 54 | [self.view addSubview:downloadView]; 55 | [self.downloadViewArr addObject:downloadView]; 56 | } 57 | 58 | for (NSInteger i = 0; i < self.downloadViewArr.count; i++) { 59 | XZDownloadView *downloadView = (XZDownloadView *)[self.downloadViewArr objectAtIndex:i]; 60 | NSLog(@"self.downloadViewArr---->>%@/%@",downloadView,downloadView.identifer); 61 | } 62 | 63 | NSLog(@"%@",self.musicIdentifierArr); 64 | 65 | for (NSInteger i = 0; i < 4; i++) { 66 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 67 | btn.tag = i+100; 68 | [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; 69 | btn.frame = CGRectMake(ScreenWidth*i/4, 350, ScreenWidth/4, 50); 70 | btn.backgroundColor = [UIColor blackColor]; 71 | 72 | NSString *titStr; 73 | if (i == 0) { 74 | titStr = @"全部开始"; 75 | } else if (i == 1) { 76 | titStr = @"全部暂停"; 77 | } else if (i == 2) { 78 | titStr = @"重新下载"; 79 | } else { 80 | titStr = @"全部取消"; 81 | } 82 | [btn setTitle:titStr forState:UIControlStateNormal]; 83 | btn.tintColor = [UIColor whiteColor]; 84 | 85 | [self.view addSubview:btn]; 86 | } 87 | } 88 | 89 | - (void)startClick:(NSString *)identifier { 90 | [self startDownload:[self getDownloadTaskIndex:identifier]]; 91 | } 92 | 93 | - (void)pauseClick:(NSString *)identifier { 94 | [[XZDownloadGroupManager shareInstance] pauseDownload:identifier]; 95 | } 96 | - (void)resumeClick:(NSString *)identifier { 97 | [[XZDownloadGroupManager shareInstance] resumeDownload:identifier]; 98 | } 99 | - (void)cancleClick:(NSString *)identifier { 100 | [[XZDownloadGroupManager shareInstance] cancleDownload:identifier]; 101 | } 102 | 103 | - (NSInteger)getDownloadTaskIndex:(NSString *)identifier { 104 | return [self.musicIdentifierArr indexOfObject:identifier]; 105 | } 106 | 107 | - (void)btnClick:(id)sender { 108 | UIButton *btn = (UIButton *)sender; 109 | NSInteger tag = btn.tag; 110 | 111 | if (tag == 100) { 112 | [self startDownload:-1]; 113 | } else if (tag == 101) { 114 | [[XZDownloadGroupManager shareInstance] pauseAllDownloadRequest]; 115 | } else if (tag == 102) { 116 | [[XZDownloadGroupManager shareInstance] cancleAllDownloadRequest]; 117 | } else { 118 | [[XZDownloadGroupManager shareInstance] resumeAllDownloadRequest]; 119 | } 120 | } 121 | 122 | 123 | - (void)startDownload:(NSInteger)index { 124 | NSString *music1 = @"http://music.baidu.com/data/music/file?link=http://yinyueshiting.baidu.com/data2/music/240885332/124380645248400128.mp3?xcode=b96fdf1351643f39e12afa176a6962aab545e6e71c27bf77&song_id=124380645"; 125 | NSString *music2 = @"http://music.baidu.com/data/music/file?link=http://yinyueshiting.baidu.com/data2/music/239130183/12267411954000128.mp3?xcode=b96fdf1351643f39bc54635511ee1c13c3ba3c8b80064e3b&song_id=122674119"; 126 | NSString *music3 = @"http://music.baidu.com/data/music/file?link=http://yinyueshiting.baidu.com/data2/music/238979065/137078183169200128.mp3?xcode=999cf85ef0a16522698e959dcc59d9b4b3362d258e48ce4b&song_id=137078183"; 127 | NSString *music4 = @"https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/AVFoundationPG/AVFoundationPG.pdf"; 128 | NSArray *musicUrlArr = [NSArray arrayWithObjects:music1,music2,music3,music4, nil]; 129 | 130 | __weak typeof(self) this = self; 131 | 132 | if (index >= 0) { 133 | [[XZDownloadGroupManager shareInstance] addDownloadRequest:[musicUrlArr objectAtIndex:index] 134 | identifier:[self.musicIdentifierArr objectAtIndex:index] 135 | targetSelf:self 136 | showProgress:YES 137 | isDownloadBackground:YES 138 | downloadResponse:^(XZDownloadResponse *response) { 139 | [this handleResponse:response]; 140 | }]; 141 | } else { 142 | for (NSInteger i = 0; i < 4; i++) { 143 | [[XZDownloadGroupManager shareInstance] addDownloadRequest:[musicUrlArr objectAtIndex:i] 144 | identifier:[self.musicIdentifierArr objectAtIndex:i] 145 | targetSelf:self 146 | showProgress:YES 147 | isDownloadBackground:YES 148 | downloadResponse:^(XZDownloadResponse *response) { 149 | [this handleResponse:response]; 150 | }]; 151 | } 152 | } 153 | } 154 | 155 | - (void)handleResponse:(XZDownloadResponse *)response { 156 | if (response.downloadStatus == XZDownloading) { 157 | NSLog(@"下载任务ing%@",response.identifier); 158 | XZDownloadView *downloadView = [self getDownloadView:response.identifier]; 159 | dispatch_async(dispatch_get_main_queue(), ^{ 160 | downloadView.progressV = response.progress; 161 | }); 162 | } else if (response.downloadStatus == XZDownloadSuccuss) { 163 | NSLog(@"下载任务成功%@",response.identifier); 164 | XZDownloadView *downloadView = [self getDownloadView:response.identifier]; 165 | downloadView.progressV = 1.0; 166 | } else if (response.downloadStatus == XZDownloadBackgroudSuccuss) { 167 | NSLog(@"后台下载任务成功%@",response.identifier); 168 | [self showLocalNotification:YES]; 169 | XZDownloadView *downloadView = [self getDownloadView:response.identifier]; 170 | downloadView.progressV = 1.0; 171 | } else if (response.downloadStatus == XZDownloadFail) { 172 | NSLog(@"下载任务失败%@",response.identifier); 173 | [self showLocalNotification:NO]; 174 | } else if (response.downloadStatus == XZDownloadCancle) { 175 | NSLog(@"下载任务取消%@",response.identifier); 176 | } else if (response.downloadStatus == XZDownloadPause) { 177 | NSLog(@"下载任务暂停%@",response.identifier); 178 | } else if (response.downloadStatus == XZDownloadResume) { 179 | NSLog(@"下载任务重启%@",response.identifier); 180 | } 181 | } 182 | 183 | - (XZDownloadView *)getDownloadView:(NSString *)identifier { 184 | XZDownloadView *downloadView; 185 | for (NSInteger i = 0; i < 4; i++) { 186 | XZDownloadView *view = (XZDownloadView *)[self.downloadViewArr objectAtIndex:i]; 187 | if ([view.identifer isEqualToString:identifier]) { 188 | downloadView = view; 189 | 190 | break; 191 | } 192 | } 193 | return downloadView; 194 | } 195 | 196 | - (void)showLocalNotification:(BOOL)downloadSuc { 197 | UILocalNotification *notification = [[UILocalNotification alloc] init]; 198 | if (notification!=nil) { 199 | 200 | NSDate *now=[NSDate new]; 201 | notification.fireDate=[now dateByAddingTimeInterval:6]; //触发通知的时间 202 | notification.repeatInterval = 0; //循环次数,kCFCalendarUnitWeekday一周一次 203 | 204 | notification.timeZone = [NSTimeZone defaultTimeZone]; 205 | notification.soundName = UILocalNotificationDefaultSoundName; 206 | notification.alertBody = downloadSuc ? @"后台下载成功啦" : @"下载失败"; 207 | notification.alertAction = @"打开"; //提示框按钮 208 | notification.hasAction = YES; //是否显示额外的按钮,为no时alertAction消失 209 | notification.applicationIconBadgeNumber = 1; //设置app图标右上角的数字 210 | 211 | //下面设置本地通知发送的消息,这个消息可以接受 212 | NSDictionary* infoDic = [NSDictionary dictionaryWithObject:@"value" forKey:@"key"]; 213 | notification.userInfo = infoDic; 214 | //发送通知 215 | [[UIApplication sharedApplication] scheduleLocalNotification:notification]; 216 | } 217 | } 218 | 219 | - (XZDownloadManager *)downloadManager { 220 | if (!_downloadManager) { 221 | _downloadManager = [[XZDownloadManager alloc] init]; 222 | } 223 | 224 | return _downloadManager; 225 | } 226 | 227 | - (void)didReceiveMemoryWarning { 228 | [super didReceiveMemoryWarning]; 229 | // Dispose of any resources that can be recreated. 230 | } 231 | 232 | @end 233 | -------------------------------------------------------------------------------- /XZDownloadTask/SingleDownloadViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SingleDownloadViewController.h 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/27. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SingleDownloadViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /XZDownloadTask/SingleDownloadViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SingleDownloadViewController.m 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/27. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import "SingleDownloadViewController.h" 10 | #import "XZDownloadManager.h" 11 | 12 | //定义屏幕高度 13 | #define ScreenHeight [UIScreen mainScreen].bounds.size.height 14 | //定义屏幕宽度 15 | #define ScreenWidth [UIScreen mainScreen].bounds.size.width 16 | 17 | 18 | @interface SingleDownloadViewController () 19 | @property (nonatomic, strong) XZDownloadManager *downloadManager; 20 | @property (nonatomic, strong) UILabel *progressLab; 21 | @property (nonatomic, assign) float downloadProgress; 22 | @end 23 | 24 | @implementation SingleDownloadViewController 25 | 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | // Do any additional setup after loading the view. 30 | 31 | self.title = @"单任务下载"; 32 | self.view.backgroundColor = [UIColor whiteColor]; 33 | 34 | for (NSInteger i = 0; i < 4; i++) { 35 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 36 | btn.tag = i; 37 | [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; 38 | btn.frame = CGRectMake(0, 100+60*i, ScreenWidth, 50); 39 | btn.backgroundColor = [UIColor blackColor]; 40 | 41 | NSString *titStr; 42 | if (i == 0) { 43 | titStr = @"开始"; 44 | } else if (i == 1) { 45 | titStr = @"暂停"; 46 | } else if (i == 2) { 47 | titStr = @"重新下载"; 48 | } else { 49 | titStr = @"取消"; 50 | } 51 | [btn setTitle:titStr forState:UIControlStateNormal]; 52 | btn.tintColor = [UIColor whiteColor]; 53 | 54 | 55 | [self.view addSubview:btn]; 56 | } 57 | 58 | self.progressLab = [[UILabel alloc] initWithFrame:CGRectMake(0, 400, ScreenWidth, 50)]; 59 | self.progressLab.backgroundColor = [UIColor blackColor]; 60 | self.progressLab.textAlignment = NSTextAlignmentCenter; 61 | self.progressLab.textColor = [UIColor redColor]; 62 | [self.view addSubview:self.progressLab]; 63 | } 64 | 65 | - (void)btnClick:(id)sender { 66 | UIButton *btn = (UIButton *)sender; 67 | NSInteger tag = btn.tag; 68 | 69 | switch (tag) { 70 | case 0: 71 | [self startDownload]; 72 | break; 73 | case 1: 74 | [self pauseDownload]; 75 | break; 76 | case 2: 77 | [self resumeDownload]; 78 | break; 79 | case 3: 80 | [self cancleDownload]; 81 | break; 82 | 83 | default: 84 | break; 85 | } 86 | } 87 | 88 | - (void)startDownload { 89 | __weak typeof(self) this = self; 90 | 91 | NSString *imgStr = @"https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/AVFoundationPG/AVFoundationPG.pdf"; 92 | 93 | 94 | [self.downloadManager configDownloadInfo:imgStr 95 | isDownloadBackground:YES 96 | identifier:nil 97 | succuss:^(XZDownloadResponse *response) { 98 | NSLog(@"下载成功"); 99 | } fail:^(XZDownloadResponse *response) { 100 | NSLog(@"下载失败"); 101 | } progress:^(XZDownloadResponse *response) { 102 | this.downloadProgress = response.progress; 103 | 104 | NSString *proStr = [NSString stringWithFormat:@"%0.2f %@",response.progress*100,@"%"]; 105 | NSLog(@"下载进度---%@",proStr); 106 | 107 | [this updateProgress]; 108 | } cancle:^(XZDownloadResponse *response) { 109 | 110 | } pause:^(XZDownloadResponse *response) { 111 | 112 | } resume:^(XZDownloadResponse *response) { 113 | 114 | }]; 115 | 116 | } 117 | 118 | - (void)updateProgress { 119 | dispatch_async(dispatch_get_main_queue(), ^{ 120 | self.progressLab.text = [NSString stringWithFormat:@"%0.2f",self.downloadProgress]; 121 | }); 122 | } 123 | 124 | - (void)pauseDownload { 125 | [self.downloadManager pauseDownload]; 126 | } 127 | 128 | - (void)resumeDownload { 129 | [self.downloadManager resumeDownload]; 130 | } 131 | 132 | - (void)cancleDownload { 133 | [self.downloadManager cancleDownload]; 134 | } 135 | 136 | - (void)showLocalNotification:(BOOL)downloadSuc { 137 | UILocalNotification *notification = [[UILocalNotification alloc] init]; 138 | if (notification!=nil) { 139 | 140 | NSDate *now=[NSDate new]; 141 | notification.fireDate=[now dateByAddingTimeInterval:6]; //触发通知的时间 142 | notification.repeatInterval = 0; //循环次数,kCFCalendarUnitWeekday一周一次 143 | 144 | notification.timeZone = [NSTimeZone defaultTimeZone]; 145 | notification.soundName = UILocalNotificationDefaultSoundName; 146 | notification.alertBody = downloadSuc ? @"后台下载成功啦" : @"下载失败"; 147 | notification.alertAction = @"打开"; //提示框按钮 148 | notification.hasAction = YES; //是否显示额外的按钮,为no时alertAction消失 149 | notification.applicationIconBadgeNumber = 1; //设置app图标右上角的数字 150 | 151 | //下面设置本地通知发送的消息,这个消息可以接受 152 | NSDictionary* infoDic = [NSDictionary dictionaryWithObject:@"value" forKey:@"key"]; 153 | notification.userInfo = infoDic; 154 | //发送通知 155 | [[UIApplication sharedApplication] scheduleLocalNotification:notification]; 156 | } 157 | } 158 | 159 | - (XZDownloadManager *)downloadManager { 160 | if (!_downloadManager) { 161 | _downloadManager = [[XZDownloadManager alloc] init]; 162 | } 163 | 164 | return _downloadManager; 165 | } 166 | 167 | 168 | - (void)didReceiveMemoryWarning { 169 | [super didReceiveMemoryWarning]; 170 | // Dispose of any resources that can be recreated. 171 | } 172 | 173 | @end 174 | -------------------------------------------------------------------------------- /XZDownloadTask/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/17. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /XZDownloadTask/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/17. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "SingleDownloadViewController.h" 11 | #import "MultDownloadViewController.h" 12 | 13 | ////定义屏幕高度 14 | //#define ScreenHeight [UIScreen mainScreen].bounds.size.height 15 | ////定义屏幕宽度 16 | //#define ScreenWidth [UIScreen mainScreen].bounds.size.width 17 | 18 | @interface ViewController () 19 | @end 20 | 21 | @implementation ViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | // Do any additional setup after loading the view, typically from a nib. 26 | self.title = @"后台下载Demo"; 27 | self.view.backgroundColor = [UIColor whiteColor]; 28 | 29 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 30 | btn.frame = CGRectMake(0, 100, ScreenWidth, 40); 31 | [btn addTarget:self action:@selector(singleDownloadBtnClick:) forControlEvents:UIControlEventTouchUpInside]; 32 | btn.backgroundColor = [UIColor blackColor]; 33 | NSString *titStr = @"单任务下载"; 34 | [btn setTitle:titStr forState:UIControlStateNormal]; 35 | btn.tintColor = [UIColor whiteColor]; 36 | [self.view addSubview:btn]; 37 | 38 | UIButton *btnMult = [UIButton buttonWithType:UIButtonTypeCustom]; 39 | btnMult.frame = CGRectMake(0, 160, ScreenWidth, 40); 40 | [btnMult addTarget:self action:@selector(multDownloadBtnClick:) forControlEvents:UIControlEventTouchUpInside]; 41 | btnMult.backgroundColor = [UIColor blackColor]; 42 | NSString *titStrMult = @"多任务下载"; 43 | [btnMult setTitle:titStrMult forState:UIControlStateNormal]; 44 | btnMult.tintColor = [UIColor whiteColor]; 45 | [self.view addSubview:btnMult]; 46 | } 47 | 48 | - (void)singleDownloadBtnClick:(id)sender { 49 | SingleDownloadViewController *vc = [[SingleDownloadViewController alloc] init]; 50 | [self.navigationController pushViewController:vc animated:YES]; 51 | } 52 | 53 | - (void)multDownloadBtnClick:(id)sender { 54 | MultDownloadViewController *vc = [[MultDownloadViewController alloc] init]; 55 | [self.navigationController pushViewController:vc animated:YES]; 56 | } 57 | 58 | - (void)didReceiveMemoryWarning { 59 | [super didReceiveMemoryWarning]; 60 | // Dispose of any resources that can be recreated. 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownload-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownload-Prefix.pch 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/21. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #ifndef XZDownloadTask_XZDownload_Prefix_pch 10 | 11 | //定义屏幕高度 12 | #define ScreenHeight [UIScreen mainScreen].bounds.size.height 13 | //定义屏幕宽度 14 | #define ScreenWidth [UIScreen mainScreen].bounds.size.width 15 | 16 | 17 | #define XZDownloadTask_XZDownload_Prefix_pch 18 | 19 | // Include any system framework and library headers here that should be included in all compilation units. 20 | // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownloadElement.h: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadElement.h 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/21. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "XZDownloadResponse.h" 11 | 12 | @interface XZDownloadElement : NSObject 13 | 14 | @property (nonatomic, strong) NSString *identifier; 15 | @property (nonatomic, strong) id targert; 16 | @property (nonatomic, strong) NSString *action; 17 | @property (nonatomic, copy) void(^downloadResponse)(XZDownloadResponse *response); 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownloadElement.m: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadElement.m 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/21. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import "XZDownloadElement.h" 10 | 11 | @implementation XZDownloadElement 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownloadGroupManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadGroupManager.h 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/20. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "XZDownloadResponse.h" 11 | 12 | @interface XZDownloadGroupManager : NSObject 13 | 14 | typedef void(^downloadResponse)(XZDownloadResponse *response); 15 | 16 | + (id)shareInstance; 17 | 18 | // 下载请求 19 | - (void)addDownloadRequest:(NSString *)downloadStr 20 | identifier:(NSString *)identifier 21 | targetSelf:(id)targetSelf 22 | showProgress:(BOOL)showProgress 23 | isDownloadBackground:(BOOL)isDownloadBackground 24 | downloadResponse:(void(^)(XZDownloadResponse *response))downloadResponse; 25 | 26 | // 所有下载任务控制 27 | - (void)pauseAllDownloadRequest; 28 | - (void)cancleAllDownloadRequest; 29 | - (void)resumeAllDownloadRequest; 30 | 31 | // 单个下载任务控制 32 | - (void)pauseDownload:(NSString *)identifier; 33 | - (void)resumeDownload:(NSString *)identifier; 34 | - (void)cancleDownload:(NSString *)identifier; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownloadGroupManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadGroupManager.m 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/20. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import "XZDownloadGroupManager.h" 10 | #import "XZDownloadManager.h" 11 | #import "XZDownloadElement.h" 12 | 13 | @interface XZDownloadGroupManager () 14 | @property (nonatomic, strong) NSMutableArray *downloadManagerArr; 15 | @property (nonatomic, strong) NSMutableArray *downloadElementArr; 16 | @property (nonatomic, copy) void(^downloadResponse)(XZDownloadResponse *response); 17 | @end 18 | 19 | @implementation XZDownloadGroupManager 20 | 21 | - (instancetype)init { 22 | self = [super init]; 23 | if (self) { 24 | [self initData]; 25 | } 26 | 27 | return self; 28 | } 29 | 30 | + (instancetype)shareInstance { 31 | static XZDownloadGroupManager *downloadGroupManager; 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | downloadGroupManager = [[XZDownloadGroupManager alloc] init]; 35 | }); 36 | return downloadGroupManager; 37 | } 38 | 39 | - (void)initData { 40 | self.downloadManagerArr = [NSMutableArray array]; 41 | } 42 | 43 | #pragma mark - 下载请求 44 | - (void)addDownloadRequest:(NSString *)downloadStr 45 | identifier:(NSString *)identifier 46 | targetSelf:(id)targetSelf 47 | showProgress:(BOOL)showProgress 48 | isDownloadBackground:(BOOL)isDownloadBackground 49 | downloadResponse:(void(^)(XZDownloadResponse *response))downloadResponse { 50 | self.downloadResponse = downloadResponse; 51 | 52 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 53 | __weak typeof(self) this = self; 54 | XZDownloadManager *downloadManager = [[XZDownloadManager alloc] init]; 55 | [downloadManager configDownloadInfo:downloadStr 56 | isDownloadBackground:isDownloadBackground 57 | identifier:identifier 58 | succuss:^(XZDownloadResponse *response) { 59 | [this downloadSuccuss:response]; 60 | } fail:^(XZDownloadResponse *response) { 61 | [this downloadFail:response]; 62 | } progress:^(XZDownloadResponse *response) { 63 | if (showProgress) { 64 | [self downloadIng:response]; 65 | } 66 | } cancle:^(XZDownloadResponse *response) { 67 | [self downloadCancle:response]; 68 | } pause:^(XZDownloadResponse *response) { 69 | [self downloadPause:response]; 70 | } resume:^(XZDownloadResponse *response) { 71 | [self downloadResume:response]; 72 | }]; 73 | 74 | [self.downloadManagerArr addObject:downloadManager]; 75 | }); 76 | } 77 | #pragma mark - 下载基本方法,批量任务处理 78 | - (void)pauseAllDownloadRequest { 79 | [self.downloadManagerArr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 80 | XZDownloadManager *downloadManager = (XZDownloadManager *)obj; 81 | [downloadManager pauseDownload]; 82 | }]; 83 | } 84 | 85 | - (void)cancleAllDownloadRequest { 86 | __weak typeof(self) this = self; 87 | [self.downloadManagerArr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 88 | XZDownloadManager *downloadManager = (XZDownloadManager *)obj; 89 | [downloadManager cancleDownload]; 90 | 91 | NSString *identifier = downloadManager.identifier; 92 | [this removeDownloadTask:identifier]; 93 | }]; 94 | } 95 | 96 | - (void)resumeAllDownloadRequest { 97 | [self.downloadManagerArr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 98 | XZDownloadManager *downloadManager = (XZDownloadManager *)obj; 99 | [downloadManager resumeDownload]; 100 | }]; 101 | } 102 | 103 | 104 | #pragma mark - 下载成功失败进度处理,下载基本方法,暂停、重启、取消 105 | - (void)downloadSuccuss:(XZDownloadResponse *)response { 106 | self.downloadResponse(response); 107 | 108 | [self removeDownloadTask:response.identifier]; 109 | } 110 | 111 | - (void)downloadFail:(XZDownloadResponse *)response { 112 | self.downloadResponse(response); 113 | 114 | [self removeDownloadTask:response.identifier]; 115 | } 116 | 117 | - (void)downloadCancle:(XZDownloadResponse *)response { 118 | self.downloadResponse(response); 119 | } 120 | 121 | - (void)downloadPause:(XZDownloadResponse *)response { 122 | self.downloadResponse(response); 123 | } 124 | 125 | - (void)downloadResume:(XZDownloadResponse *)response { 126 | self.downloadResponse(response); 127 | } 128 | 129 | #pragma mark - 下载基本方法,暂停、重启、取消 130 | - (void)pauseDownload:(NSString *)identifier { 131 | XZDownloadManager *downloadManager = [self getDownloadManager:identifier]; 132 | [downloadManager pauseDownload]; 133 | } 134 | 135 | - (void)resumeDownload:(NSString *)identifier { 136 | XZDownloadManager *downloadManager = [self getDownloadManager:identifier]; 137 | [downloadManager resumeDownload]; 138 | } 139 | 140 | - (void)cancleDownload:(NSString *)identifier { 141 | XZDownloadManager *downloadManager = [self getDownloadManager:identifier]; 142 | [downloadManager cancleDownload]; 143 | 144 | [self removeDownloadTask:identifier]; 145 | } 146 | 147 | 148 | #pragma mark - 下载类管理 149 | - (void)addDownloadRequestElementWith:(NSString *)identifier targetSelf:(id)targetSelf downloadResponse:(void(^)(XZDownloadResponse *response))downloadResponse { 150 | 151 | XZDownloadElement *element = [[XZDownloadElement alloc] init]; 152 | element.identifier = identifier; 153 | element.targert = targetSelf; 154 | element.downloadResponse = downloadResponse; 155 | 156 | [self.downloadElementArr addObject:[NSDictionary dictionaryWithObjectsAndKeys:element,identifier, nil]]; 157 | } 158 | 159 | 160 | - (void)downloadIng:(XZDownloadResponse *)response { 161 | self.downloadResponse(response); 162 | } 163 | 164 | - (XZDownloadManager *)getDownloadManager:(NSString *)identifier { 165 | for (NSInteger i = 0; i < self.downloadManagerArr.count; i++) { 166 | XZDownloadManager *downloadManager = [self.downloadManagerArr objectAtIndex:i]; 167 | if ([downloadManager.identifier isEqualToString:identifier]) { 168 | return downloadManager; 169 | } 170 | } 171 | return nil; 172 | } 173 | 174 | - (XZDownloadElement *)getDownloadElement:(NSString *)identifier { 175 | XZDownloadElement *downloadElement; 176 | 177 | [self.downloadElementArr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 178 | NSDictionary *downloadElement = (NSDictionary *)obj; 179 | if ([downloadElement[@"identifier"] isEqualToString:identifier]) { 180 | downloadElement = [self.downloadElementArr objectAtIndex:idx]; 181 | 182 | *stop = YES; 183 | } 184 | }]; 185 | 186 | return downloadElement; 187 | } 188 | 189 | #pragma mark - 删除下载任务 190 | - (void)removeDownloadTask:(NSString *)identifier { 191 | if (!self.downloadManagerArr && self.downloadManagerArr.count == 0) { 192 | return; 193 | } 194 | if (!self.downloadElementArr && self.downloadElementArr.count == 0) { 195 | return; 196 | } 197 | 198 | __weak typeof(self) this = self; 199 | // 删除下载任务 200 | [self.downloadManagerArr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 201 | NSDictionary *downloadManager = (NSDictionary *)obj; 202 | if ([downloadManager[@"identifier"] isEqualToString:identifier]) { 203 | [this.downloadManagerArr removeObjectAtIndex:idx]; 204 | *stop = YES; 205 | } 206 | }]; 207 | 208 | //删除下载对应的element 209 | [self.downloadElementArr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 210 | NSDictionary *downloadElement = (NSDictionary *)obj; 211 | if ([downloadElement[@"identifier"] isEqualToString:identifier]) { 212 | [this.downloadElementArr removeObjectAtIndex:idx]; 213 | *stop = YES; 214 | } 215 | }]; 216 | } 217 | 218 | @end 219 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownloadManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadManager.h 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/20. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "XZDownloadResponse.h" 11 | 12 | typedef void(^downloadSuccuss)(XZDownloadResponse *response); 13 | typedef void(^downloadFail)(XZDownloadResponse *response); 14 | typedef void(^downloadProgress)(XZDownloadResponse *response); 15 | typedef void(^downloadCancle)(XZDownloadResponse *response); 16 | typedef void(^downloadPause)(XZDownloadResponse *response); 17 | typedef void(^downloadResume)(XZDownloadResponse *response); 18 | 19 | @interface XZDownloadManager : NSObject 20 | 21 | @property (nonatomic, strong) NSString *identifier; 22 | 23 | - (void)configDownloadInfo:(NSString *) downloadStr 24 | isDownloadBackground:(BOOL)isDownloadBackground 25 | identifier:(NSString *)identifier 26 | succuss:(void (^)(XZDownloadResponse *response)) succuss 27 | fail:(void(^)(XZDownloadResponse *response)) fail 28 | progress:(void(^)(XZDownloadResponse *response)) progress 29 | cancle:(void(^)(XZDownloadResponse *response)) cancle 30 | pause:(void(^)(XZDownloadResponse *response)) pause 31 | resume:(void(^)(XZDownloadResponse *response)) resume; 32 | 33 | - (void)pauseDownload; 34 | - (void)resumeDownload; 35 | - (void)cancleDownload; 36 | @end 37 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownloadManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadManager.m 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/20. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import "XZDownloadManager.h" 10 | #import "AppDelegate.h" 11 | 12 | @interface XZDownloadManager () 13 | @property (nonatomic, strong) NSURLSession *backgroundSession; 14 | @property (nonatomic, strong) NSURLSessionDownloadTask *backgroundSessionTask; 15 | 16 | @property (nonatomic, strong) NSURLSession *normalSession; 17 | @property (nonatomic, strong) NSURLSessionDownloadTask *normalSessionTask; 18 | @property (nonatomic, strong) NSURLSessionDownloadTask *resumeSessionTask; 19 | @property (nonatomic, strong) NSData *partialData; 20 | @property (nonatomic, copy) void(^downloadSuccuss)(XZDownloadResponse *response); 21 | @property (nonatomic, copy) void(^downloadFail)(XZDownloadResponse *response); 22 | @property (nonatomic, copy) void(^downloadProgress)(XZDownloadResponse *response); 23 | @property (nonatomic, copy) void(^downloadCancle)(XZDownloadResponse *response); 24 | @property (nonatomic, copy) void(^downloadPause)(XZDownloadResponse *response); 25 | @property (nonatomic, copy) void(^downloadResume)(XZDownloadResponse *response); 26 | @property (nonatomic, assign) double lastProgress; 27 | @property (nonatomic, strong) XZDownloadResponse *downloadResponse; 28 | @end 29 | 30 | @implementation XZDownloadManager 31 | 32 | 33 | - (instancetype)init { 34 | self = [super init]; 35 | if (self) { 36 | self.backgroundSession.sessionDescription = @"XZDownloadUrlSession"; 37 | } 38 | 39 | return self; 40 | } 41 | 42 | - (NSURLSession *)getBackgroundSession:(NSString *)identifier { 43 | NSURLSession *backgroundSession = nil; 44 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:[NSString stringWithFormat:@"background-NSURLSession-%@",identifier]]; 45 | config.HTTPMaximumConnectionsPerHost = 5; 46 | backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; 47 | 48 | return backgroundSession; 49 | } 50 | 51 | 52 | - (NSURLSession *)normalSession { 53 | if (!_normalSession) { 54 | NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; 55 | _normalSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil]; 56 | _normalSession.sessionDescription = @"normal NSURLSession"; 57 | } 58 | 59 | return _normalSession; 60 | } 61 | 62 | - (void)configDownloadInfo:(NSString *) downloadStr isDownloadBackground:(BOOL)isDownloadBackground identifier:(NSString *)identifier succuss:(void (^)(XZDownloadResponse *response)) succuss fail:(void(^)(XZDownloadResponse *response)) fail progress:(void(^)(XZDownloadResponse *response)) progress cancle:(void(^)(XZDownloadResponse *response)) cancle pause:(void(^)(XZDownloadResponse *response)) pause resume:(void(^)(XZDownloadResponse *response)) resume{ 63 | self.downloadSuccuss = succuss; 64 | self.downloadFail = fail; 65 | self.downloadProgress = progress; 66 | self.downloadCancle = cancle; 67 | self.downloadPause = pause; 68 | self.downloadResume = resume; 69 | 70 | self.identifier = identifier ? identifier : [[NSProcessInfo processInfo] globallyUniqueString]; 71 | 72 | if (isDownloadBackground) { 73 | [self startBackgroundDownload:downloadStr identifier:self.identifier]; 74 | } else { 75 | [self startNormalDownload:downloadStr]; 76 | } 77 | } 78 | 79 | - (void)startBackgroundDownload:(NSString *)downloadStr identifier:(NSString *)identifier { 80 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadStr]]; 81 | self.backgroundSession = [self getBackgroundSession:identifier]; 82 | self.backgroundSessionTask = [self.backgroundSession downloadTaskWithRequest:request]; 83 | [self.backgroundSessionTask resume]; 84 | } 85 | 86 | - (void)startNormalDownload:(NSString *)downloadStr { 87 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadStr]]; 88 | self.normalSessionTask = [self.normalSession downloadTaskWithRequest:request]; 89 | [self.normalSessionTask resume]; 90 | } 91 | 92 | #pragma mark - 下载任务控制,暂停、重启、取消 93 | - (void)pauseDownload { 94 | __weak typeof(self) this = self; 95 | if (self.normalSessionTask) { 96 | [self.normalSessionTask cancelByProducingResumeData:^(NSData *resumeData) { 97 | this.partialData = resumeData; 98 | this.normalSessionTask = nil; 99 | }]; 100 | } else if (self.backgroundSessionTask) { 101 | [self.backgroundSessionTask cancelByProducingResumeData:^(NSData *resumeData) { 102 | this.partialData = resumeData; 103 | }]; 104 | } 105 | 106 | self.downloadPause([self getDownloadRespose:XZDownloadPause identifier:self.identifier progress:self.lastProgress downloadUrl:nil downloadSaveFileUrl:nil downloadData:nil downloadResult:@"任务暂停"]); 107 | } 108 | 109 | - (void)resumeDownload { 110 | if (!self.resumeSessionTask) { 111 | if (self.partialData) { 112 | self.resumeSessionTask = [self.normalSession downloadTaskWithResumeData:self.partialData]; 113 | 114 | [self.resumeSessionTask resume]; 115 | } else { 116 | self.downloadFail([self getDownloadRespose:XZDownloadFail identifier:self.identifier progress:0.00 downloadUrl:nil downloadSaveFileUrl:nil downloadData:nil downloadResult:@"没有需要恢复的任务"]); 117 | } 118 | } else { 119 | self.downloadFail([self getDownloadRespose:XZDownloadFail identifier:self.identifier progress:0.00 downloadUrl:nil downloadSaveFileUrl:nil downloadData:nil downloadResult:@"没有需要恢复的任务"]); 120 | } 121 | self.downloadResume([self getDownloadRespose:XZDownloadResume identifier:self.identifier progress:self.lastProgress downloadUrl:nil downloadSaveFileUrl:nil downloadData:nil downloadResult:@"任务重启"]); 122 | } 123 | 124 | - (void)cancleDownload { 125 | if (self.normalSessionTask) { 126 | [self.normalSessionTask cancel]; 127 | self.normalSessionTask = nil; 128 | } else if (self.resumeSessionTask) { 129 | self.partialData = nil; 130 | [self.resumeSessionTask cancel]; 131 | self.resumeSessionTask = nil; 132 | } else if (self.backgroundSessionTask) { 133 | [self.backgroundSessionTask cancel]; 134 | self.backgroundSessionTask = nil; 135 | } 136 | 137 | self.downloadCancle([self getDownloadRespose:XZDownloadCancle identifier:self.identifier progress:self.lastProgress downloadUrl:nil downloadSaveFileUrl:nil downloadData:nil downloadResult:@"任务取消"]); 138 | } 139 | 140 | - (XZDownloadResponse *)downloadResponse { 141 | if (!_downloadResponse) { 142 | _downloadResponse = [[XZDownloadResponse alloc] init]; 143 | } 144 | 145 | return _downloadResponse; 146 | } 147 | 148 | - (XZDownloadResponse *)getDownloadRespose:(XZDownloadStatus)status identifier:(NSString *)identifier progress:(double)progress downloadUrl:(NSString *)downloadUrl downloadSaveFileUrl:(NSURL *)downloadSaveFileUrl downloadData:(NSData *)downloadData downloadResult:(NSString *)downloadResult { 149 | self.downloadResponse.downloadStatus = status; 150 | self.downloadResponse.identifier = identifier; 151 | self.downloadResponse.progress = progress; 152 | self.downloadResponse.downloadUrl = downloadUrl; 153 | self.downloadResponse.downloadSaveFileUrl = downloadSaveFileUrl; 154 | self.downloadResponse.downloadData = downloadData; 155 | self.downloadResponse.downloadResult = downloadResult; 156 | 157 | 158 | return self.downloadResponse; 159 | }; 160 | 161 | 162 | #pragma mark - NSURLSessionDownloadDelegate methods 163 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 164 | { 165 | double currentProgress = totalBytesWritten / (double)totalBytesExpectedToWrite; 166 | NSLog(@"%@---%0.2f",self.identifier,currentProgress); 167 | 168 | if (currentProgress >= self.lastProgress+0.05 || currentProgress == 1.00 || currentProgress == 0) { 169 | self.lastProgress = currentProgress; 170 | self.downloadProgress([self getDownloadRespose:XZDownloading identifier:self.identifier progress:currentProgress downloadUrl:nil downloadSaveFileUrl:nil downloadData:nil downloadResult:@"下载中"]); 171 | } 172 | } 173 | 174 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes 175 | { 176 | // 下载失败 177 | self.downloadFail([self getDownloadRespose:XZDownloadFail identifier:self.identifier progress:0.00 downloadUrl:nil downloadSaveFileUrl:nil downloadData:nil downloadResult:@"下载失败"]); 178 | } 179 | 180 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location 181 | { 182 | // We've successfully finished the download. Let's save the file 183 | NSFileManager *fileManager = [NSFileManager defaultManager]; 184 | 185 | NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; 186 | NSURL *documentsDirectory = URLs[0]; 187 | 188 | NSURL *destinationPath = [documentsDirectory URLByAppendingPathComponent:self.identifier]; 189 | NSError *error; 190 | 191 | // Make sure we overwrite anything that's already there 192 | [fileManager removeItemAtURL:destinationPath error:NULL]; 193 | BOOL success = [fileManager copyItemAtURL:location toURL:destinationPath error:&error]; 194 | 195 | if (success) { 196 | dispatch_async(dispatch_get_main_queue(), ^{ 197 | // 此处可更新UI 198 | }); 199 | self.downloadSuccuss([self getDownloadRespose:XZDownloadSuccuss identifier:self.identifier progress:1.00 downloadUrl:nil downloadSaveFileUrl:destinationPath downloadData:nil downloadResult:@"下载成功"]); 200 | } else { 201 | NSLog(@"Couldn't copy the downloaded file"); 202 | self.downloadFail([self getDownloadRespose:XZDownloadFail identifier:self.identifier progress:0.00 downloadUrl:nil downloadSaveFileUrl:destinationPath downloadData:nil downloadResult:@"下载失败"]); 203 | } 204 | 205 | if(downloadTask == self.normalSessionTask) { 206 | self.normalSessionTask = nil; 207 | } else if (downloadTask == self.resumeSessionTask) { 208 | self.resumeSessionTask = nil; 209 | self.partialData = nil; 210 | } else if (session == self.backgroundSession) { 211 | self.backgroundSessionTask = nil; 212 | 213 | AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 214 | if(appDelegate.backgroundURLSessionCompletionHandler) { 215 | void (^handler)() = appDelegate.backgroundURLSessionCompletionHandler; 216 | appDelegate.backgroundURLSessionCompletionHandler = nil; 217 | handler(); 218 | 219 | NSLog(@"后台下载完成"); 220 | } 221 | 222 | self.downloadSuccuss([self getDownloadRespose:XZDownloadBackgroudSuccuss identifier:self.identifier progress:1.00 downloadUrl:nil downloadSaveFileUrl:destinationPath downloadData:nil downloadResult:@"后台下载下载成功"]); 223 | } 224 | } 225 | 226 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 227 | { 228 | // 后台处理下载任务 229 | dispatch_async(dispatch_get_main_queue(), ^{ 230 | }); 231 | } 232 | 233 | 234 | @end 235 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownloadResponse.h: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadResponse.h 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/20. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM (NSInteger, XZDownloadStatus) { 12 | XZDownloadSuccuss, // 下载成功 13 | XZDownloadBackgroudSuccuss, // 下载成功 14 | XZDownloading, // 下载中 15 | XZDownloadFail, // 下载失败 16 | XZDownloadResume, // 重启 17 | XZDownloadCancle, // 取消 18 | XZDownloadPause // 暂停 19 | }; 20 | 21 | @interface XZDownloadResponse : NSObject 22 | 23 | @property (nonatomic, strong) NSString *identifier; 24 | @property (nonatomic, assign) XZDownloadStatus downloadStatus; 25 | @property (nonatomic, strong) id targert; 26 | @property (nonatomic, strong) NSString *action; 27 | @property (nonatomic, assign) double progress; 28 | @property (nonatomic, strong) NSString *downloadUrl; 29 | @property (nonatomic, strong) NSURL *downloadSaveFileUrl; 30 | @property (nonatomic, strong) NSData *downloadData; 31 | @property (nonatomic, strong) NSString *downloadResult; 32 | 33 | 34 | @end 35 | 36 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownloadResponse.m: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadResponse.m 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/20. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import "XZDownloadResponse.h" 10 | 11 | @implementation XZDownloadResponse 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownloadView.h: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadView.h 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/21. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XZDownloadView : UIView 12 | @property (nonatomic, strong) NSString *identifer; 13 | @property (nonatomic, assign) float progressV; 14 | 15 | - (void)displayUIWithIdentifier:(NSString *)identifier 16 | startClick:(void(^)(NSString *identifier))startClick 17 | pauseClick:(void(^)(NSString *identifier))pauseClick 18 | resumeClick:(void(^)(NSString *identifier))resumeClick 19 | cancleClick:(void(^)(NSString *identifier))cancleClick; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /XZDownloadTask/XZDownloadView.m: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadView.m 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/21. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import "XZDownloadView.h" 10 | 11 | @interface XZDownloadView () 12 | @property (nonatomic, copy) void(^startClick)(NSString *identifier); 13 | @property (nonatomic, copy) void(^pauseClick)(NSString *identifier); 14 | @property (nonatomic, copy) void(^resumeClick)(NSString *identifier); 15 | @property (nonatomic, copy) void(^cancleClick)(NSString *identifier); 16 | @property (nonatomic, strong) UIProgressView *showProgressView; 17 | @end 18 | 19 | @implementation XZDownloadView 20 | 21 | - (instancetype)init { 22 | self = [super init]; 23 | if (self) { 24 | 25 | } 26 | 27 | return self; 28 | } 29 | 30 | - (void)drawRect:(CGRect)rect { 31 | for (NSInteger i = 0; i < 4; i++) { 32 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 33 | btn.tag = i; 34 | [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; 35 | btn.frame = CGRectMake(ScreenWidth*i/4, 0, ScreenWidth/4, 50); 36 | btn.backgroundColor = [UIColor blackColor]; 37 | 38 | NSString *titStr; 39 | if (i == 0) { 40 | titStr = @"开始"; 41 | } else if (i == 1) { 42 | titStr = @"暂停"; 43 | } else if (i == 2) { 44 | titStr = @"重新下载"; 45 | } else { 46 | titStr = @"取消"; 47 | } 48 | [btn setTitle:titStr forState:UIControlStateNormal]; 49 | btn.tintColor = [UIColor whiteColor]; 50 | 51 | [self addSubview:btn]; 52 | } 53 | 54 | UIProgressView *progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 50, ScreenWidth, 5)]; 55 | progressView.progress = self.progressV; 56 | progressView.progressTintColor = [UIColor redColor]; 57 | [self addSubview:progressView]; 58 | self.showProgressView = progressView; 59 | } 60 | 61 | - (void)setProgressV:(float)progressV { 62 | _progressV = progressV; 63 | dispatch_async(dispatch_get_main_queue(), ^{ 64 | [self.showProgressView setProgress:_progressV animated:YES]; 65 | }); 66 | } 67 | 68 | - (void)displayUIWithIdentifier:(NSString *)identifier 69 | startClick:(void(^)(NSString *identifier))startClick 70 | pauseClick:(void(^)(NSString *identifier))pauseClick 71 | resumeClick:(void(^)(NSString *identifier))resumeClick 72 | cancleClick:(void(^)(NSString *identifier))cancleClick { 73 | 74 | self.identifer = identifier; 75 | self.startClick = startClick; 76 | self.pauseClick = pauseClick; 77 | self.resumeClick = resumeClick; 78 | self.cancleClick = cancleClick; 79 | } 80 | 81 | - (void)btnClick:(id)sender { 82 | UIButton *btn = (UIButton *)sender; 83 | NSInteger tag = btn.tag; 84 | 85 | switch (tag) { 86 | case 0: 87 | self.startClick(self.identifer); 88 | break; 89 | case 1: 90 | self.pauseClick(self.identifer); 91 | break; 92 | case 2: 93 | self.resumeClick(self.identifer); 94 | break; 95 | case 3: 96 | self.startClick(self.identifer); 97 | break; 98 | 99 | default: 100 | break; 101 | } 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /XZDownloadTask/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // XZDownloadTask 4 | // 5 | // Created by xiazer on 15/4/17. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /XZDownloadTaskTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | anjuke.com.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /XZDownloadTaskTests/XZDownloadTaskTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // XZDownloadTaskTests.m 3 | // XZDownloadTaskTests 4 | // 5 | // Created by xiazer on 15/4/17. 6 | // Copyright (c) 2015年 anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface XZDownloadTaskTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation XZDownloadTaskTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /XZDownlod.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingundertree/XZDownloadTask/28382bbe467ba8defc20b51279caebf648066b58/XZDownlod.gif --------------------------------------------------------------------------------