18 |
19 |
20 |
--------------------------------------------------------------------------------
/Wyler/Wyler/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Wyler.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint Wyler.podspec' to ensure this is a
3 | # valid spec and remove all comments before submitting the spec.
4 | #
5 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
6 | #
7 |
8 | Pod::Spec.new do |s|
9 | s.name = "Wyler"
10 | s.version = "0.1"
11 | s.summary = "Screen Recording Made Easy."
12 | s.description = <<-DESC
13 | A light library written in Swift that makes easy the process of Screen Recording for IOS. You can record your app video screen, access to the recorded video, and save it to the Photo Library.
14 | DESC
15 | s.homepage = "https://github.com/toupper/Wyler"
16 | s.license = 'MIT'
17 | s.author = { "César Vargas Casaseca" => "c.vargas.casaseca@gmail.com" }
18 | s.source = { :git => "https://github.com/toupper/Wyler.git", :tag => s.version.to_s }
19 |
20 | s.ios.deployment_target = '11.0'
21 |
22 | s.requires_arc = true
23 |
24 | s.ios.source_files = 'Wyler/Wyler/**/*.{h,m,swift}'
25 |
26 | s.swift_version = "5.2"
27 | end
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 César Vargas Casaseca
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 |
--------------------------------------------------------------------------------
/Wyler/SampleApp/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 |
--------------------------------------------------------------------------------
/Wyler/SampleApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | NSPhotoLibraryUsageDescription
24 | Save Sample Video to Camera Roll
25 | NSMicrophoneUsageDescription
26 | Record audio
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile~iphone
30 | Main
31 | UIRequiredDeviceCapabilities
32 |
33 | armv7
34 |
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Wyler/SampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/.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 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/Wyler/SampleApp/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // SampleApp
4 | //
5 | // Created by Cesar Vargas on 12.04.20.
6 | // Copyright © 2020 Cesar Vargas. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import QuartzCore
12 | import Wyler
13 | import AVFoundation
14 |
15 | final class ViewController: UIViewController {
16 | @IBOutlet weak var bouncingBall: UIView!
17 | @IBOutlet weak var startRecordingButton: UIButton!
18 | @IBOutlet weak var startRecordingWithoutAudioButton: UIButton!
19 | @IBOutlet weak var stopRecordingButton: UIButton!
20 | @IBOutlet weak var cameraRollSwitch: UISwitch!
21 | @IBOutlet weak var cameraRollLabel: UILabel!
22 |
23 | private var player: AVAudioPlayer?
24 | private let screenRecorder = ScreenRecorder()
25 |
26 | override func viewDidLoad() {
27 | super.viewDidLoad()
28 |
29 | bouncingBall.layer.cornerRadius = bouncingBall.frame.width / 2
30 | }
31 |
32 | @IBAction func stopRecordingButtonWasPressed(_ sender: Any) {
33 | enableElements(isRecording: false)
34 |
35 | bouncingBall.layer.removeAllAnimations()
36 | self.bouncingBall.alpha = 1
37 |
38 | screenRecorder.stoprecording(errorHandler: { error in
39 | debugPrint("Error when stop recording \(error)")
40 | })
41 | }
42 |
43 | @IBAction func startRecordingButtonWasPressed(_ sender: Any) {
44 | enableElements(isRecording: true)
45 |
46 | playSound()
47 | animateBall()
48 |
49 | screenRecorder.startRecording(saveToCameraRoll: cameraRollSwitch.isOn,
50 | errorHandler: { error in
51 | debugPrint("Error when recording \(error)")
52 | })
53 | }
54 |
55 | @IBAction func startRecordingButtonWithoutAudioWasPressed(_ sender: Any) {
56 | enableElements(isRecording: true)
57 |
58 | playSound()
59 | animateBall()
60 |
61 | screenRecorder.startRecording(saveToCameraRoll: cameraRollSwitch.isOn,
62 | recordAudio: false,
63 | errorHandler: { error in
64 | debugPrint("Error when recording \(error)")
65 | })
66 | }
67 |
68 | private func playSound() {
69 | guard let url = Bundle.main.url(forResource: "MP3Sample", withExtension: "mp3") else { return }
70 |
71 | do {
72 | try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
73 | try AVAudioSession.sharedInstance().setActive(true)
74 |
75 | player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
76 |
77 | player?.play()
78 |
79 | } catch let error {
80 | print(error.localizedDescription)
81 | }
82 | }
83 |
84 | private func enableElements(isRecording: Bool) {
85 | startRecordingButton.isEnabled = !isRecording
86 | startRecordingWithoutAudioButton.isEnabled = !isRecording
87 | stopRecordingButton.isEnabled = isRecording
88 | cameraRollSwitch.isEnabled = !isRecording
89 | cameraRollLabel.isEnabled = !isRecording
90 | }
91 |
92 | private func animateBall() {
93 | UIView.animate(withDuration: 2,
94 | delay: 1,
95 | usingSpringWithDamping: 0.5,
96 | initialSpringVelocity: 5,
97 | options: [.curveEaseInOut, .repeat, .autoreverse], animations: {
98 | self.bouncingBall.alpha = 0.25
99 | }, completion: { _ in })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Welcome to **Wyler** — A light library written in Swift that makes easy the process of Screen Recording for IOS. You can record your app video screen, access to the recorded video, and save it to the Photo Library.
22 |
23 | ## Features
24 |
25 | - [x] App Screen Recording
26 | - [x] Set Video Size
27 | - [x] Access to the Video
28 | - [x] Save the video to the Photo Library
29 |
30 | ## Requirements
31 |
32 | - iOS 11.0+
33 | - Xcode 11.0+
34 |
35 | ## Installation
36 | Since Wyler is implemented within a single file, the easiest way to use it is to simply drag and drop it into your Xcode project. If anyways you want to use a dependency manager:
37 |
38 | #### CocoaPods
39 | You can use [CocoaPods](http://cocoapods.org/) to install `Wyler` by adding it to your `Podfile`:
40 |
41 | ```ruby
42 | platform :ios, '11.0'
43 | use_frameworks!
44 | pod 'Wyler'
45 | ```
46 |
47 | To get the full benefits import `Wyler` wherever you use it
48 |
49 | ``` swift
50 | import Wyler
51 | ```
52 | ### Carthage
53 |
54 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`:
55 |
56 | ```ogdl
57 | github "toupper/Wyler"
58 | ```
59 | ## Manually
60 |
61 | You can also integrate Wyler into your project manually.
62 |
63 | #### Embedded Framework
64 |
65 | - Open up Terminal, `cd` into your top-level project directory, and run the following command "if" your project is not initialized as a git repository:
66 |
67 | ```bash
68 | $ git init
69 | ```
70 |
71 | - Add Wyler as a git [submodule](https://git-scm.com/docs/git-submodule) by running the following command:
72 |
73 | ```bash
74 | $ git submodule add https://github.com/toupper/Wyler.git
75 | ```
76 |
77 | - Open the new `Wyler` folder, and drag the `Wyler.xcodeproj` into the Project Navigator of your application's Xcode project.
78 |
79 | - And that's it!
80 |
81 | ## Usage example
82 |
83 | ### Recording
84 |
85 | Import Wyler in the file you are going to use it. Create an instance of ```ScreenRecorder```, and call it to start recording whenever you want:
86 |
87 | ```swift
88 | import Wyler
89 |
90 | screenRecorder.startRecording(saveToCameraRoll: true, errorHandler: { error in
91 | debugPrint("Error when recording \(error)")
92 | })
93 | ```
94 | If you want to access the video, turn off audio recording, or set a different size than the App screen, you can pass these parameters:
95 |
96 | ```swift
97 | import Wyler
98 |
99 | screenRecorder.startRecording(to: yourInternalURL,
100 | size: yourSize,
101 | saveToCameraRoll: true,
102 | recordAudio: shouldRecordAudio,
103 | errorHandler: { error in
104 | debugPrint("Error when recording \(error)")
105 | })
106 | ```
107 |
108 |
109 | When you want to stop recording, you just have to call the recorder with stop recording:
110 |
111 | ```swift
112 | import Wyler
113 |
114 | screenRecorder.stoprecording(errorHandler: { error in
115 | debugPrint("Error when stop recording \(error)")
116 | })
117 | ```
118 | If you want to save the video to the camera, do not forget to add the Privacy - Photo Library Usage Description to the Info.plist
119 | ## Contribute
120 |
121 | We would love you for the contribution to **Wyler**, check the ``LICENSE`` file for more info.
122 |
123 | ## Credits
124 |
125 | Created and maintained with love by [César Vargas Casaseca](https://github.com/toupper). You can follow me on Medium [@toupper](https://medium.com/@toupper) for project updates, releases and more stories.
126 |
127 | ## License
128 |
129 | Wyler is released under the MIT license. [See LICENSE](https://github.com/toupper/Wyler/blob/master/LICENSE) for details.
130 |
--------------------------------------------------------------------------------
/Wyler/Wyler/ScreenRecorder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScreenRecorder.swift
3 | // Wyler
4 | //
5 | // Created by Cesar Vargas on 10.04.20.
6 | // Copyright © 2020 Cesar Vargas. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReplayKit
11 | import Photos
12 |
13 | public enum ScreenRecorderError: Error {
14 | case notAvailable
15 | case photoLibraryAccessNotGranted
16 | }
17 |
18 | final public class ScreenRecorder {
19 | private var videoOutputURL: URL?
20 | private var videoWriter: AVAssetWriter?
21 | private var videoWriterInput: AVAssetWriterInput?
22 | private var micAudioWriterInput: AVAssetWriterInput?
23 | private var appAudioWriterInput: AVAssetWriterInput?
24 | private var saveToCameraRoll = false
25 | private var recordAudio = true
26 | let recorder = RPScreenRecorder.shared()
27 |
28 | /**
29 | Starts recording the content of the application screen. It works together with stopRecording
30 |
31 | - Parameter outputURL: The output where the video will be saved. If nil, it saves it in the documents directory.
32 | - Parameter size: The size of the video. If nil, it will use the app screen size.
33 | - Parameter saveToCameraRoll: Whether to save it to camera roll. False by default.
34 | - Parameter recordAudio: Whether to record audio as well as video. True by default for compatibility.
35 | - Parameter errorHandler: Called when an error is found
36 | */
37 | public func startRecording(to outputURL: URL? = nil,
38 | size: CGSize? = nil,
39 | saveToCameraRoll: Bool = false,
40 | recordAudio: Bool = true,
41 | errorHandler: @escaping (Error) -> Void) {
42 | do {
43 | try createVideoWriter(in: outputURL)
44 | addVideoWriterInput(size: size)
45 | self.recordAudio = recordAudio
46 | self.recorder.isMicrophoneEnabled = recordAudio
47 | if(recordAudio) {
48 | self.micAudioWriterInput = createAndAddAudioInput()
49 | self.appAudioWriterInput = createAndAddAudioInput()
50 | }
51 | startCapture(error: errorHandler)
52 | } catch let err {
53 | errorHandler(err)
54 | }
55 | }
56 |
57 | private func checkPhotoLibraryAuthorizationStatus() {
58 | let status = PHPhotoLibrary.authorizationStatus()
59 | if status == .notDetermined {
60 | PHPhotoLibrary.requestAuthorization({ _ in })
61 | }
62 | }
63 |
64 | private func createVideoWriter(in outputURL: URL? = nil) throws {
65 | let newVideoOutputURL: URL
66 |
67 | if let passedVideoOutput = outputURL {
68 | self.videoOutputURL = passedVideoOutput
69 | newVideoOutputURL = passedVideoOutput
70 | } else {
71 | let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
72 | newVideoOutputURL = URL(fileURLWithPath: documentsPath.appendingPathComponent("WylerNewVideo.mp4"))
73 | self.videoOutputURL = newVideoOutputURL
74 | }
75 |
76 | do {
77 | try FileManager.default.removeItem(at: newVideoOutputURL)
78 | } catch {}
79 |
80 | do {
81 | try videoWriter = AVAssetWriter(outputURL: newVideoOutputURL, fileType: AVFileType.mp4)
82 | } catch let writerError as NSError {
83 | videoWriter = nil
84 | throw writerError
85 | }
86 | }
87 |
88 | private func addVideoWriterInput(size: CGSize?) {
89 | let passingSize: CGSize = size ?? UIScreen.main.bounds.size
90 |
91 | let videoSettings: [String: Any] = [AVVideoCodecKey: AVVideoCodecType.h264,
92 | AVVideoWidthKey: passingSize.width,
93 | AVVideoHeightKey: passingSize.height]
94 |
95 | let newVideoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)
96 | self.videoWriterInput = newVideoWriterInput
97 | newVideoWriterInput.expectsMediaDataInRealTime = true
98 | videoWriter?.add(newVideoWriterInput)
99 | }
100 |
101 | private func createAndAddAudioInput() -> AVAssetWriterInput {
102 | let settings = [
103 | AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
104 | AVSampleRateKey: 12000,
105 | AVNumberOfChannelsKey: 1,
106 | AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
107 | ]
108 |
109 | let audioInput = AVAssetWriterInput(mediaType: .audio, outputSettings: settings)
110 |
111 | audioInput.expectsMediaDataInRealTime = true
112 | videoWriter?.add(audioInput)
113 |
114 | return audioInput
115 | }
116 |
117 | private func startCapture(handler: @escaping (Error?) -> Void) {
118 | guard recorder.isAvailable else {
119 | return handler(ScreenRecorderError.notAvailable)
120 | }
121 | recorder.startCapture(handler: { (sampleBuffer, sampleType, passedError) in
122 | if let passedError = passedError {
123 | handler(passedError)
124 | }
125 |
126 | switch sampleType {
127 | case .video:
128 | self.handleSampleBuffer(sampleBuffer: sampleBuffer)
129 | case .audioApp:
130 | self.add(sample: sampleBuffer, to: self.appAudioWriterInput)
131 | case .audioMic:
132 | self.add(sample: sampleBuffer, to: self.micAudioWriterInput)
133 | default:
134 | break
135 | }
136 | handler(nil)
137 | })
138 | }
139 |
140 | private func handleSampleBuffer(sampleBuffer: CMSampleBuffer) {
141 | if self.videoWriter?.status == AVAssetWriter.Status.unknown {
142 | self.videoWriter?.startWriting()
143 | self.videoWriter?.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
144 | } else if self.videoWriter?.status == AVAssetWriter.Status.writing &&
145 | self.videoWriterInput?.isReadyForMoreMediaData == true {
146 | self.videoWriterInput?.append(sampleBuffer)
147 | }
148 | }
149 |
150 | private func add(sample: CMSampleBuffer, to writerInput: AVAssetWriterInput?) {
151 | if writerInput?.isReadyForMoreMediaData ?? false {
152 | writerInput?.append(sample)
153 | }
154 | }
155 |
156 | /**
157 | Stops recording the content of the application screen, after calling startRecording
158 |
159 | - Parameter errorHandler: Called when an error is found
160 | */
161 | public func stoprecording(handler: @escaping (Error?) -> Void) {
162 | recorder.stopCapture( handler: { error in
163 | if let error = error {
164 | handler(error)
165 | } else {
166 | self.videoWriterInput?.markAsFinished()
167 | if(self.recordAudio) {
168 | self.micAudioWriterInput?.markAsFinished()
169 | self.appAudioWriterInput?.markAsFinished()
170 | }
171 | self.videoWriter?.finishWriting {
172 | self.saveVideoToCameraRollAfterAuthorized(handler: handler)
173 | }
174 | }
175 | })
176 |
177 | self.videoWriterInput?.markAsFinished()
178 | if(self.recordAudio) {
179 | self.micAudioWriterInput?.markAsFinished()
180 | self.appAudioWriterInput?.markAsFinished()
181 | }
182 | self.videoWriter?.finishWriting {
183 | self.saveVideoToCameraRollAfterAuthorized(errorHandler: errorHandler)
184 | }
185 | }
186 |
187 | private func saveVideoToCameraRollAfterAuthorized(handler: @escaping (Error?) -> Void) {
188 | if PHPhotoLibrary.authorizationStatus() == .authorized {
189 | self.saveVideoToCameraRoll(handler: handler)
190 | } else {
191 | PHPhotoLibrary.requestAuthorization({ (status) in
192 | if status == .authorized {
193 | self.saveVideoToCameraRoll(handler: handler)
194 | } else {
195 | handler(ScreenRecorderError.photoLibraryAccessNotGranted)
196 | }
197 | })
198 | }
199 | }
200 |
201 | private func saveVideoToCameraRoll(handler: @escaping (Error?) -> Void) {
202 | guard let videoOutputURL = self.videoOutputURL else {
203 | return handler(nil)
204 | }
205 |
206 | PHPhotoLibrary.shared().performChanges({
207 | PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoOutputURL)
208 | }, completionHandler: { _, error in
209 | if let error = error {
210 | handler(error)
211 | } else {
212 | handler(nil)
213 | }
214 | })
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/Wyler/SampleApp/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 |
31 |
39 |
40 |
41 |
42 |
48 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/Wyler/Wyler.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 370DA4C62440D8C300BAB2B7 /* Wyler.h in Headers */ = {isa = PBXBuildFile; fileRef = 370DA4C42440D8C300BAB2B7 /* Wyler.h */; settings = {ATTRIBUTES = (Public, ); }; };
11 | 370DA4D32440D8DA00BAB2B7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370DA4D22440D8DA00BAB2B7 /* AppDelegate.swift */; };
12 | 370DA4D92440D8DE00BAB2B7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 370DA4D82440D8DE00BAB2B7 /* Assets.xcassets */; };
13 | 370DA4DC2440D8DE00BAB2B7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 370DA4DB2440D8DE00BAB2B7 /* Preview Assets.xcassets */; };
14 | 370DA4DF2440D8DE00BAB2B7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 370DA4DD2440D8DE00BAB2B7 /* LaunchScreen.storyboard */; };
15 | 370DA4E52440D96900BAB2B7 /* ScreenRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370DA4E42440D96900BAB2B7 /* ScreenRecorder.swift */; };
16 | 370DA4E82443678400BAB2B7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 370DA4E72443678400BAB2B7 /* Main.storyboard */; };
17 | 370DA4EA244367A700BAB2B7 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370DA4E9244367A700BAB2B7 /* ViewController.swift */; };
18 | 370DA4EC2443710500BAB2B7 /* Wyler.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 370DA4C12440D8C300BAB2B7 /* Wyler.framework */; };
19 | 370DA4ED2443710500BAB2B7 /* Wyler.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 370DA4C12440D8C300BAB2B7 /* Wyler.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
20 | 37B519B7244C71D30048B034 /* MP3Sample.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 37B519B6244C71D30048B034 /* MP3Sample.mp3 */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXContainerItemProxy section */
24 | 370DA4EE2443710500BAB2B7 /* PBXContainerItemProxy */ = {
25 | isa = PBXContainerItemProxy;
26 | containerPortal = 370DA4B82440D8C300BAB2B7 /* Project object */;
27 | proxyType = 1;
28 | remoteGlobalIDString = 370DA4C02440D8C300BAB2B7;
29 | remoteInfo = Wyler;
30 | };
31 | /* End PBXContainerItemProxy section */
32 |
33 | /* Begin PBXCopyFilesBuildPhase section */
34 | 370DA4F02443710500BAB2B7 /* Embed Frameworks */ = {
35 | isa = PBXCopyFilesBuildPhase;
36 | buildActionMask = 2147483647;
37 | dstPath = "";
38 | dstSubfolderSpec = 10;
39 | files = (
40 | 370DA4ED2443710500BAB2B7 /* Wyler.framework in Embed Frameworks */,
41 | );
42 | name = "Embed Frameworks";
43 | runOnlyForDeploymentPostprocessing = 0;
44 | };
45 | /* End PBXCopyFilesBuildPhase section */
46 |
47 | /* Begin PBXFileReference section */
48 | 370DA4C12440D8C300BAB2B7 /* Wyler.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Wyler.framework; sourceTree = BUILT_PRODUCTS_DIR; };
49 | 370DA4C42440D8C300BAB2B7 /* Wyler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Wyler.h; sourceTree = ""; };
50 | 370DA4C52440D8C300BAB2B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
51 | 370DA4D02440D8DA00BAB2B7 /* SampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
52 | 370DA4D22440D8DA00BAB2B7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
53 | 370DA4D82440D8DE00BAB2B7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
54 | 370DA4DB2440D8DE00BAB2B7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
55 | 370DA4DE2440D8DE00BAB2B7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
56 | 370DA4E02440D8DE00BAB2B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
57 | 370DA4E42440D96900BAB2B7 /* ScreenRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenRecorder.swift; sourceTree = ""; };
58 | 370DA4E72443678400BAB2B7 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; };
59 | 370DA4E9244367A700BAB2B7 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
60 | 37B519B6244C71D30048B034 /* MP3Sample.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = MP3Sample.mp3; sourceTree = ""; };
61 | /* End PBXFileReference section */
62 |
63 | /* Begin PBXFrameworksBuildPhase section */
64 | 370DA4BE2440D8C300BAB2B7 /* Frameworks */ = {
65 | isa = PBXFrameworksBuildPhase;
66 | buildActionMask = 2147483647;
67 | files = (
68 | );
69 | runOnlyForDeploymentPostprocessing = 0;
70 | };
71 | 370DA4CD2440D8DA00BAB2B7 /* Frameworks */ = {
72 | isa = PBXFrameworksBuildPhase;
73 | buildActionMask = 2147483647;
74 | files = (
75 | 370DA4EC2443710500BAB2B7 /* Wyler.framework in Frameworks */,
76 | );
77 | runOnlyForDeploymentPostprocessing = 0;
78 | };
79 | /* End PBXFrameworksBuildPhase section */
80 |
81 | /* Begin PBXGroup section */
82 | 370DA4B72440D8C300BAB2B7 = {
83 | isa = PBXGroup;
84 | children = (
85 | 370DA4C32440D8C300BAB2B7 /* Wyler */,
86 | 370DA4D12440D8DA00BAB2B7 /* SampleApp */,
87 | 370DA4C22440D8C300BAB2B7 /* Products */,
88 | 370DA4EB2443710500BAB2B7 /* Frameworks */,
89 | );
90 | sourceTree = "";
91 | };
92 | 370DA4C22440D8C300BAB2B7 /* Products */ = {
93 | isa = PBXGroup;
94 | children = (
95 | 370DA4C12440D8C300BAB2B7 /* Wyler.framework */,
96 | 370DA4D02440D8DA00BAB2B7 /* SampleApp.app */,
97 | );
98 | name = Products;
99 | sourceTree = "";
100 | };
101 | 370DA4C32440D8C300BAB2B7 /* Wyler */ = {
102 | isa = PBXGroup;
103 | children = (
104 | 370DA4C42440D8C300BAB2B7 /* Wyler.h */,
105 | 370DA4C52440D8C300BAB2B7 /* Info.plist */,
106 | 370DA4E42440D96900BAB2B7 /* ScreenRecorder.swift */,
107 | );
108 | path = Wyler;
109 | sourceTree = "";
110 | };
111 | 370DA4D12440D8DA00BAB2B7 /* SampleApp */ = {
112 | isa = PBXGroup;
113 | children = (
114 | 37B519B6244C71D30048B034 /* MP3Sample.mp3 */,
115 | 370DA4E9244367A700BAB2B7 /* ViewController.swift */,
116 | 370DA4E72443678400BAB2B7 /* Main.storyboard */,
117 | 370DA4D22440D8DA00BAB2B7 /* AppDelegate.swift */,
118 | 370DA4D82440D8DE00BAB2B7 /* Assets.xcassets */,
119 | 370DA4DD2440D8DE00BAB2B7 /* LaunchScreen.storyboard */,
120 | 370DA4E02440D8DE00BAB2B7 /* Info.plist */,
121 | 370DA4DA2440D8DE00BAB2B7 /* Preview Content */,
122 | );
123 | path = SampleApp;
124 | sourceTree = "";
125 | };
126 | 370DA4DA2440D8DE00BAB2B7 /* Preview Content */ = {
127 | isa = PBXGroup;
128 | children = (
129 | 370DA4DB2440D8DE00BAB2B7 /* Preview Assets.xcassets */,
130 | );
131 | path = "Preview Content";
132 | sourceTree = "";
133 | };
134 | 370DA4EB2443710500BAB2B7 /* Frameworks */ = {
135 | isa = PBXGroup;
136 | children = (
137 | );
138 | name = Frameworks;
139 | sourceTree = "";
140 | };
141 | /* End PBXGroup section */
142 |
143 | /* Begin PBXHeadersBuildPhase section */
144 | 370DA4BC2440D8C300BAB2B7 /* Headers */ = {
145 | isa = PBXHeadersBuildPhase;
146 | buildActionMask = 2147483647;
147 | files = (
148 | 370DA4C62440D8C300BAB2B7 /* Wyler.h in Headers */,
149 | );
150 | runOnlyForDeploymentPostprocessing = 0;
151 | };
152 | /* End PBXHeadersBuildPhase section */
153 |
154 | /* Begin PBXNativeTarget section */
155 | 370DA4C02440D8C300BAB2B7 /* Wyler */ = {
156 | isa = PBXNativeTarget;
157 | buildConfigurationList = 370DA4C92440D8C300BAB2B7 /* Build configuration list for PBXNativeTarget "Wyler" */;
158 | buildPhases = (
159 | 370DA4BC2440D8C300BAB2B7 /* Headers */,
160 | 370DA4E624422F6A00BAB2B7 /* SwiftLint */,
161 | 370DA4BD2440D8C300BAB2B7 /* Sources */,
162 | 370DA4BE2440D8C300BAB2B7 /* Frameworks */,
163 | 370DA4BF2440D8C300BAB2B7 /* Resources */,
164 | );
165 | buildRules = (
166 | );
167 | dependencies = (
168 | );
169 | name = Wyler;
170 | productName = Wyler;
171 | productReference = 370DA4C12440D8C300BAB2B7 /* Wyler.framework */;
172 | productType = "com.apple.product-type.framework";
173 | };
174 | 370DA4CF2440D8DA00BAB2B7 /* SampleApp */ = {
175 | isa = PBXNativeTarget;
176 | buildConfigurationList = 370DA4E12440D8DE00BAB2B7 /* Build configuration list for PBXNativeTarget "SampleApp" */;
177 | buildPhases = (
178 | 370DA4CC2440D8DA00BAB2B7 /* Sources */,
179 | 370DA4CD2440D8DA00BAB2B7 /* Frameworks */,
180 | 370DA4CE2440D8DA00BAB2B7 /* Resources */,
181 | 370DA4F02443710500BAB2B7 /* Embed Frameworks */,
182 | );
183 | buildRules = (
184 | );
185 | dependencies = (
186 | 370DA4EF2443710500BAB2B7 /* PBXTargetDependency */,
187 | );
188 | name = SampleApp;
189 | productName = SampleApp;
190 | productReference = 370DA4D02440D8DA00BAB2B7 /* SampleApp.app */;
191 | productType = "com.apple.product-type.application";
192 | };
193 | /* End PBXNativeTarget section */
194 |
195 | /* Begin PBXProject section */
196 | 370DA4B82440D8C300BAB2B7 /* Project object */ = {
197 | isa = PBXProject;
198 | attributes = {
199 | LastSwiftUpdateCheck = 1140;
200 | LastUpgradeCheck = 1140;
201 | ORGANIZATIONNAME = "Cesar Vargas";
202 | TargetAttributes = {
203 | 370DA4C02440D8C300BAB2B7 = {
204 | CreatedOnToolsVersion = 11.4;
205 | LastSwiftMigration = 1140;
206 | };
207 | 370DA4CF2440D8DA00BAB2B7 = {
208 | CreatedOnToolsVersion = 11.4;
209 | };
210 | };
211 | };
212 | buildConfigurationList = 370DA4BB2440D8C300BAB2B7 /* Build configuration list for PBXProject "Wyler" */;
213 | compatibilityVersion = "Xcode 9.3";
214 | developmentRegion = en;
215 | hasScannedForEncodings = 0;
216 | knownRegions = (
217 | en,
218 | Base,
219 | );
220 | mainGroup = 370DA4B72440D8C300BAB2B7;
221 | productRefGroup = 370DA4C22440D8C300BAB2B7 /* Products */;
222 | projectDirPath = "";
223 | projectRoot = "";
224 | targets = (
225 | 370DA4C02440D8C300BAB2B7 /* Wyler */,
226 | 370DA4CF2440D8DA00BAB2B7 /* SampleApp */,
227 | );
228 | };
229 | /* End PBXProject section */
230 |
231 | /* Begin PBXResourcesBuildPhase section */
232 | 370DA4BF2440D8C300BAB2B7 /* Resources */ = {
233 | isa = PBXResourcesBuildPhase;
234 | buildActionMask = 2147483647;
235 | files = (
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | };
239 | 370DA4CE2440D8DA00BAB2B7 /* Resources */ = {
240 | isa = PBXResourcesBuildPhase;
241 | buildActionMask = 2147483647;
242 | files = (
243 | 370DA4DF2440D8DE00BAB2B7 /* LaunchScreen.storyboard in Resources */,
244 | 37B519B7244C71D30048B034 /* MP3Sample.mp3 in Resources */,
245 | 370DA4DC2440D8DE00BAB2B7 /* Preview Assets.xcassets in Resources */,
246 | 370DA4D92440D8DE00BAB2B7 /* Assets.xcassets in Resources */,
247 | 370DA4E82443678400BAB2B7 /* Main.storyboard in Resources */,
248 | );
249 | runOnlyForDeploymentPostprocessing = 0;
250 | };
251 | /* End PBXResourcesBuildPhase section */
252 |
253 | /* Begin PBXShellScriptBuildPhase section */
254 | 370DA4E624422F6A00BAB2B7 /* SwiftLint */ = {
255 | isa = PBXShellScriptBuildPhase;
256 | buildActionMask = 2147483647;
257 | files = (
258 | );
259 | inputFileListPaths = (
260 | );
261 | inputPaths = (
262 | );
263 | name = SwiftLint;
264 | outputFileListPaths = (
265 | );
266 | outputPaths = (
267 | );
268 | runOnlyForDeploymentPostprocessing = 0;
269 | shellPath = /bin/sh;
270 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
271 | };
272 | /* End PBXShellScriptBuildPhase section */
273 |
274 | /* Begin PBXSourcesBuildPhase section */
275 | 370DA4BD2440D8C300BAB2B7 /* Sources */ = {
276 | isa = PBXSourcesBuildPhase;
277 | buildActionMask = 2147483647;
278 | files = (
279 | 370DA4E52440D96900BAB2B7 /* ScreenRecorder.swift in Sources */,
280 | );
281 | runOnlyForDeploymentPostprocessing = 0;
282 | };
283 | 370DA4CC2440D8DA00BAB2B7 /* Sources */ = {
284 | isa = PBXSourcesBuildPhase;
285 | buildActionMask = 2147483647;
286 | files = (
287 | 370DA4EA244367A700BAB2B7 /* ViewController.swift in Sources */,
288 | 370DA4D32440D8DA00BAB2B7 /* AppDelegate.swift in Sources */,
289 | );
290 | runOnlyForDeploymentPostprocessing = 0;
291 | };
292 | /* End PBXSourcesBuildPhase section */
293 |
294 | /* Begin PBXTargetDependency section */
295 | 370DA4EF2443710500BAB2B7 /* PBXTargetDependency */ = {
296 | isa = PBXTargetDependency;
297 | target = 370DA4C02440D8C300BAB2B7 /* Wyler */;
298 | targetProxy = 370DA4EE2443710500BAB2B7 /* PBXContainerItemProxy */;
299 | };
300 | /* End PBXTargetDependency section */
301 |
302 | /* Begin PBXVariantGroup section */
303 | 370DA4DD2440D8DE00BAB2B7 /* LaunchScreen.storyboard */ = {
304 | isa = PBXVariantGroup;
305 | children = (
306 | 370DA4DE2440D8DE00BAB2B7 /* Base */,
307 | );
308 | name = LaunchScreen.storyboard;
309 | sourceTree = "";
310 | };
311 | /* End PBXVariantGroup section */
312 |
313 | /* Begin XCBuildConfiguration section */
314 | 370DA4C72440D8C300BAB2B7 /* Debug */ = {
315 | isa = XCBuildConfiguration;
316 | buildSettings = {
317 | ALWAYS_SEARCH_USER_PATHS = NO;
318 | CLANG_ANALYZER_NONNULL = YES;
319 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
321 | CLANG_CXX_LIBRARY = "libc++";
322 | CLANG_ENABLE_MODULES = YES;
323 | CLANG_ENABLE_OBJC_ARC = YES;
324 | CLANG_ENABLE_OBJC_WEAK = YES;
325 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
326 | CLANG_WARN_BOOL_CONVERSION = YES;
327 | CLANG_WARN_COMMA = YES;
328 | CLANG_WARN_CONSTANT_CONVERSION = YES;
329 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
331 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
332 | CLANG_WARN_EMPTY_BODY = YES;
333 | CLANG_WARN_ENUM_CONVERSION = YES;
334 | CLANG_WARN_INFINITE_RECURSION = YES;
335 | CLANG_WARN_INT_CONVERSION = YES;
336 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
337 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
340 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
341 | CLANG_WARN_STRICT_PROTOTYPES = YES;
342 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
343 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
344 | CLANG_WARN_UNREACHABLE_CODE = YES;
345 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
346 | COPY_PHASE_STRIP = NO;
347 | CURRENT_PROJECT_VERSION = 1;
348 | DEBUG_INFORMATION_FORMAT = dwarf;
349 | ENABLE_STRICT_OBJC_MSGSEND = YES;
350 | ENABLE_TESTABILITY = YES;
351 | GCC_C_LANGUAGE_STANDARD = gnu11;
352 | GCC_DYNAMIC_NO_PIC = NO;
353 | GCC_NO_COMMON_BLOCKS = YES;
354 | GCC_OPTIMIZATION_LEVEL = 0;
355 | GCC_PREPROCESSOR_DEFINITIONS = (
356 | "DEBUG=1",
357 | "$(inherited)",
358 | );
359 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
360 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
361 | GCC_WARN_UNDECLARED_SELECTOR = YES;
362 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
363 | GCC_WARN_UNUSED_FUNCTION = YES;
364 | GCC_WARN_UNUSED_VARIABLE = YES;
365 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
366 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
367 | MTL_FAST_MATH = YES;
368 | ONLY_ACTIVE_ARCH = YES;
369 | SDKROOT = iphoneos;
370 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
371 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
372 | VERSIONING_SYSTEM = "apple-generic";
373 | VERSION_INFO_PREFIX = "";
374 | };
375 | name = Debug;
376 | };
377 | 370DA4C82440D8C300BAB2B7 /* Release */ = {
378 | isa = XCBuildConfiguration;
379 | buildSettings = {
380 | ALWAYS_SEARCH_USER_PATHS = NO;
381 | CLANG_ANALYZER_NONNULL = YES;
382 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
383 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
384 | CLANG_CXX_LIBRARY = "libc++";
385 | CLANG_ENABLE_MODULES = YES;
386 | CLANG_ENABLE_OBJC_ARC = YES;
387 | CLANG_ENABLE_OBJC_WEAK = YES;
388 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
389 | CLANG_WARN_BOOL_CONVERSION = YES;
390 | CLANG_WARN_COMMA = YES;
391 | CLANG_WARN_CONSTANT_CONVERSION = YES;
392 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
393 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
394 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
395 | CLANG_WARN_EMPTY_BODY = YES;
396 | CLANG_WARN_ENUM_CONVERSION = YES;
397 | CLANG_WARN_INFINITE_RECURSION = YES;
398 | CLANG_WARN_INT_CONVERSION = YES;
399 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
400 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
401 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
402 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
403 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
404 | CLANG_WARN_STRICT_PROTOTYPES = YES;
405 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
406 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
407 | CLANG_WARN_UNREACHABLE_CODE = YES;
408 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
409 | COPY_PHASE_STRIP = NO;
410 | CURRENT_PROJECT_VERSION = 1;
411 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
412 | ENABLE_NS_ASSERTIONS = NO;
413 | ENABLE_STRICT_OBJC_MSGSEND = YES;
414 | GCC_C_LANGUAGE_STANDARD = gnu11;
415 | GCC_NO_COMMON_BLOCKS = YES;
416 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
417 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
418 | GCC_WARN_UNDECLARED_SELECTOR = YES;
419 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
420 | GCC_WARN_UNUSED_FUNCTION = YES;
421 | GCC_WARN_UNUSED_VARIABLE = YES;
422 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
423 | MTL_ENABLE_DEBUG_INFO = NO;
424 | MTL_FAST_MATH = YES;
425 | SDKROOT = iphoneos;
426 | SWIFT_COMPILATION_MODE = wholemodule;
427 | SWIFT_OPTIMIZATION_LEVEL = "-O";
428 | VALIDATE_PRODUCT = YES;
429 | VERSIONING_SYSTEM = "apple-generic";
430 | VERSION_INFO_PREFIX = "";
431 | };
432 | name = Release;
433 | };
434 | 370DA4CA2440D8C300BAB2B7 /* Debug */ = {
435 | isa = XCBuildConfiguration;
436 | buildSettings = {
437 | CLANG_ENABLE_MODULES = YES;
438 | CODE_SIGN_IDENTITY = "Apple Development";
439 | CODE_SIGN_STYLE = Automatic;
440 | DEFINES_MODULE = YES;
441 | DEVELOPMENT_TEAM = 67VC2B47W7;
442 | DYLIB_COMPATIBILITY_VERSION = 1;
443 | DYLIB_CURRENT_VERSION = 1;
444 | DYLIB_INSTALL_NAME_BASE = "@rpath";
445 | INFOPLIST_FILE = Wyler/Info.plist;
446 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
447 | LD_RUNPATH_SEARCH_PATHS = (
448 | "$(inherited)",
449 | "@executable_path/Frameworks",
450 | "@loader_path/Frameworks",
451 | );
452 | PRODUCT_BUNDLE_IDENTIFIER = com.casaseca.Wyler;
453 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
454 | PROVISIONING_PROFILE_SPECIFIER = "";
455 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
456 | SKIP_INSTALL = YES;
457 | SUPPORTS_MACCATALYST = NO;
458 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
459 | SWIFT_VERSION = 5.0;
460 | TARGETED_DEVICE_FAMILY = "1,2";
461 | };
462 | name = Debug;
463 | };
464 | 370DA4CB2440D8C300BAB2B7 /* Release */ = {
465 | isa = XCBuildConfiguration;
466 | buildSettings = {
467 | CLANG_ENABLE_MODULES = YES;
468 | CODE_SIGN_IDENTITY = "Apple Development";
469 | CODE_SIGN_STYLE = Automatic;
470 | DEFINES_MODULE = YES;
471 | DEVELOPMENT_TEAM = 67VC2B47W7;
472 | DYLIB_COMPATIBILITY_VERSION = 1;
473 | DYLIB_CURRENT_VERSION = 1;
474 | DYLIB_INSTALL_NAME_BASE = "@rpath";
475 | INFOPLIST_FILE = Wyler/Info.plist;
476 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
477 | LD_RUNPATH_SEARCH_PATHS = (
478 | "$(inherited)",
479 | "@executable_path/Frameworks",
480 | "@loader_path/Frameworks",
481 | );
482 | PRODUCT_BUNDLE_IDENTIFIER = com.casaseca.Wyler;
483 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
484 | PROVISIONING_PROFILE_SPECIFIER = "";
485 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
486 | SKIP_INSTALL = YES;
487 | SUPPORTS_MACCATALYST = NO;
488 | SWIFT_VERSION = 5.0;
489 | TARGETED_DEVICE_FAMILY = "1,2";
490 | };
491 | name = Release;
492 | };
493 | 370DA4E22440D8DE00BAB2B7 /* Debug */ = {
494 | isa = XCBuildConfiguration;
495 | buildSettings = {
496 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
497 | CODE_SIGN_STYLE = Automatic;
498 | DEVELOPMENT_ASSET_PATHS = "\"SampleApp/Preview Content\"";
499 | DEVELOPMENT_TEAM = EKP9ZV782L;
500 | ENABLE_PREVIEWS = YES;
501 | INFOPLIST_FILE = SampleApp/Info.plist;
502 | LD_RUNPATH_SEARCH_PATHS = (
503 | "$(inherited)",
504 | "@executable_path/Frameworks",
505 | );
506 | PRODUCT_BUNDLE_IDENTIFIER = de.casaseca.Wyler.SampleApp;
507 | PRODUCT_NAME = "$(TARGET_NAME)";
508 | SWIFT_VERSION = 5.0;
509 | TARGETED_DEVICE_FAMILY = "1,2";
510 | };
511 | name = Debug;
512 | };
513 | 370DA4E32440D8DE00BAB2B7 /* Release */ = {
514 | isa = XCBuildConfiguration;
515 | buildSettings = {
516 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
517 | CODE_SIGN_STYLE = Automatic;
518 | DEVELOPMENT_ASSET_PATHS = "\"SampleApp/Preview Content\"";
519 | DEVELOPMENT_TEAM = EKP9ZV782L;
520 | ENABLE_PREVIEWS = YES;
521 | INFOPLIST_FILE = SampleApp/Info.plist;
522 | LD_RUNPATH_SEARCH_PATHS = (
523 | "$(inherited)",
524 | "@executable_path/Frameworks",
525 | );
526 | PRODUCT_BUNDLE_IDENTIFIER = de.casaseca.Wyler.SampleApp;
527 | PRODUCT_NAME = "$(TARGET_NAME)";
528 | SWIFT_VERSION = 5.0;
529 | TARGETED_DEVICE_FAMILY = "1,2";
530 | };
531 | name = Release;
532 | };
533 | /* End XCBuildConfiguration section */
534 |
535 | /* Begin XCConfigurationList section */
536 | 370DA4BB2440D8C300BAB2B7 /* Build configuration list for PBXProject "Wyler" */ = {
537 | isa = XCConfigurationList;
538 | buildConfigurations = (
539 | 370DA4C72440D8C300BAB2B7 /* Debug */,
540 | 370DA4C82440D8C300BAB2B7 /* Release */,
541 | );
542 | defaultConfigurationIsVisible = 0;
543 | defaultConfigurationName = Release;
544 | };
545 | 370DA4C92440D8C300BAB2B7 /* Build configuration list for PBXNativeTarget "Wyler" */ = {
546 | isa = XCConfigurationList;
547 | buildConfigurations = (
548 | 370DA4CA2440D8C300BAB2B7 /* Debug */,
549 | 370DA4CB2440D8C300BAB2B7 /* Release */,
550 | );
551 | defaultConfigurationIsVisible = 0;
552 | defaultConfigurationName = Release;
553 | };
554 | 370DA4E12440D8DE00BAB2B7 /* Build configuration list for PBXNativeTarget "SampleApp" */ = {
555 | isa = XCConfigurationList;
556 | buildConfigurations = (
557 | 370DA4E22440D8DE00BAB2B7 /* Debug */,
558 | 370DA4E32440D8DE00BAB2B7 /* Release */,
559 | );
560 | defaultConfigurationIsVisible = 0;
561 | defaultConfigurationName = Release;
562 | };
563 | /* End XCConfigurationList section */
564 | };
565 | rootObject = 370DA4B82440D8C300BAB2B7 /* Project object */;
566 | }
567 |
--------------------------------------------------------------------------------