├── QiAppRunInBackground ├── Assets.xcassets │ ├── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── SomethingJustLikeThis.mp3 ├── AppDelegate.h ├── SceneDelegate.h ├── ViewController.h ├── QiTimerViewController.h ├── QiDownloadViewController.h ├── QiPlayVideoViewController.h ├── QiBackgroundViewController.h ├── QiAudioPlayer.h ├── main.m ├── QiTimerViewController.m ├── QiAudioPlayer.m ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.storyboard ├── QiBackgroundViewController.m ├── Info.plist ├── QiPlayVideoViewController.m ├── AppDelegate.m ├── SceneDelegate.m ├── ViewController.m └── QiDownloadViewController.m ├── QiAppRunInBackground.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── LICENSE └── .gitignore /QiAppRunInBackground/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /QiAppRunInBackground/SomethingJustLikeThis.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QiShare/QiAppRunInBackground/HEAD/QiAppRunInBackground/SomethingJustLikeThis.mp3 -------------------------------------------------------------------------------- /QiAppRunInBackground.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /QiAppRunInBackground.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /QiAppRunInBackground/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/28. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /QiAppRunInBackground/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/28. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SceneDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow * window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /QiAppRunInBackground/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/28. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString *const AppViewControllerRefreshNotificationName; 12 | 13 | @interface ViewController : UIViewController 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /QiAppRunInBackground/QiTimerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // QiTimerViewController.h 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/31. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface QiTimerViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /QiAppRunInBackground/QiDownloadViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // QiDownloadViewController.h 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/30. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface QiDownloadViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /QiAppRunInBackground/QiPlayVideoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // QiPlayVideoViewController.h 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/30. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface QiPlayVideoViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /QiAppRunInBackground/QiBackgroundViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // QiBackgroundViewController.h 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/28. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface QiBackgroundViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /QiAppRunInBackground/QiAudioPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // QiAudioPlayer.h 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/30. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface QiAudioPlayer : NSObject 15 | 16 | + (instancetype)sharedInstance; 17 | 18 | @property (nonatomic, assign) BOOL needRunInBackground; 19 | @property (nonatomic, strong) AVAudioPlayer *player; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /QiAppRunInBackground/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/28. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | NSString * appDelegateClassName; 14 | @autoreleasepool { 15 | // Setup code that might create autoreleased objects goes here. 16 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 17 | } 18 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 iOS及周边技术文章分享平台。 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QiAppRunInBackground/QiTimerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // QiTimerViewController.m 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/31. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import "QiTimerViewController.h" 10 | 11 | @interface QiTimerViewController () 12 | 13 | @property (nonatomic, strong) NSTimer *timer; 14 | 15 | @end 16 | 17 | @implementation QiTimerViewController 18 | 19 | - (void)viewWillDisappear:(BOOL)animated { 20 | [super viewWillDisappear:animated]; 21 | 22 | [_timer invalidate]; 23 | _timer = nil; 24 | } 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | [self setupTimer]; 30 | [self setupUI]; 31 | } 32 | 33 | - (void)setupUI { 34 | 35 | self.title = @"普通定时器"; 36 | self.view.backgroundColor = [UIColor whiteColor]; 37 | } 38 | 39 | #pragma mark - 定时器 40 | - (void)setupTimer { 41 | 42 | _timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerEvent:) userInfo:nil repeats:YES]; 43 | [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; 44 | [_timer fire]; 45 | } 46 | 47 | - (void)timerEvent:(id)sender { 48 | 49 | NSLog(@"普通定时器运行中"); 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /QiAppRunInBackground/QiAudioPlayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // QiAudioPlayer.m 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/30. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import "QiAudioPlayer.h" 10 | 11 | static QiAudioPlayer *instance = nil; 12 | 13 | @interface QiAudioPlayer () 14 | 15 | @end 16 | 17 | @implementation QiAudioPlayer 18 | 19 | + (instancetype)sharedInstance { 20 | 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | instance = [[QiAudioPlayer alloc] init]; 24 | }); 25 | return instance; 26 | } 27 | 28 | - (instancetype)init { 29 | 30 | self = [super init]; 31 | if (!self) { 32 | return nil; 33 | } 34 | [self initPlayer]; 35 | return self; 36 | } 37 | 38 | - (void)initPlayer { 39 | 40 | [self.player prepareToPlay]; 41 | } 42 | 43 | - (AVAudioPlayer *)player { 44 | 45 | if (!_player) { 46 | NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"SomethingJustLikeThis" withExtension:@"mp3"]; 47 | AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil]; 48 | audioPlayer.numberOfLoops = NSUIntegerMax; 49 | _player = audioPlayer; 50 | } 51 | return _player; 52 | } 53 | 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /QiAppRunInBackground/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 | -------------------------------------------------------------------------------- /QiAppRunInBackground/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | # CocoaPods 34 | # 35 | # We recommend against adding the Pods directory to your .gitignore. However 36 | # you should judge for yourself, the pros and cons are mentioned at: 37 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 38 | # 39 | # Pods/ 40 | # 41 | # Add this line if you want to avoid checking in source code from the Xcode workspace 42 | # *.xcworkspace 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | # Carthage/Checkouts 48 | 49 | Carthage/Build/ 50 | 51 | # fastlane 52 | # 53 | # It is recommended to not store the screenshots in the git repo. 54 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 55 | # For more information about the recommended setup visit: 56 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 57 | 58 | fastlane/report.xml 59 | fastlane/Preview.html 60 | fastlane/screenshots/**/*.png 61 | fastlane/test_output 62 | 63 | # Code Injection 64 | # 65 | # After new code Injection tools there's a generated folder /iOSInjectionProject 66 | # https://github.com/johnno1962/injectionforxcode 67 | 68 | iOSInjectionProject/ 69 | -------------------------------------------------------------------------------- /QiAppRunInBackground/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /QiAppRunInBackground/QiBackgroundViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // QiBackgroundViewController.m 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/28. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import "QiBackgroundViewController.h" 10 | #import 11 | 12 | @interface QiBackgroundViewController () 13 | 14 | @property (nonatomic, strong) NSTimer *timer; 15 | @property (nonatomic, strong) CLLocationManager *locationManager; 16 | 17 | @end 18 | 19 | @implementation QiBackgroundViewController 20 | 21 | - (void)viewWillDisappear:(BOOL)animated { 22 | 23 | [super viewWillDisappear:animated]; 24 | [_timer invalidate]; 25 | _timer = nil; 26 | } 27 | 28 | - (void)viewDidLoad { 29 | [super viewDidLoad]; 30 | 31 | [self setupTimer]; 32 | [self setupUI]; 33 | } 34 | 35 | - (void)setupUI { 36 | 37 | self.title = @"定位持续运行"; 38 | self.view.backgroundColor = [UIColor whiteColor]; 39 | 40 | [self initLocation]; 41 | } 42 | 43 | - (void)initLocation { 44 | 45 | self.locationManager = [CLLocationManager new]; 46 | self.locationManager.delegate = self; 47 | [self.locationManager requestAlwaysAuthorization]; 48 | // [self.locationManager requestWhenInUseAuthorization]; 49 | @try { 50 | // 后台定位依然可用 51 | self.locationManager.allowsBackgroundLocationUpdates = YES; 52 | } @catch (NSException *exception) { 53 | NSLog(@"异常:%@", exception); 54 | } @finally { 55 | 56 | } 57 | [self.locationManager startUpdatingLocation]; 58 | } 59 | 60 | - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { 61 | 62 | NSLog(@"%s:位置信息:%@", __FUNCTION__, locations); 63 | } 64 | 65 | #pragma mark - 定时器 66 | - (void)setupTimer { 67 | 68 | _timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerEvent:) userInfo:nil repeats:YES]; 69 | [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; 70 | [_timer fire]; 71 | } 72 | 73 | - (void)timerEvent:(id)sender { 74 | 75 | NSLog(@"定时器运行中"); 76 | } 77 | 78 | - (void)readMe { 79 | 80 | /** 81 | * 参考学习网址: 82 | * App 接入 iOS 11 的 Files App:https://www.jianshu.com/p/61b4e26ab413 83 | * iOS 后台挂起的一些坑:https://www.cnblogs.com/saytome/p/7080056.html 84 | * iOS如何实时查看App运行日志:http://www.cocoachina.com/articles/19933 85 | * iOS 版微信推送即时消息是如何实现的:https://www.zhihu.com/question/20654687 86 | * iPhone在一个小时内,接受推送的次数有限制吗:https://community.jiguang.cn/t/iphone/2511 87 | * 为什么 iOS 的微信没有常驻后台,也能收到新消息通知:https://mlog.club/article/45386 88 | * iOS后台运行的相关方案总结:https://ctinusdev.github.io/2016/05/10/BackgroundTask/ 89 | * iOS之原生地图的使用详解:https://blog.csdn.net/u011146511/article/details/51245469 90 | * iOS项目技术还债之路《一》后台下载趟坑:https://juejin.im/post/5cf7eb0351882576710e5c15 91 | */ 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /QiAppRunInBackground/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BGTaskSchedulerPermittedIdentifiers 6 | 7 | com.qishare.ios.wyw.background.refresh 8 | com.qishare.ios.wyw.background.db_cleaning 9 | 10 | CFBundleDevelopmentRegion 11 | $(DEVELOPMENT_LANGUAGE) 12 | CFBundleExecutable 13 | $(EXECUTABLE_NAME) 14 | CFBundleIdentifier 15 | $(PRODUCT_BUNDLE_IDENTIFIER) 16 | CFBundleInfoDictionaryVersion 17 | 6.0 18 | CFBundleName 19 | $(PRODUCT_NAME) 20 | CFBundlePackageType 21 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 22 | CFBundleShortVersionString 23 | 1.0 24 | CFBundleVersion 25 | 1 26 | LSRequiresIPhoneOS 27 | 28 | LSSupportsOpeningDocumentsInPlace 29 | 30 | NSLocationAlwaysAndWhenInUseUsageDescription 31 | 为了给您提供精确导航服务,申请使用您的位置 32 | NSLocationAlwaysUsageDescription 33 | 为了给您提供实时精确导航服务,申请使用您的位置 34 | NSLocationUsageDescription 35 | 为了给您提供实时精确导航服务,申请使用您的位置 36 | NSLocationWhenInUseUsageDescription 37 | 为了给您提供精确导航服务,申请使用您的位置 38 | UIApplicationSceneManifest 39 | 40 | UIApplicationSupportsMultipleScenes 41 | 42 | UISceneConfigurations 43 | 44 | UIWindowSceneSessionRoleApplication 45 | 46 | 47 | UISceneConfigurationName 48 | Default Configuration 49 | UISceneDelegateClassName 50 | SceneDelegate 51 | UISceneStoryboardFile 52 | Main 53 | 54 | 55 | 56 | 57 | UIBackgroundModes 58 | 59 | audio 60 | fetch 61 | location 62 | processing 63 | remote-notification 64 | voip 65 | 66 | UIFileSharingEnabled 67 | 68 | UILaunchStoryboardName 69 | LaunchScreen 70 | UIMainStoryboardFile 71 | Main 72 | UIRequiredDeviceCapabilities 73 | 74 | armv7 75 | 76 | UISupportedInterfaceOrientations 77 | 78 | UIInterfaceOrientationPortrait 79 | UIInterfaceOrientationLandscapeLeft 80 | UIInterfaceOrientationLandscapeRight 81 | 82 | UISupportedInterfaceOrientations~ipad 83 | 84 | UIInterfaceOrientationPortrait 85 | UIInterfaceOrientationPortraitUpsideDown 86 | UIInterfaceOrientationLandscapeLeft 87 | UIInterfaceOrientationLandscapeRight 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /QiAppRunInBackground/QiPlayVideoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // QiPlayVideoViewController.m 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/30. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import "QiPlayVideoViewController.h" 10 | #import "QiAudioPlayer.h" 11 | 12 | @interface QiPlayVideoViewController () 13 | 14 | @property (nonatomic, strong) AVAudioPlayer *audioPlayer; 15 | @property (nonatomic, strong) NSTimer *timer; 16 | 17 | @end 18 | 19 | @implementation QiPlayVideoViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | // [self initAudioPlayer]; 25 | [self setupUI]; 26 | [self setupTimer]; 27 | } 28 | 29 | - (void)viewWillDisappear:(BOOL)animated { 30 | [super viewWillDisappear:animated]; 31 | 32 | [_timer invalidate]; 33 | _timer = nil; 34 | } 35 | 36 | - (void)setupUI { 37 | 38 | self.title = @"播放音乐"; 39 | self.view.backgroundColor = [UIColor whiteColor]; 40 | 41 | [self setupButtons]; 42 | } 43 | 44 | - (void)setupButtons { 45 | 46 | CGFloat topMargin = 200.0; 47 | CGFloat leftMargin = 20.0; 48 | CGFloat verticalMargin = 30.0; 49 | CGFloat btnW = [UIScreen mainScreen].bounds.size.width - leftMargin * 2; 50 | CGFloat btnH = 44.0; 51 | UIColor *btnColor = [UIColor grayColor]; 52 | 53 | UIButton *playBtn = [[UIButton alloc] initWithFrame:CGRectMake(leftMargin, topMargin, btnW, btnH)]; 54 | [playBtn setTitle:@"开始播放" forState:UIControlStateNormal]; 55 | playBtn.backgroundColor = btnColor; 56 | [self.view addSubview:playBtn]; 57 | [playBtn addTarget:self action:@selector(playButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; 58 | 59 | UIButton *pauseBtn = [[UIButton alloc] initWithFrame:CGRectMake(leftMargin, CGRectGetMaxY(playBtn.frame) + verticalMargin, btnW, btnH)]; 60 | [pauseBtn setTitle:@"暂停" forState:UIControlStateNormal]; 61 | pauseBtn.backgroundColor = btnColor; 62 | [self.view addSubview:pauseBtn]; 63 | [pauseBtn addTarget:self action:@selector(pauseButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; 64 | } 65 | 66 | - (void)initAudioPlayer { 67 | 68 | NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"SomethingJustLikeThis" withExtension:@"mp3"]; 69 | AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil]; 70 | audioPlayer.numberOfLoops = NSUIntegerMax; 71 | audioPlayer.delegate = self; 72 | [audioPlayer prepareToPlay]; 73 | _audioPlayer = audioPlayer; 74 | } 75 | 76 | #pragma mark - 播放音乐 77 | - (void)playButtonClicked:(UIButton *)sender { 78 | 79 | // [_audioPlayer play]; 80 | [[QiAudioPlayer sharedInstance].player play]; 81 | } 82 | 83 | #pragma mark - 暂停播放 84 | - (void)pauseButtonClicked:(UIButton *)sender { 85 | 86 | // [self.audioPlayer pause]; 87 | [[QiAudioPlayer sharedInstance].player pause]; 88 | } 89 | 90 | #pragma mark - 定时器 91 | - (void)setupTimer { 92 | 93 | _timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerEvent:) userInfo:nil repeats:YES]; 94 | [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; 95 | [_timer fire]; 96 | } 97 | 98 | - (void)timerEvent:(id)sender { 99 | 100 | NSLog(@"定时器运行中"); 101 | } 102 | 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /QiAppRunInBackground/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/28. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | #import "QiAudioPlayer.h" 12 | 13 | @interface AppDelegate () 14 | 15 | @property (nonatomic, assign) UIBackgroundTaskIdentifier backgroundTaskIdentifier; 16 | 17 | @end 18 | 19 | static NSString *const kBgTaskName = @"com.qishare.ios.wyw.QiAppRunInBackground"; 20 | 21 | @implementation AppDelegate 22 | 23 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 24 | 25 | if (@available(iOS 13.0, *)) { 26 | 27 | } else { 28 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 29 | self.window.backgroundColor = [UIColor whiteColor]; 30 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; 31 | [self.window makeKeyAndVisible]; 32 | } 33 | 34 | return YES; 35 | } 36 | 37 | #pragma mark - MARK: - 需要后台下载的情况需要实现如下方法 38 | - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler { 39 | 40 | NSLog(@"标识:%@ 后台任务下载完成", identifier); 41 | } 42 | 43 | #pragma mark - UISceneSession lifecycle 44 | 45 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)){ 46 | 47 | NSLog(@"%s: 创建新的场景会话", __FUNCTION__); 48 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 49 | } 50 | 51 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions API_AVAILABLE(ios(13.0)){ 52 | 53 | NSLog(@"%s:已丢弃一个场景会话didDiscardSceneSessions", __FUNCTION__); 54 | } 55 | 56 | - (void)applicationWillEnterForeground:(UIApplication *)application { 57 | 58 | NSLog(@"%s:应用将进入前台WillEnterForeground", __FUNCTION__); 59 | if ([QiAudioPlayer sharedInstance].needRunInBackground) { 60 | [[QiAudioPlayer sharedInstance].player pause]; 61 | } 62 | [[UIApplication sharedApplication] endBackgroundTask: self.backgroundTaskIdentifier]; 63 | } 64 | 65 | - (void)applicationDidBecomeActive:(UIApplication *)application { 66 | 67 | NSLog(@"%s:已进入活跃状态DidBecomeActive", __FUNCTION__); 68 | } 69 | 70 | - (void)applicationWillResignActive:(UIApplication *)application { 71 | 72 | NSLog(@"%s:将进入非活跃状态WillResignActive", __FUNCTION__); 73 | } 74 | 75 | - (void)applicationDidEnterBackground:(UIApplication *)application { 76 | 77 | NSLog(@"%s:应用进入后台DidEnterBackground", __FUNCTION__); 78 | self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:kBgTaskName expirationHandler:^{ 79 | 80 | if ([QiAudioPlayer sharedInstance].needRunInBackground) { 81 | [[QiAudioPlayer sharedInstance].player play]; 82 | } 83 | if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) { 84 | [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier]; 85 | self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; 86 | } 87 | }]; 88 | } 89 | 90 | - (void)applicationWillTerminate:(UIApplication *)application { 91 | 92 | NSLog(@"%s:应用终止:WillTerminate", __FUNCTION__); 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /QiAppRunInBackground/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | #import "SceneDelegate.h" 2 | #import "ViewController.h" 3 | #import "QiAudioPlayer.h" 4 | #import 5 | #import 6 | 7 | @interface SceneDelegate () 8 | 9 | @property (nonatomic, assign) UIBackgroundTaskIdentifier backgroundTaskIdentifier; 10 | 11 | @end 12 | 13 | static NSString *const kBgTaskName = @"com.qishare.ios.wyw.QiAppRunInBackground"; 14 | static NSString *const kRefreshTaskId = @"com.qishare.ios.wyw.background.refresh"; 15 | static NSString *const kCleanTaskId = @"com.qishare.ios.wyw.background.db_cleaning"; 16 | 17 | @implementation SceneDelegate 18 | 19 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions API_AVAILABLE(ios(13.0)){ 20 | 21 | UIWindowScene *windowScene = [[UIWindowScene alloc] initWithSession:session connectionOptions:connectionOptions]; 22 | self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; 23 | self.window.backgroundColor = [UIColor whiteColor]; 24 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; 25 | [self.window makeKeyAndVisible]; 26 | NSLog(@"%s: 场景将要连接会话willConnectToSession", __FUNCTION__); 27 | 28 | [self registerBgTask]; 29 | } 30 | 31 | - (void)registerBgTask { 32 | 33 | if (@available(iOS 13.0, *)) { 34 | BOOL registerFlag = [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:kRefreshTaskId usingQueue:nil launchHandler:^(__kindof BGTask * _Nonnull task) { 35 | [self handleAppRefresh:task]; 36 | }]; 37 | if (registerFlag) { 38 | NSLog(@"注册成功"); 39 | } else { 40 | NSLog(@"注册失败"); 41 | } 42 | } else { 43 | // Fallback on earlier versions 44 | } 45 | 46 | if (@available(iOS 13.0, *)) { 47 | [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:kCleanTaskId usingQueue:nil launchHandler:^(__kindof BGTask * _Nonnull task) { 48 | [self handleAppRefresh:task]; 49 | }]; 50 | } else { 51 | // Fallback on earlier versions 52 | } 53 | } 54 | 55 | - (void)handleAppRefresh:(BGAppRefreshTask *)appRefreshTask API_AVAILABLE(ios(13.0)){ 56 | 57 | [self scheduleAppRefresh]; 58 | 59 | NSLog(@"App刷新===================================================================="); 60 | NSOperationQueue *queue = [NSOperationQueue new]; 61 | queue.maxConcurrentOperationCount = 1; 62 | 63 | NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 64 | 65 | [[NSNotificationCenter defaultCenter] postNotificationName:AppViewControllerRefreshNotificationName object:nil]; 66 | 67 | NSLog(@"操作"); 68 | NSDate *date = [NSDate date]; 69 | NSDateFormatter *dateFormatter = [NSDateFormatter new]; 70 | [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm"]; 71 | NSString *timeString = [dateFormatter stringFromDate:date]; 72 | 73 | NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"QiLog.txt"]; 74 | if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { 75 | NSData *data = [timeString dataUsingEncoding:NSUTF8StringEncoding]; 76 | [[NSFileManager defaultManager] createFileAtPath:filePath contents:data attributes:nil]; 77 | } else { 78 | NSData *data = [[NSData alloc] initWithContentsOfFile:filePath]; 79 | NSString *originalContent = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 80 | NSString *content = [originalContent stringByAppendingString:[NSString stringWithFormat:@"\n时间:%@\n", timeString]]; 81 | data = [content dataUsingEncoding:NSUTF8StringEncoding]; 82 | [data writeToFile:filePath atomically:YES]; 83 | } 84 | }]; 85 | 86 | appRefreshTask.expirationHandler = ^{ 87 | [queue cancelAllOperations]; 88 | }; 89 | [queue addOperation:operation]; 90 | 91 | __weak NSBlockOperation *weakOperation = operation; 92 | operation.completionBlock = ^{ 93 | [appRefreshTask setTaskCompletedWithSuccess:!weakOperation.isCancelled]; 94 | }; 95 | } 96 | 97 | - (void)scheduleAppRefresh { 98 | 99 | if (@available(iOS 13.0, *)) { 100 | BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:kRefreshTaskId]; 101 | // 最早15分钟后启动后台任务请求 102 | request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15.0 * 60]; 103 | NSError *error = nil; 104 | [[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]; 105 | if (error) { 106 | NSLog(@"错误信息:%@", error); 107 | } 108 | 109 | } else { 110 | // Fallback on earlier versions 111 | } 112 | } 113 | 114 | - (void)sceneDidDisconnect:(UIScene *)scene API_AVAILABLE(ios(13.0)){ 115 | 116 | NSLog(@"%s:场景已断开Unattach", __FUNCTION__); 117 | } 118 | 119 | - (void)sceneDidBecomeActive:(UIScene *)scene API_AVAILABLE(ios(13.0)){ 120 | 121 | NSLog(@"%s:不活跃到活跃Inactive -> Active", __FUNCTION__); 122 | } 123 | 124 | - (void)sceneWillResignActive:(UIScene *)scene API_AVAILABLE(ios(13.0)){ 125 | 126 | NSLog(@"%s:活跃到不活跃Active -> Inactive", __FUNCTION__); 127 | } 128 | 129 | - (void)sceneWillEnterForeground:(UIScene *)scene API_AVAILABLE(ios(13.0)){ 130 | 131 | if ([QiAudioPlayer sharedInstance].needRunInBackground) { 132 | [[QiAudioPlayer sharedInstance].player pause]; 133 | } 134 | [[UIApplication sharedApplication] endBackgroundTask: self.backgroundTaskIdentifier]; 135 | 136 | NSLog(@"%s:应用将进入前台WillEnterForeground", __FUNCTION__); 137 | } 138 | 139 | - (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)){ 140 | 141 | [self scheduleAppRefresh]; 142 | // e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier: @"com.qishare.ios.wyw.background.refresh"] 143 | NSLog(@"%s:应用已进入后台DidEnterBackground", __FUNCTION__); 144 | 145 | self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithName:kBgTaskName expirationHandler:^{ 146 | if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) { 147 | if ([QiAudioPlayer sharedInstance].needRunInBackground) { 148 | [[QiAudioPlayer sharedInstance].player play]; 149 | } 150 | NSLog(@"终止后台任务"); 151 | [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier]; 152 | self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; 153 | } 154 | }]; 155 | } 156 | 157 | @end 158 | -------------------------------------------------------------------------------- /QiAppRunInBackground/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/28. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "QiBackgroundViewController.h" 11 | #import "QiPlayVideoViewController.h" 12 | #import "QiDownloadViewController.h" 13 | #import "QiTimerViewController.h" 14 | #import "QiAudioPlayer.h" 15 | 16 | NSString *const AppViewControllerRefreshNotificationName = @"AppViewControllerRefreshNotificationName"; 17 | 18 | @interface ViewController () 19 | 20 | @property (nonatomic, strong) UIColor *buttonBackgroundColor; 21 | @property (nonatomic, strong) NSMutableArray *mButtonArray; 22 | 23 | @end 24 | 25 | @implementation ViewController 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | 30 | [self initAudioSession]; 31 | [self addNotification]; 32 | [self setupUI]; 33 | } 34 | 35 | #pragma mark - UI 36 | - (void)setupUI { 37 | 38 | self.title = @"首页"; 39 | [self setupButtons]; 40 | } 41 | 42 | #pragma mark - 按钮 43 | - (void)setupButtons { 44 | 45 | CGFloat topMargin = 200.0; 46 | CGFloat leftMargin = 20.0; 47 | CGFloat verticalMargin = 30.0; 48 | CGFloat btnW = [UIScreen mainScreen].bounds.size.width - leftMargin * 2; 49 | CGFloat btnH = 44.0; 50 | UIColor *btnColor = [UIColor grayColor]; 51 | _buttonBackgroundColor = btnColor; 52 | _mButtonArray = [NSMutableArray array]; 53 | 54 | UIButton *locationBtn = [[UIButton alloc] initWithFrame:CGRectMake(leftMargin, topMargin, btnW, btnH)]; 55 | [locationBtn setTitle:@"地图" forState:UIControlStateNormal]; 56 | locationBtn.backgroundColor = btnColor; 57 | [self.view addSubview:locationBtn]; 58 | [locationBtn addTarget:self action:@selector(locationButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; 59 | [_mButtonArray addObject:locationBtn]; 60 | 61 | UIButton *playBtn = [[UIButton alloc] initWithFrame:CGRectMake(leftMargin, CGRectGetMaxY(locationBtn.frame) + verticalMargin, btnW, btnH)]; 62 | [playBtn setTitle:@"播放音乐" forState:UIControlStateNormal]; 63 | playBtn.backgroundColor = btnColor; 64 | [self.view addSubview:playBtn]; 65 | [playBtn addTarget:self action:@selector(playButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; 66 | [_mButtonArray addObject:playBtn]; 67 | 68 | UIButton *needRunInBgBtn = [[UIButton alloc] initWithFrame:CGRectMake(leftMargin, CGRectGetMaxY(playBtn.frame) + verticalMargin, btnW, btnH)]; 69 | [needRunInBgBtn setTitle:@"需要后台运行" forState:UIControlStateNormal]; 70 | needRunInBgBtn.backgroundColor = [UIColor blackColor]; 71 | [self.view addSubview:needRunInBgBtn]; 72 | [needRunInBgBtn addTarget:self action:@selector(needRunInBackgroundButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; 73 | 74 | UIButton *downloadBtn = [[UIButton alloc] initWithFrame:CGRectMake(leftMargin, CGRectGetMaxY(needRunInBgBtn.frame) + verticalMargin, btnW, btnH)]; 75 | [downloadBtn setTitle:@"下载" forState:UIControlStateNormal]; 76 | downloadBtn.backgroundColor = btnColor; 77 | [self.view addSubview:downloadBtn]; 78 | [downloadBtn addTarget:self action:@selector(downloadButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; 79 | [_mButtonArray addObject:downloadBtn]; 80 | 81 | UIButton *timerBtn = [[UIButton alloc] initWithFrame:CGRectMake(leftMargin, CGRectGetMaxY(downloadBtn.frame) + verticalMargin, btnW, btnH)]; 82 | [timerBtn setTitle:@"定时器" forState:UIControlStateNormal]; 83 | timerBtn.backgroundColor = btnColor; 84 | [self.view addSubview:timerBtn]; 85 | [timerBtn addTarget:self action:@selector(timerButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; 86 | [_mButtonArray addObject:timerBtn]; 87 | } 88 | 89 | #pragma mark - 地图视图 90 | - (void)locationButtonClicked:(UIButton *)sender { 91 | 92 | NSURL *url = [NSURL URLWithString:@"https://www.so.com"]; 93 | NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; 94 | NSURLSession *session = [NSURLSession sharedSession]; 95 | NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 96 | if (error) { 97 | NSLog(@"错误信息:%@", error); 98 | } else { 99 | NSLog(@"数据长度:%lu", (unsigned long)data.length); 100 | } 101 | }]; 102 | 103 | [dataTask resume]; 104 | return; 105 | 106 | QiBackgroundViewController *backgroundVC = [QiBackgroundViewController new]; 107 | [self.navigationController pushViewController:backgroundVC animated:YES]; 108 | } 109 | 110 | #pragma mark - 播放音乐 111 | - (void)playButtonClicked:(UIButton *)sender { 112 | 113 | QiPlayVideoViewController *playVieoVC = [QiPlayVideoViewController new]; 114 | [self.navigationController pushViewController:playVieoVC animated:YES]; 115 | } 116 | 117 | #pragma mark - 需要后台运行 118 | - (void)needRunInBackgroundButtonClicked:(UIButton *)sender { 119 | 120 | [QiAudioPlayer sharedInstance].needRunInBackground = YES; 121 | } 122 | 123 | #pragma mark - 下载 124 | - (void)downloadButtonClicked:(UIButton *)sender { 125 | 126 | QiDownloadViewController *downloadVC = [QiDownloadViewController new]; 127 | [self.navigationController pushViewController:downloadVC animated:YES]; 128 | } 129 | 130 | #pragma mark - 定时器 131 | - (void)timerButtonClicked:(UIButton *)sender { 132 | 133 | QiTimerViewController *timerVC = [QiTimerViewController new]; 134 | [self.navigationController pushViewController:timerVC animated:YES]; 135 | } 136 | 137 | - (void)initAudioSession { 138 | 139 | AVAudioSession *session = [AVAudioSession sharedInstance]; 140 | [session setActive:YES error:nil]; 141 | [session setCategory:AVAudioSessionCategoryPlayback error:nil]; 142 | } 143 | 144 | #pragma mark - 添加通知 145 | - (void)addNotification { 146 | 147 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appRefreshNoti:) name:AppViewControllerRefreshNotificationName object:nil]; 148 | } 149 | 150 | - (void)appRefreshNoti:(id)sender { 151 | 152 | dispatch_async(dispatch_get_main_queue(), ^{ 153 | UIColor *btnColor = [UIColor colorWithRed:(arc4random() % 256 / 255.0) green:(arc4random() % 256 / 255.0) blue:(arc4random() % 256 / 255.0) alpha:1.0]; 154 | [self.mButtonArray enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 155 | obj.backgroundColor = btnColor; 156 | }]; 157 | }); 158 | } 159 | 160 | #pragma mark - 移除通知 161 | - (void)dealloc { 162 | 163 | [[NSNotificationCenter defaultCenter] removeObserver:self name:AppViewControllerRefreshNotificationName object:nil]; 164 | } 165 | 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /QiAppRunInBackground/QiDownloadViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // QiDownloadViewController.m 3 | // QiAppRunInBackground 4 | // 5 | // Created by wangyongwang on 2019/12/30. 6 | // Copyright © 2019 WYW. All rights reserved. 7 | // 8 | 9 | #import "QiDownloadViewController.h" 10 | 11 | @interface QiDownloadViewController () 12 | 13 | @property (nonatomic, strong) NSTimer *timer; 14 | @property (nonatomic, strong) UIImageView *backgroundImageView; 15 | @property (nonatomic, strong) NSURLSession *session; 16 | 17 | @end 18 | 19 | @implementation QiDownloadViewController 20 | 21 | - (void)viewDidDisappear:(BOOL)animated { 22 | [super viewDidDisappear:animated]; 23 | 24 | [self.session invalidateAndCancel]; 25 | } 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | 30 | [self initDownloadTask]; 31 | // [self setupTimer]; 32 | [self setupUI]; 33 | } 34 | 35 | - (void)initDownloadTask { 36 | NSURLSessionConfiguration *sessionConfig; 37 | @try { 38 | sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.qishare.ios.wyw.backgroundDownloadTask"]; 39 | } @catch (NSException *exception) { 40 | NSLog(@"异常信息:%@", exception); 41 | } @finally { 42 | 43 | } 44 | 45 | // sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; 46 | NSURL *url = [NSURL URLWithString:@"https://images.pexels.com/photos/3225517/pexels-photo-3225517.jpeg"]; 47 | // 资源下载完后 可以得到通知 AppDelegate.m 文件中的 - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler 48 | sessionConfig.sessionSendsLaunchEvents = YES; 49 | // 当传输大数据量数据的时候,建议将此属性设置为YES,这样系统可以安排对设备而言最佳的传输时间 50 | sessionConfig.discretionary = YES; 51 | NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil]; 52 | self.session = session; 53 | NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url]; 54 | [downloadTask resume]; 55 | 56 | /* 57 | NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; 58 | NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) { 59 | if (error) { 60 | NSLog(@"错误信息:%@", error); 61 | } else { 62 | NSLog(@"response:%lld", response.expectedContentLength); 63 | dispatch_async(dispatch_get_main_queue(), ^{ 64 | 65 | // self.backgroundImageView.image = [UIImage imageWithData:response]; 66 | }); 67 | } 68 | }]; 69 | [downloadTask resume]; 70 | 71 | */ 72 | } 73 | 74 | - (void)setupUI { 75 | 76 | self.title = @"下载任务"; 77 | self.view.backgroundColor = [UIColor whiteColor]; 78 | 79 | UIImageView *bgImageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; 80 | [self.view addSubview:bgImageView]; 81 | bgImageView.backgroundColor = [UIColor grayColor]; 82 | self.backgroundImageView = bgImageView; 83 | 84 | // [self setupButtons]; 85 | } 86 | 87 | - (void)setupButtons { 88 | 89 | CGFloat topMargin = 200.0; 90 | CGFloat leftMargin = 20.0; 91 | CGFloat verticalMargin = 30.0; 92 | CGFloat btnW = [UIScreen mainScreen].bounds.size.width - leftMargin * 2; 93 | CGFloat btnH = 44.0; 94 | UIColor *btnColor = [UIColor grayColor]; 95 | 96 | UIButton *downloadBtn = [[UIButton alloc] initWithFrame:CGRectMake(leftMargin, topMargin, btnW, btnH)]; 97 | [downloadBtn setTitle:@"开始下载" forState:UIControlStateNormal]; 98 | downloadBtn.backgroundColor = btnColor; 99 | [self.view addSubview:downloadBtn]; 100 | [downloadBtn addTarget:self action:@selector(startDownloadButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; 101 | 102 | UIButton *pauseBtn = [[UIButton alloc] initWithFrame:CGRectMake(leftMargin, CGRectGetMaxY(downloadBtn.frame) + verticalMargin, btnW, btnH)]; 103 | [pauseBtn setTitle:@"暂停下载" forState:UIControlStateNormal]; 104 | pauseBtn.backgroundColor = btnColor; 105 | [self.view addSubview:pauseBtn]; 106 | [pauseBtn addTarget:self action:@selector(pauseButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; 107 | } 108 | 109 | #pragma mark - 播放音乐 110 | - (void)startDownloadButtonClicked:(UIButton *)sender { 111 | 112 | [self initDownloadTask]; 113 | } 114 | 115 | #pragma mark - 暂停播放 116 | - (void)pauseButtonClicked:(UIButton *)sender { 117 | 118 | } 119 | 120 | #pragma mark - 定时器 121 | - (void)setupTimer { 122 | 123 | _timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerEvent:) userInfo:nil repeats:YES]; 124 | [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; 125 | [_timer fire]; 126 | } 127 | 128 | - (void)timerEvent:(id)sender { 129 | 130 | NSLog(@"定时器运行中"); 131 | } 132 | 133 | #pragma mark - Delegates 134 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask 135 | didFinishDownloadingToURL:(NSURL *)location { 136 | 137 | NSLog(@"下载结束%@", location.path); 138 | [self.session invalidateAndCancel]; 139 | NSError *error = nil; 140 | NSString *documentPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename]; 141 | if ([[NSFileManager defaultManager] fileExistsAtPath:documentPath]) { 142 | [[NSFileManager defaultManager] removeItemAtPath:documentPath error:nil]; 143 | } 144 | [[NSFileManager defaultManager] moveItemAtPath:location.path toPath:documentPath error:&error]; 145 | dispatch_async(dispatch_get_main_queue(), ^{ 146 | NSData *data = [[NSData alloc] initWithContentsOfFile:documentPath]; 147 | self.backgroundImageView.image = [UIImage imageWithData:data]; 148 | }); 149 | if (error) { 150 | NSLog(@"错误信息:%@", error); 151 | } 152 | } 153 | 154 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask 155 | didWriteData:(int64_t)bytesWritten 156 | totalBytesWritten:(int64_t)totalBytesWritten 157 | totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { 158 | 159 | NSLog(@"下载进度%f", totalBytesWritten * 1.0 / totalBytesExpectedToWrite); 160 | } 161 | 162 | - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask 163 | didResumeAtOffset:(int64_t)fileOffset 164 | expectedTotalBytes:(int64_t)expectedTotalBytes { 165 | 166 | NSLog(@"部分进度%f", fileOffset * 1.0 / expectedTotalBytes); 167 | } 168 | 169 | @end 170 | -------------------------------------------------------------------------------- /QiAppRunInBackground.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | EE4CF90823BB08C20071863C /* QiTimerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE4CF90723BB08C20071863C /* QiTimerViewController.m */; }; 11 | EE6D228923B9B3AE00C0A2FC /* QiAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = EE6D228823B9B3AE00C0A2FC /* QiAudioPlayer.m */; }; 12 | EE6D228C23B9D52400C0A2FC /* QiDownloadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE6D228B23B9D52400C0A2FC /* QiDownloadViewController.m */; }; 13 | EEA5E93923B722AF00BBE9EF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EEA5E93823B722AF00BBE9EF /* AppDelegate.m */; }; 14 | EEA5E93C23B722AF00BBE9EF /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EEA5E93B23B722AF00BBE9EF /* SceneDelegate.m */; }; 15 | EEA5E93F23B722AF00BBE9EF /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEA5E93E23B722AF00BBE9EF /* ViewController.m */; }; 16 | EEA5E94223B722AF00BBE9EF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEA5E94023B722AF00BBE9EF /* Main.storyboard */; }; 17 | EEA5E94423B722B100BBE9EF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EEA5E94323B722B100BBE9EF /* Assets.xcassets */; }; 18 | EEA5E94723B722B100BBE9EF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEA5E94523B722B100BBE9EF /* LaunchScreen.storyboard */; }; 19 | EEA5E94A23B722B100BBE9EF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = EEA5E94923B722B100BBE9EF /* main.m */; }; 20 | EEA5E95223B7232400BBE9EF /* QiBackgroundViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEA5E95123B7232400BBE9EF /* QiBackgroundViewController.m */; }; 21 | EEA5E97923B98EF000BBE9EF /* SomethingJustLikeThis.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = EEA5E97823B98EF000BBE9EF /* SomethingJustLikeThis.mp3 */; }; 22 | EEA5E97C23B98F9C00BBE9EF /* QiPlayVideoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EEA5E97B23B98F9C00BBE9EF /* QiPlayVideoViewController.m */; }; 23 | EEA64FAE23BEDAD100348A95 /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEA64FAD23BEDAD000348A95 /* PushKit.framework */; }; 24 | EEA64FAF23BEDAFD00348A95 /* CallKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE08C9FC23BE09F500D23463 /* CallKit.framework */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | EE08C9FC23BE09F500D23463 /* CallKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CallKit.framework; path = System/Library/Frameworks/CallKit.framework; sourceTree = SDKROOT; }; 29 | EE4CF90623BB08C20071863C /* QiTimerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QiTimerViewController.h; sourceTree = ""; }; 30 | EE4CF90723BB08C20071863C /* QiTimerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QiTimerViewController.m; sourceTree = ""; }; 31 | EE6D228723B9B3AE00C0A2FC /* QiAudioPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QiAudioPlayer.h; sourceTree = ""; }; 32 | EE6D228823B9B3AE00C0A2FC /* QiAudioPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QiAudioPlayer.m; sourceTree = ""; }; 33 | EE6D228A23B9D52400C0A2FC /* QiDownloadViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QiDownloadViewController.h; sourceTree = ""; }; 34 | EE6D228B23B9D52400C0A2FC /* QiDownloadViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QiDownloadViewController.m; sourceTree = ""; }; 35 | EEA5E93423B722AF00BBE9EF /* QiAppRunInBackground.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QiAppRunInBackground.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | EEA5E93723B722AF00BBE9EF /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 37 | EEA5E93823B722AF00BBE9EF /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 38 | EEA5E93A23B722AF00BBE9EF /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 39 | EEA5E93B23B722AF00BBE9EF /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 40 | EEA5E93D23B722AF00BBE9EF /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 41 | EEA5E93E23B722AF00BBE9EF /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 42 | EEA5E94123B722AF00BBE9EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 43 | EEA5E94323B722B100BBE9EF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 44 | EEA5E94623B722B100BBE9EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 45 | EEA5E94823B722B100BBE9EF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | EEA5E94923B722B100BBE9EF /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 47 | EEA5E95023B7232400BBE9EF /* QiBackgroundViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QiBackgroundViewController.h; sourceTree = ""; }; 48 | EEA5E95123B7232400BBE9EF /* QiBackgroundViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QiBackgroundViewController.m; sourceTree = ""; }; 49 | EEA5E97823B98EF000BBE9EF /* SomethingJustLikeThis.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = SomethingJustLikeThis.mp3; sourceTree = ""; }; 50 | EEA5E97A23B98F9C00BBE9EF /* QiPlayVideoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QiPlayVideoViewController.h; sourceTree = ""; }; 51 | EEA5E97B23B98F9C00BBE9EF /* QiPlayVideoViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QiPlayVideoViewController.m; sourceTree = ""; }; 52 | EEA64FAD23BEDAD000348A95 /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = System/Library/Frameworks/PushKit.framework; sourceTree = SDKROOT; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | EEA5E93123B722AF00BBE9EF /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | EEA64FAE23BEDAD100348A95 /* PushKit.framework in Frameworks */, 61 | EEA64FAF23BEDAFD00348A95 /* CallKit.framework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | EE08C9FB23BE09F400D23463 /* Frameworks */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | EEA64FAD23BEDAD000348A95 /* PushKit.framework */, 72 | EE08C9FC23BE09F500D23463 /* CallKit.framework */, 73 | ); 74 | name = Frameworks; 75 | sourceTree = ""; 76 | }; 77 | EEA5E92B23B722AF00BBE9EF = { 78 | isa = PBXGroup; 79 | children = ( 80 | EEA5E93623B722AF00BBE9EF /* QiAppRunInBackground */, 81 | EEA5E93523B722AF00BBE9EF /* Products */, 82 | EE08C9FB23BE09F400D23463 /* Frameworks */, 83 | ); 84 | sourceTree = ""; 85 | }; 86 | EEA5E93523B722AF00BBE9EF /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | EEA5E93423B722AF00BBE9EF /* QiAppRunInBackground.app */, 90 | ); 91 | name = Products; 92 | sourceTree = ""; 93 | }; 94 | EEA5E93623B722AF00BBE9EF /* QiAppRunInBackground */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | EEA5E97823B98EF000BBE9EF /* SomethingJustLikeThis.mp3 */, 98 | EEA5E93723B722AF00BBE9EF /* AppDelegate.h */, 99 | EEA5E93823B722AF00BBE9EF /* AppDelegate.m */, 100 | EEA5E93A23B722AF00BBE9EF /* SceneDelegate.h */, 101 | EEA5E93B23B722AF00BBE9EF /* SceneDelegate.m */, 102 | EEA5E93D23B722AF00BBE9EF /* ViewController.h */, 103 | EEA5E93E23B722AF00BBE9EF /* ViewController.m */, 104 | EEA5E95023B7232400BBE9EF /* QiBackgroundViewController.h */, 105 | EEA5E95123B7232400BBE9EF /* QiBackgroundViewController.m */, 106 | EEA5E97A23B98F9C00BBE9EF /* QiPlayVideoViewController.h */, 107 | EEA5E97B23B98F9C00BBE9EF /* QiPlayVideoViewController.m */, 108 | EE6D228A23B9D52400C0A2FC /* QiDownloadViewController.h */, 109 | EE6D228B23B9D52400C0A2FC /* QiDownloadViewController.m */, 110 | EE4CF90623BB08C20071863C /* QiTimerViewController.h */, 111 | EE4CF90723BB08C20071863C /* QiTimerViewController.m */, 112 | EE6D228723B9B3AE00C0A2FC /* QiAudioPlayer.h */, 113 | EE6D228823B9B3AE00C0A2FC /* QiAudioPlayer.m */, 114 | EEA5E94023B722AF00BBE9EF /* Main.storyboard */, 115 | EEA5E94323B722B100BBE9EF /* Assets.xcassets */, 116 | EEA5E94523B722B100BBE9EF /* LaunchScreen.storyboard */, 117 | EEA5E94823B722B100BBE9EF /* Info.plist */, 118 | EEA5E94923B722B100BBE9EF /* main.m */, 119 | ); 120 | path = QiAppRunInBackground; 121 | sourceTree = ""; 122 | }; 123 | /* End PBXGroup section */ 124 | 125 | /* Begin PBXNativeTarget section */ 126 | EEA5E93323B722AF00BBE9EF /* QiAppRunInBackground */ = { 127 | isa = PBXNativeTarget; 128 | buildConfigurationList = EEA5E94D23B722B100BBE9EF /* Build configuration list for PBXNativeTarget "QiAppRunInBackground" */; 129 | buildPhases = ( 130 | EEA5E93023B722AF00BBE9EF /* Sources */, 131 | EEA5E93123B722AF00BBE9EF /* Frameworks */, 132 | EEA5E93223B722AF00BBE9EF /* Resources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | ); 138 | name = QiAppRunInBackground; 139 | productName = QiAppRunInBackground; 140 | productReference = EEA5E93423B722AF00BBE9EF /* QiAppRunInBackground.app */; 141 | productType = "com.apple.product-type.application"; 142 | }; 143 | /* End PBXNativeTarget section */ 144 | 145 | /* Begin PBXProject section */ 146 | EEA5E92C23B722AF00BBE9EF /* Project object */ = { 147 | isa = PBXProject; 148 | attributes = { 149 | LastUpgradeCheck = 1100; 150 | ORGANIZATIONNAME = WYW; 151 | TargetAttributes = { 152 | EEA5E93323B722AF00BBE9EF = { 153 | CreatedOnToolsVersion = 11.0; 154 | }; 155 | }; 156 | }; 157 | buildConfigurationList = EEA5E92F23B722AF00BBE9EF /* Build configuration list for PBXProject "QiAppRunInBackground" */; 158 | compatibilityVersion = "Xcode 9.3"; 159 | developmentRegion = en; 160 | hasScannedForEncodings = 0; 161 | knownRegions = ( 162 | en, 163 | Base, 164 | ); 165 | mainGroup = EEA5E92B23B722AF00BBE9EF; 166 | productRefGroup = EEA5E93523B722AF00BBE9EF /* Products */; 167 | projectDirPath = ""; 168 | projectRoot = ""; 169 | targets = ( 170 | EEA5E93323B722AF00BBE9EF /* QiAppRunInBackground */, 171 | ); 172 | }; 173 | /* End PBXProject section */ 174 | 175 | /* Begin PBXResourcesBuildPhase section */ 176 | EEA5E93223B722AF00BBE9EF /* Resources */ = { 177 | isa = PBXResourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | EEA5E97923B98EF000BBE9EF /* SomethingJustLikeThis.mp3 in Resources */, 181 | EEA5E94723B722B100BBE9EF /* LaunchScreen.storyboard in Resources */, 182 | EEA5E94423B722B100BBE9EF /* Assets.xcassets in Resources */, 183 | EEA5E94223B722AF00BBE9EF /* Main.storyboard in Resources */, 184 | ); 185 | runOnlyForDeploymentPostprocessing = 0; 186 | }; 187 | /* End PBXResourcesBuildPhase section */ 188 | 189 | /* Begin PBXSourcesBuildPhase section */ 190 | EEA5E93023B722AF00BBE9EF /* Sources */ = { 191 | isa = PBXSourcesBuildPhase; 192 | buildActionMask = 2147483647; 193 | files = ( 194 | EEA5E93F23B722AF00BBE9EF /* ViewController.m in Sources */, 195 | EEA5E93923B722AF00BBE9EF /* AppDelegate.m in Sources */, 196 | EE4CF90823BB08C20071863C /* QiTimerViewController.m in Sources */, 197 | EEA5E94A23B722B100BBE9EF /* main.m in Sources */, 198 | EEA5E95223B7232400BBE9EF /* QiBackgroundViewController.m in Sources */, 199 | EEA5E97C23B98F9C00BBE9EF /* QiPlayVideoViewController.m in Sources */, 200 | EE6D228923B9B3AE00C0A2FC /* QiAudioPlayer.m in Sources */, 201 | EE6D228C23B9D52400C0A2FC /* QiDownloadViewController.m in Sources */, 202 | EEA5E93C23B722AF00BBE9EF /* SceneDelegate.m in Sources */, 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXSourcesBuildPhase section */ 207 | 208 | /* Begin PBXVariantGroup section */ 209 | EEA5E94023B722AF00BBE9EF /* Main.storyboard */ = { 210 | isa = PBXVariantGroup; 211 | children = ( 212 | EEA5E94123B722AF00BBE9EF /* Base */, 213 | ); 214 | name = Main.storyboard; 215 | sourceTree = ""; 216 | }; 217 | EEA5E94523B722B100BBE9EF /* LaunchScreen.storyboard */ = { 218 | isa = PBXVariantGroup; 219 | children = ( 220 | EEA5E94623B722B100BBE9EF /* Base */, 221 | ); 222 | name = LaunchScreen.storyboard; 223 | sourceTree = ""; 224 | }; 225 | /* End PBXVariantGroup section */ 226 | 227 | /* Begin XCBuildConfiguration section */ 228 | EEA5E94B23B722B100BBE9EF /* Debug */ = { 229 | isa = XCBuildConfiguration; 230 | buildSettings = { 231 | ALWAYS_SEARCH_USER_PATHS = NO; 232 | CLANG_ANALYZER_NONNULL = YES; 233 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 234 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 235 | CLANG_CXX_LIBRARY = "libc++"; 236 | CLANG_ENABLE_MODULES = YES; 237 | CLANG_ENABLE_OBJC_ARC = YES; 238 | CLANG_ENABLE_OBJC_WEAK = YES; 239 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 240 | CLANG_WARN_BOOL_CONVERSION = YES; 241 | CLANG_WARN_COMMA = YES; 242 | CLANG_WARN_CONSTANT_CONVERSION = YES; 243 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 244 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 245 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 246 | CLANG_WARN_EMPTY_BODY = YES; 247 | CLANG_WARN_ENUM_CONVERSION = YES; 248 | CLANG_WARN_INFINITE_RECURSION = YES; 249 | CLANG_WARN_INT_CONVERSION = YES; 250 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 251 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 252 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 253 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 254 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 255 | CLANG_WARN_STRICT_PROTOTYPES = YES; 256 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 257 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 258 | CLANG_WARN_UNREACHABLE_CODE = YES; 259 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 260 | COPY_PHASE_STRIP = NO; 261 | DEBUG_INFORMATION_FORMAT = dwarf; 262 | ENABLE_STRICT_OBJC_MSGSEND = YES; 263 | ENABLE_TESTABILITY = YES; 264 | GCC_C_LANGUAGE_STANDARD = gnu11; 265 | GCC_DYNAMIC_NO_PIC = NO; 266 | GCC_NO_COMMON_BLOCKS = YES; 267 | GCC_OPTIMIZATION_LEVEL = 0; 268 | GCC_PREPROCESSOR_DEFINITIONS = ( 269 | "DEBUG=1", 270 | "$(inherited)", 271 | ); 272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 274 | GCC_WARN_UNDECLARED_SELECTOR = YES; 275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 276 | GCC_WARN_UNUSED_FUNCTION = YES; 277 | GCC_WARN_UNUSED_VARIABLE = YES; 278 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 279 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 280 | MTL_FAST_MATH = YES; 281 | ONLY_ACTIVE_ARCH = YES; 282 | SDKROOT = iphoneos; 283 | }; 284 | name = Debug; 285 | }; 286 | EEA5E94C23B722B100BBE9EF /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ALWAYS_SEARCH_USER_PATHS = NO; 290 | CLANG_ANALYZER_NONNULL = YES; 291 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 292 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 293 | CLANG_CXX_LIBRARY = "libc++"; 294 | CLANG_ENABLE_MODULES = YES; 295 | CLANG_ENABLE_OBJC_ARC = YES; 296 | CLANG_ENABLE_OBJC_WEAK = YES; 297 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 298 | CLANG_WARN_BOOL_CONVERSION = YES; 299 | CLANG_WARN_COMMA = YES; 300 | CLANG_WARN_CONSTANT_CONVERSION = YES; 301 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 302 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 303 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 304 | CLANG_WARN_EMPTY_BODY = YES; 305 | CLANG_WARN_ENUM_CONVERSION = YES; 306 | CLANG_WARN_INFINITE_RECURSION = YES; 307 | CLANG_WARN_INT_CONVERSION = YES; 308 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 309 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 310 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 311 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 312 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 313 | CLANG_WARN_STRICT_PROTOTYPES = YES; 314 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 315 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 316 | CLANG_WARN_UNREACHABLE_CODE = YES; 317 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 318 | COPY_PHASE_STRIP = NO; 319 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 320 | ENABLE_NS_ASSERTIONS = NO; 321 | ENABLE_STRICT_OBJC_MSGSEND = YES; 322 | GCC_C_LANGUAGE_STANDARD = gnu11; 323 | GCC_NO_COMMON_BLOCKS = YES; 324 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 325 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 326 | GCC_WARN_UNDECLARED_SELECTOR = YES; 327 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 328 | GCC_WARN_UNUSED_FUNCTION = YES; 329 | GCC_WARN_UNUSED_VARIABLE = YES; 330 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 331 | MTL_ENABLE_DEBUG_INFO = NO; 332 | MTL_FAST_MATH = YES; 333 | SDKROOT = iphoneos; 334 | VALIDATE_PRODUCT = YES; 335 | }; 336 | name = Release; 337 | }; 338 | EEA5E94E23B722B100BBE9EF /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 342 | CODE_SIGN_STYLE = Automatic; 343 | DEVELOPMENT_TEAM = 8ZX24TX624; 344 | INFOPLIST_FILE = QiAppRunInBackground/Info.plist; 345 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 346 | LD_RUNPATH_SEARCH_PATHS = ( 347 | "$(inherited)", 348 | "@executable_path/Frameworks", 349 | ); 350 | PRODUCT_BUNDLE_IDENTIFIER = com.qishare.ios.wyw; 351 | PRODUCT_NAME = "$(TARGET_NAME)"; 352 | TARGETED_DEVICE_FAMILY = "1,2"; 353 | }; 354 | name = Debug; 355 | }; 356 | EEA5E94F23B722B100BBE9EF /* Release */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 360 | CODE_SIGN_STYLE = Automatic; 361 | DEVELOPMENT_TEAM = 8ZX24TX624; 362 | INFOPLIST_FILE = QiAppRunInBackground/Info.plist; 363 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 364 | LD_RUNPATH_SEARCH_PATHS = ( 365 | "$(inherited)", 366 | "@executable_path/Frameworks", 367 | ); 368 | PRODUCT_BUNDLE_IDENTIFIER = com.qishare.ios.wyw; 369 | PRODUCT_NAME = "$(TARGET_NAME)"; 370 | TARGETED_DEVICE_FAMILY = "1,2"; 371 | }; 372 | name = Release; 373 | }; 374 | /* End XCBuildConfiguration section */ 375 | 376 | /* Begin XCConfigurationList section */ 377 | EEA5E92F23B722AF00BBE9EF /* Build configuration list for PBXProject "QiAppRunInBackground" */ = { 378 | isa = XCConfigurationList; 379 | buildConfigurations = ( 380 | EEA5E94B23B722B100BBE9EF /* Debug */, 381 | EEA5E94C23B722B100BBE9EF /* Release */, 382 | ); 383 | defaultConfigurationIsVisible = 0; 384 | defaultConfigurationName = Release; 385 | }; 386 | EEA5E94D23B722B100BBE9EF /* Build configuration list for PBXNativeTarget "QiAppRunInBackground" */ = { 387 | isa = XCConfigurationList; 388 | buildConfigurations = ( 389 | EEA5E94E23B722B100BBE9EF /* Debug */, 390 | EEA5E94F23B722B100BBE9EF /* Release */, 391 | ); 392 | defaultConfigurationIsVisible = 0; 393 | defaultConfigurationName = Release; 394 | }; 395 | /* End XCConfigurationList section */ 396 | }; 397 | rootObject = EEA5E92C23B722AF00BBE9EF /* Project object */; 398 | } 399 | --------------------------------------------------------------------------------