├── 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 |
--------------------------------------------------------------------------------