├── .gitignore
├── .swiftlint.yml
├── .travis.yml
├── Cartfile
├── Cartfile.resolved
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.md
├── Resources
├── logo.png
├── mock-mosaic-A.png
├── mock-mosaic-B.png
└── mock-normal.png
├── RxScreenProtectKit.podspec
├── RxScreenProtectKit.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── xcshareddata
│ └── xcschemes
│ ├── RxScreenProtectKit.xcscheme
│ └── RxScreenProtectKitExample.xcscheme
├── RxScreenProtectKitExample
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ └── UserMainPhoto.imageset
│ │ ├── Contents.json
│ │ └── UserMainPhoto.jpg
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
└── ViewController.swift
├── RxScreenProtectKitTests
├── Info.plist
├── RxBlocking+.swift
└── RxScreenProtectKitTests.swift
├── Source
├── Extensions
│ ├── CALayer+Mosaic.swift
│ ├── CALayer+Rx.swift
│ ├── NotificationCenter+Rx.swift
│ ├── UIView+Rx.swift
│ └── UIViewController+Rx.swift
├── SPImageView.swift
├── SPLabel.swift
└── ScreenProtectKit.swift
├── SupportingFiles
├── Info.plist
└── ScreenProtectKit.xcconfig
├── carthage.sh
├── codecov.yml
└── setup.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | .build/
41 |
42 | # CocoaPods
43 | #
44 | # We recommend against adding the Pods directory to your .gitignore. However
45 | # you should judge for yourself, the pros and cons are mentioned at:
46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
47 | #
48 | # Pods/
49 |
50 | # Carthage
51 | #
52 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
53 | # Carthage/Checkouts
54 |
55 | Carthage/Checkouts
56 | Carthage/Build
57 |
58 | # fastlane
59 | #
60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
61 | # screenshots whenever they are needed.
62 | # For more information about the recommended setup visit:
63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
64 |
65 | fastlane/report.xml
66 | fastlane/Preview.html
67 | fastlane/screenshots
68 | fastlane/test_output
69 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | excluded:
2 | - Carthage
3 | - Pods
4 |
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: swift
2 | matrix:
3 | include:
4 | - osx_image: xcode12.2
5 |
6 | before_install:
7 | # - echo "git \"file://$($(dirname $0); pwd)\"" >> Cartfile
8 | - cat Cartfile
9 | - brew update
10 | - brew outdated swiftlint || brew upgrade swiftlint
11 | # - carthage update --platform iOS
12 | - ./carthage.sh update --platform iOS
13 |
14 | script:
15 | - xcodebuild -scheme RxScreenProtectKit -sdk iphonesimulator -destination 'platform=iOS Simulator,OS=14.2,name=iPhone 12 Pro' -configuration Debug test | xcpretty -c
16 | - swiftlint
17 |
18 | after_success:
19 | - bash <(curl -s https://codecov.io/bash) -t 31ae53bc-db30-4cd8-a1cf-7bca2026a5ea
20 |
21 | # https://docs.travis-ci.com/user/reference/osx/#xcode-version
22 | # https://docs.travis-ci.com/user/languages/objective-c/
23 |
24 |
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "ReactiveX/RxSwift" ~> 6.0
2 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "ReactiveX/RxSwift" "6.0.0"
2 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | gem 'cocoapods'
3 | gem 'xcpretty'
4 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.0)
5 | activesupport (4.2.11.1)
6 | i18n (~> 0.7)
7 | minitest (~> 5.1)
8 | thread_safe (~> 0.3, >= 0.3.4)
9 | tzinfo (~> 1.1)
10 | atomos (0.1.3)
11 | claide (1.0.2)
12 | cocoapods (1.7.3)
13 | activesupport (>= 4.0.2, < 5)
14 | claide (>= 1.0.2, < 2.0)
15 | cocoapods-core (= 1.7.3)
16 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
17 | cocoapods-downloader (>= 1.2.2, < 2.0)
18 | cocoapods-plugins (>= 1.0.0, < 2.0)
19 | cocoapods-search (>= 1.0.0, < 2.0)
20 | cocoapods-stats (>= 1.0.0, < 2.0)
21 | cocoapods-trunk (>= 1.3.1, < 2.0)
22 | cocoapods-try (>= 1.1.0, < 2.0)
23 | colored2 (~> 3.1)
24 | escape (~> 0.0.4)
25 | fourflusher (>= 2.3.0, < 3.0)
26 | gh_inspector (~> 1.0)
27 | molinillo (~> 0.6.6)
28 | nap (~> 1.0)
29 | ruby-macho (~> 1.4)
30 | xcodeproj (>= 1.10.0, < 2.0)
31 | cocoapods-core (1.7.3)
32 | activesupport (>= 4.0.2, < 6)
33 | fuzzy_match (~> 2.0.4)
34 | nap (~> 1.0)
35 | cocoapods-deintegrate (1.0.4)
36 | cocoapods-downloader (1.2.2)
37 | cocoapods-plugins (1.0.0)
38 | nap
39 | cocoapods-search (1.0.0)
40 | cocoapods-stats (1.1.0)
41 | cocoapods-trunk (1.3.1)
42 | nap (>= 0.8, < 2.0)
43 | netrc (~> 0.11)
44 | cocoapods-try (1.1.0)
45 | colored2 (3.1.2)
46 | concurrent-ruby (1.1.5)
47 | escape (0.0.4)
48 | fourflusher (2.3.1)
49 | fuzzy_match (2.0.4)
50 | gh_inspector (1.1.3)
51 | i18n (0.9.5)
52 | concurrent-ruby (~> 1.0)
53 | minitest (5.11.3)
54 | molinillo (0.6.6)
55 | nanaimo (0.2.6)
56 | nap (1.1.0)
57 | netrc (0.11.0)
58 | rouge (2.0.7)
59 | ruby-macho (1.4.0)
60 | thread_safe (0.3.6)
61 | tzinfo (1.2.5)
62 | thread_safe (~> 0.1)
63 | xcodeproj (1.10.0)
64 | CFPropertyList (>= 2.3.3, < 4.0)
65 | atomos (~> 0.1.3)
66 | claide (>= 1.0.2, < 2.0)
67 | colored2 (~> 3.1)
68 | nanaimo (~> 0.2.6)
69 | xcpretty (0.3.0)
70 | rouge (~> 2.0.7)
71 |
72 | PLATFORMS
73 | ruby
74 |
75 | DEPENDENCIES
76 | cocoapods
77 | xcpretty
78 |
79 | BUNDLED WITH
80 | 2.1.4
81 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 AkkeyLab
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
RxScreenProtectKit
2 | ======================================
3 | [](https://cocoapods.org/pods/RxScreenProtectKit)
4 | 
5 | 
6 | [](https://github.com/Carthage/Carthage)
7 | [](https://travis-ci.com/AkkeyLab/RxScreenProtectKit)
8 | [](https://codecov.io/gh/AkkeyLab/RxScreenProtectKit)
9 |
10 | Protect private content from screen recordings and screen output.
11 | Screen Recording / QuickTime Recording / External display output / AirPlay
12 |
13 |
16 |
17 | # Installation
18 | #### CocoaPods
19 | ```ruby
20 | # Podfile
21 | use_frameworks!
22 |
23 | target 'YOUR_TARGET_NAME' do
24 | pod 'RxScreenProtectKit'
25 | end
26 | ```
27 | Replace YOUR_TARGET_NAME and then, in the Podfile directory, type:
28 | ```sh
29 | $ pod install
30 | ```
31 |
32 | #### Carthage
33 | Add this to `Cartfile`.
34 | ```ruby
35 | # Cartfile
36 | github "AkkeyLab/RxScreenProtectKit"
37 | ```
38 | Run this script to install it.
39 | ```sh
40 | $ carthage update --platform iOS
41 | ```
42 |
43 | # Usage
44 | If you use the example, please do the setup process with the shell script.
45 | ```sh
46 | ./setup.sh
47 | ```
48 | Please import RxScreenProtectKit and RxSwift.
49 | ```swift
50 | import RxScreenProtectKit
51 | import RxSwift
52 | import UIKit
53 | ```
54 | By binding the target layer to isScreenRecord, mosaic processing is applied during screen recording or screen output. However, the layer must be compliant with CALayer.
55 | ```swift
56 | final class ViewController: UIViewController {
57 | @IBOutlet private weak var mainImageView: UIImageView!
58 | private let bag = DisposeBag()
59 |
60 | override func viewDidLoad() {
61 | super.viewDidLoad()
62 | self.rx.isScreenRecord
63 | .bind(to: mainImageView.layer.rx.isMosaic)
64 | .disposed(by: bag)
65 | }
66 | }
67 | ```
68 | Parameter settings related to mosaic processing can be done from `ScreenProtectKit.shared.config()`.
69 | ```swift
70 | ScreenProtectKit
71 | .shared
72 | .config(rasterizationScale: 0.1,
73 | minificationFilter: .trilinear,
74 | magnificationFilter: .nearest)
75 | ```
76 | Moreover, it can be easily implemented by using SPImageView. This is particularly useful when images are set asynchronously.
77 | ```swift
78 | final class ViewController: UIViewController {
79 | @IBOutlet private weak var mainImageView: SPImageView!
80 |
81 | override func viewDidLoad() {
82 | super.viewDidLoad()
83 | DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in
84 | self?.mainImageView.image = UIImage(named: "sample")
85 | }
86 | }
87 | }
88 | ```
89 | Also, by using SPLabel, you can easily implement character substitution during recording. All you need to do is set the text to be displayed during recording in `protectText`.
90 | ```swift
91 | final class ViewController: UIViewController {
92 | @IBOutlet private weak var label: SPLabel!
93 |
94 | override func viewDidLoad() {
95 | super.viewDidLoad()
96 | label.text = "This is the original text."
97 | label.protectText = "Recording is prohibited!!"
98 | }
99 | }
100 | ```
101 |
102 | # Settings
103 | You can temporarily disable this feature. In addition, it returns to the valid state by restarting the app.
104 | `isScreenRecord` will not flow while this setting is disabled.
105 | When the value of this setting is changed, `isScreenRecord` will flow only once. When changed to invalid, false will flow, and when changed to valid, the current recording status will flow.
106 | ```swift
107 | ScreenProtectKit
108 | .shared
109 | .isValid = isValid
110 | ```
111 |
112 | # Requirements
113 | |env |version |
114 | |--- |--- |
115 | |Swift |5.x |
116 | |Xcode |11.x |
117 | |iOS |11.0 |
118 |
119 | # License
120 | RxScreenProtectKit is available under the MIT license. See the LICENSE file for more info.
121 |
--------------------------------------------------------------------------------
/Resources/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AkkeyLab/RxScreenProtectKit/c482566809f739989941ae6d5315ee39fca2c9c9/Resources/logo.png
--------------------------------------------------------------------------------
/Resources/mock-mosaic-A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AkkeyLab/RxScreenProtectKit/c482566809f739989941ae6d5315ee39fca2c9c9/Resources/mock-mosaic-A.png
--------------------------------------------------------------------------------
/Resources/mock-mosaic-B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AkkeyLab/RxScreenProtectKit/c482566809f739989941ae6d5315ee39fca2c9c9/Resources/mock-mosaic-B.png
--------------------------------------------------------------------------------
/Resources/mock-normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AkkeyLab/RxScreenProtectKit/c482566809f739989941ae6d5315ee39fca2c9c9/Resources/mock-normal.png
--------------------------------------------------------------------------------
/RxScreenProtectKit.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'RxScreenProtectKit'
3 | s.version = '0.3.0'
4 | s.summary = 'Protect the screen from recording'
5 |
6 | s.homepage = 'https://github.com/AkkeyLab/RxScreenProtectKit'
7 | s.license = 'MIT'
8 | s.author = 'AkkeyLab'
9 | s.source = { :git => 'https://github.com/AkkeyLab/RxScreenProtectKit.git', :tag => "#{s.version}" }
10 | s.social_media_url = 'https://twitter.com/AkkeyLab'
11 |
12 | s.platform = :ios, "12.0"
13 | s.swift_version = "5.0"
14 |
15 | s.module_name = "RxScreenProtectKit"
16 |
17 | s.dependency 'RxSwift', '~> 6.0'
18 | s.dependency 'RxCocoa', '~> 6.0'
19 |
20 | s.source_files = 'Source/**/*'
21 | end
22 |
--------------------------------------------------------------------------------
/RxScreenProtectKit.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | E507024722DF11BF00523EA9 /* RxRelay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E507024622DF11BF00523EA9 /* RxRelay.framework */; };
11 | E507024822DF12AC00523EA9 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5583BEA2266071D0081702C /* RxSwift.framework */; };
12 | E507024C22E0155E00523EA9 /* UIView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = E507024B22E0155E00523EA9 /* UIView+Rx.swift */; };
13 | E5111EE222E57A3E00C53F1F /* RxBlocking+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5111EE122E57A3E00C53F1F /* RxBlocking+.swift */; };
14 | E5111EE522E57CC700C53F1F /* RxBlocking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5111EE322E57C6700C53F1F /* RxBlocking.framework */; };
15 | E538577522CCC4EA0052A792 /* ScreenProtectKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E538577422CCC4EA0052A792 /* ScreenProtectKit.swift */; };
16 | E5583BBA2265DC1F0081702C /* RxScreenProtectKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5583BB02265DC1F0081702C /* RxScreenProtectKit.framework */; };
17 | E5583BBF2265DC1F0081702C /* RxScreenProtectKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5583BBE2265DC1F0081702C /* RxScreenProtectKitTests.swift */; };
18 | E5583BCF2265EF960081702C /* CALayer+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5583BCE2265EF960081702C /* CALayer+Rx.swift */; };
19 | E5583BD92265FBB20081702C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5583BD82265FBB20081702C /* AppDelegate.swift */; };
20 | E5583BDB2265FBB20081702C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5583BDA2265FBB20081702C /* ViewController.swift */; };
21 | E5583BDE2265FBB20081702C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5583BDC2265FBB20081702C /* Main.storyboard */; };
22 | E5583BE02265FBB60081702C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E5583BDF2265FBB60081702C /* Assets.xcassets */; };
23 | E5583BE32265FBB60081702C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E5583BE12265FBB60081702C /* LaunchScreen.storyboard */; };
24 | E5583BEC2266071D0081702C /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5583BEA2266071D0081702C /* RxSwift.framework */; };
25 | E5583BED2266071D0081702C /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5583BEB2266071D0081702C /* RxCocoa.framework */; };
26 | E5589C1222D86DDC001BD58D /* RxScreenProtectKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5583BB02265DC1F0081702C /* RxScreenProtectKit.framework */; };
27 | E5589C1322D86DDC001BD58D /* RxScreenProtectKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E5583BB02265DC1F0081702C /* RxScreenProtectKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
28 | E5589C1722D8716B001BD58D /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5583BEB2266071D0081702C /* RxCocoa.framework */; };
29 | E5AA57A922D32B96005E38BE /* UIViewController+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AA57A822D32B96005E38BE /* UIViewController+Rx.swift */; };
30 | E5C080C52327EE43009B2DF1 /* SPImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5C080C42327EE43009B2DF1 /* SPImageView.swift */; };
31 | E5C220FA234F44520061235B /* SPLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5C220F9234F44520061235B /* SPLabel.swift */; };
32 | EA57C4D62316E5FA002AF7C6 /* NotificationCenter+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA57C4D52316E5FA002AF7C6 /* NotificationCenter+Rx.swift */; };
33 | EACE9D432316EE8D0003B077 /* CALayer+Mosaic.swift in Sources */ = {isa = PBXBuildFile; fileRef = EACE9D422316EE8D0003B077 /* CALayer+Mosaic.swift */; };
34 | /* End PBXBuildFile section */
35 |
36 | /* Begin PBXContainerItemProxy section */
37 | E5583BBB2265DC1F0081702C /* PBXContainerItemProxy */ = {
38 | isa = PBXContainerItemProxy;
39 | containerPortal = E5583BA72265DC1F0081702C /* Project object */;
40 | proxyType = 1;
41 | remoteGlobalIDString = E5583BAF2265DC1F0081702C;
42 | remoteInfo = RxScreenProtectKit;
43 | };
44 | E5589C1422D86DDC001BD58D /* PBXContainerItemProxy */ = {
45 | isa = PBXContainerItemProxy;
46 | containerPortal = E5583BA72265DC1F0081702C /* Project object */;
47 | proxyType = 1;
48 | remoteGlobalIDString = E5583BAF2265DC1F0081702C;
49 | remoteInfo = RxScreenProtectKit;
50 | };
51 | E5AA576C22CDBB57005E38BE /* PBXContainerItemProxy */ = {
52 | isa = PBXContainerItemProxy;
53 | containerPortal = E5583BA72265DC1F0081702C /* Project object */;
54 | proxyType = 1;
55 | remoteGlobalIDString = E5583BD52265FBB20081702C;
56 | remoteInfo = RxScreenProtectKitExample;
57 | };
58 | /* End PBXContainerItemProxy section */
59 |
60 | /* Begin PBXCopyFilesBuildPhase section */
61 | E5589C1622D86DDC001BD58D /* Embed Frameworks */ = {
62 | isa = PBXCopyFilesBuildPhase;
63 | buildActionMask = 2147483647;
64 | dstPath = "";
65 | dstSubfolderSpec = 10;
66 | files = (
67 | E5589C1322D86DDC001BD58D /* RxScreenProtectKit.framework in Embed Frameworks */,
68 | );
69 | name = "Embed Frameworks";
70 | runOnlyForDeploymentPostprocessing = 0;
71 | };
72 | /* End PBXCopyFilesBuildPhase section */
73 |
74 | /* Begin PBXFileReference section */
75 | E507024622DF11BF00523EA9 /* RxRelay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxRelay.framework; path = Carthage/Build/iOS/RxRelay.framework; sourceTree = ""; };
76 | E507024B22E0155E00523EA9 /* UIView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Rx.swift"; sourceTree = ""; };
77 | E5111EE122E57A3E00C53F1F /* RxBlocking+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RxBlocking+.swift"; sourceTree = ""; };
78 | E5111EE322E57C6700C53F1F /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxBlocking.framework; path = Carthage/Build/iOS/RxBlocking.framework; sourceTree = ""; };
79 | E538577422CCC4EA0052A792 /* ScreenProtectKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenProtectKit.swift; sourceTree = ""; };
80 | E5583BB02265DC1F0081702C /* RxScreenProtectKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxScreenProtectKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
81 | E5583BB42265DC1F0081702C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
82 | E5583BB92265DC1F0081702C /* RxScreenProtectKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RxScreenProtectKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
83 | E5583BBE2265DC1F0081702C /* RxScreenProtectKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxScreenProtectKitTests.swift; sourceTree = ""; };
84 | E5583BC02265DC1F0081702C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
85 | E5583BCC2265EBA90081702C /* ScreenProtectKit.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ScreenProtectKit.xcconfig; sourceTree = ""; };
86 | E5583BCE2265EF960081702C /* CALayer+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+Rx.swift"; sourceTree = ""; };
87 | E5583BD62265FBB20081702C /* RxScreenProtectKitExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxScreenProtectKitExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
88 | E5583BD82265FBB20081702C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
89 | E5583BDA2265FBB20081702C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
90 | E5583BDD2265FBB20081702C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
91 | E5583BDF2265FBB60081702C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
92 | E5583BE22265FBB60081702C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
93 | E5583BE42265FBB60081702C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
94 | E5583BEA2266071D0081702C /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; };
95 | E5583BEB2266071D0081702C /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; };
96 | E5AA57A822D32B96005E38BE /* UIViewController+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Rx.swift"; sourceTree = ""; };
97 | E5C080C42327EE43009B2DF1 /* SPImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPImageView.swift; sourceTree = ""; };
98 | E5C220F9234F44520061235B /* SPLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SPLabel.swift; sourceTree = ""; };
99 | EA57C4D52316E5FA002AF7C6 /* NotificationCenter+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationCenter+Rx.swift"; sourceTree = ""; };
100 | EACE9D422316EE8D0003B077 /* CALayer+Mosaic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+Mosaic.swift"; sourceTree = ""; };
101 | /* End PBXFileReference section */
102 |
103 | /* Begin PBXFrameworksBuildPhase section */
104 | E5583BAD2265DC1F0081702C /* Frameworks */ = {
105 | isa = PBXFrameworksBuildPhase;
106 | buildActionMask = 2147483647;
107 | files = (
108 | E507024822DF12AC00523EA9 /* RxSwift.framework in Frameworks */,
109 | E507024722DF11BF00523EA9 /* RxRelay.framework in Frameworks */,
110 | E5589C1722D8716B001BD58D /* RxCocoa.framework in Frameworks */,
111 | );
112 | runOnlyForDeploymentPostprocessing = 0;
113 | };
114 | E5583BB62265DC1F0081702C /* Frameworks */ = {
115 | isa = PBXFrameworksBuildPhase;
116 | buildActionMask = 2147483647;
117 | files = (
118 | E5583BBA2265DC1F0081702C /* RxScreenProtectKit.framework in Frameworks */,
119 | );
120 | runOnlyForDeploymentPostprocessing = 0;
121 | };
122 | E5583BD32265FBB20081702C /* Frameworks */ = {
123 | isa = PBXFrameworksBuildPhase;
124 | buildActionMask = 2147483647;
125 | files = (
126 | E5111EE522E57CC700C53F1F /* RxBlocking.framework in Frameworks */,
127 | E5583BEC2266071D0081702C /* RxSwift.framework in Frameworks */,
128 | E5589C1222D86DDC001BD58D /* RxScreenProtectKit.framework in Frameworks */,
129 | E5583BED2266071D0081702C /* RxCocoa.framework in Frameworks */,
130 | );
131 | runOnlyForDeploymentPostprocessing = 0;
132 | };
133 | /* End PBXFrameworksBuildPhase section */
134 |
135 | /* Begin PBXGroup section */
136 | E538577622CCCE5E0052A792 /* Extensions */ = {
137 | isa = PBXGroup;
138 | children = (
139 | E5583BCE2265EF960081702C /* CALayer+Rx.swift */,
140 | E5AA57A822D32B96005E38BE /* UIViewController+Rx.swift */,
141 | E507024B22E0155E00523EA9 /* UIView+Rx.swift */,
142 | EA57C4D52316E5FA002AF7C6 /* NotificationCenter+Rx.swift */,
143 | EACE9D422316EE8D0003B077 /* CALayer+Mosaic.swift */,
144 | );
145 | path = Extensions;
146 | sourceTree = "";
147 | };
148 | E5583BA62265DC1F0081702C = {
149 | isa = PBXGroup;
150 | children = (
151 | E5583BD12265F5860081702C /* SupportingFiles */,
152 | E5583BD02265F55A0081702C /* Source */,
153 | E5583BBD2265DC1F0081702C /* RxScreenProtectKitTests */,
154 | E5583BD72265FBB20081702C /* RxScreenProtectKitExample */,
155 | E5583BB12265DC1F0081702C /* Products */,
156 | E5583BE82265FBD40081702C /* Frameworks */,
157 | );
158 | sourceTree = "";
159 | };
160 | E5583BB12265DC1F0081702C /* Products */ = {
161 | isa = PBXGroup;
162 | children = (
163 | E5583BB02265DC1F0081702C /* RxScreenProtectKit.framework */,
164 | E5583BB92265DC1F0081702C /* RxScreenProtectKitTests.xctest */,
165 | E5583BD62265FBB20081702C /* RxScreenProtectKitExample.app */,
166 | );
167 | name = Products;
168 | sourceTree = "";
169 | };
170 | E5583BBD2265DC1F0081702C /* RxScreenProtectKitTests */ = {
171 | isa = PBXGroup;
172 | children = (
173 | E5583BBE2265DC1F0081702C /* RxScreenProtectKitTests.swift */,
174 | E5111EE122E57A3E00C53F1F /* RxBlocking+.swift */,
175 | E5583BC02265DC1F0081702C /* Info.plist */,
176 | );
177 | path = RxScreenProtectKitTests;
178 | sourceTree = "";
179 | };
180 | E5583BD02265F55A0081702C /* Source */ = {
181 | isa = PBXGroup;
182 | children = (
183 | E538577622CCCE5E0052A792 /* Extensions */,
184 | E538577422CCC4EA0052A792 /* ScreenProtectKit.swift */,
185 | E5C080C42327EE43009B2DF1 /* SPImageView.swift */,
186 | E5C220F9234F44520061235B /* SPLabel.swift */,
187 | );
188 | path = Source;
189 | sourceTree = "";
190 | };
191 | E5583BD12265F5860081702C /* SupportingFiles */ = {
192 | isa = PBXGroup;
193 | children = (
194 | E5583BB42265DC1F0081702C /* Info.plist */,
195 | E5583BCC2265EBA90081702C /* ScreenProtectKit.xcconfig */,
196 | );
197 | path = SupportingFiles;
198 | sourceTree = "";
199 | };
200 | E5583BD72265FBB20081702C /* RxScreenProtectKitExample */ = {
201 | isa = PBXGroup;
202 | children = (
203 | E5583BD82265FBB20081702C /* AppDelegate.swift */,
204 | E5583BDA2265FBB20081702C /* ViewController.swift */,
205 | E5583BDC2265FBB20081702C /* Main.storyboard */,
206 | E5583BDF2265FBB60081702C /* Assets.xcassets */,
207 | E5583BE12265FBB60081702C /* LaunchScreen.storyboard */,
208 | E5583BE42265FBB60081702C /* Info.plist */,
209 | );
210 | path = RxScreenProtectKitExample;
211 | sourceTree = "";
212 | };
213 | E5583BE82265FBD40081702C /* Frameworks */ = {
214 | isa = PBXGroup;
215 | children = (
216 | E5111EE322E57C6700C53F1F /* RxBlocking.framework */,
217 | E507024622DF11BF00523EA9 /* RxRelay.framework */,
218 | E5583BEB2266071D0081702C /* RxCocoa.framework */,
219 | E5583BEA2266071D0081702C /* RxSwift.framework */,
220 | );
221 | name = Frameworks;
222 | sourceTree = "";
223 | };
224 | /* End PBXGroup section */
225 |
226 | /* Begin PBXHeadersBuildPhase section */
227 | E5583BAB2265DC1F0081702C /* Headers */ = {
228 | isa = PBXHeadersBuildPhase;
229 | buildActionMask = 2147483647;
230 | files = (
231 | );
232 | runOnlyForDeploymentPostprocessing = 0;
233 | };
234 | /* End PBXHeadersBuildPhase section */
235 |
236 | /* Begin PBXNativeTarget section */
237 | E5583BAF2265DC1F0081702C /* RxScreenProtectKit */ = {
238 | isa = PBXNativeTarget;
239 | buildConfigurationList = E5583BC42265DC1F0081702C /* Build configuration list for PBXNativeTarget "RxScreenProtectKit" */;
240 | buildPhases = (
241 | E5583BAB2265DC1F0081702C /* Headers */,
242 | E5583BAC2265DC1F0081702C /* Sources */,
243 | E5583BAD2265DC1F0081702C /* Frameworks */,
244 | E5583BAE2265DC1F0081702C /* Resources */,
245 | E5AA57AA22D33556005E38BE /* SwiftLint */,
246 | );
247 | buildRules = (
248 | );
249 | dependencies = (
250 | );
251 | name = RxScreenProtectKit;
252 | productName = RxScreenProtectKit;
253 | productReference = E5583BB02265DC1F0081702C /* RxScreenProtectKit.framework */;
254 | productType = "com.apple.product-type.framework";
255 | };
256 | E5583BB82265DC1F0081702C /* RxScreenProtectKitTests */ = {
257 | isa = PBXNativeTarget;
258 | buildConfigurationList = E5583BC72265DC1F0081702C /* Build configuration list for PBXNativeTarget "RxScreenProtectKitTests" */;
259 | buildPhases = (
260 | E5583BB52265DC1F0081702C /* Sources */,
261 | E5583BB62265DC1F0081702C /* Frameworks */,
262 | E5583BB72265DC1F0081702C /* Resources */,
263 | );
264 | buildRules = (
265 | );
266 | dependencies = (
267 | E5583BBC2265DC1F0081702C /* PBXTargetDependency */,
268 | E5AA576D22CDBB57005E38BE /* PBXTargetDependency */,
269 | );
270 | name = RxScreenProtectKitTests;
271 | productName = RxScreenProtectKitTests;
272 | productReference = E5583BB92265DC1F0081702C /* RxScreenProtectKitTests.xctest */;
273 | productType = "com.apple.product-type.bundle.unit-test";
274 | };
275 | E5583BD52265FBB20081702C /* RxScreenProtectKitExample */ = {
276 | isa = PBXNativeTarget;
277 | buildConfigurationList = E5583BE52265FBB60081702C /* Build configuration list for PBXNativeTarget "RxScreenProtectKitExample" */;
278 | buildPhases = (
279 | E5583BD22265FBB20081702C /* Sources */,
280 | E5583BD32265FBB20081702C /* Frameworks */,
281 | E5583BD42265FBB20081702C /* Resources */,
282 | E5583BEE226607940081702C /* Carthage */,
283 | EAC85EF422CFC1DE0048ED3B /* SwiftLint */,
284 | E5589C1622D86DDC001BD58D /* Embed Frameworks */,
285 | );
286 | buildRules = (
287 | );
288 | dependencies = (
289 | E5589C1522D86DDC001BD58D /* PBXTargetDependency */,
290 | );
291 | name = RxScreenProtectKitExample;
292 | productName = RxScreenProtectKitExample;
293 | productReference = E5583BD62265FBB20081702C /* RxScreenProtectKitExample.app */;
294 | productType = "com.apple.product-type.application";
295 | };
296 | /* End PBXNativeTarget section */
297 |
298 | /* Begin PBXProject section */
299 | E5583BA72265DC1F0081702C /* Project object */ = {
300 | isa = PBXProject;
301 | attributes = {
302 | LastSwiftUpdateCheck = 1020;
303 | LastUpgradeCheck = 1230;
304 | ORGANIZATIONNAME = AKIO;
305 | TargetAttributes = {
306 | E5583BAF2265DC1F0081702C = {
307 | CreatedOnToolsVersion = 10.2;
308 | LastSwiftMigration = 1020;
309 | };
310 | E5583BB82265DC1F0081702C = {
311 | CreatedOnToolsVersion = 10.2;
312 | TestTargetID = E5583BD52265FBB20081702C;
313 | };
314 | E5583BD52265FBB20081702C = {
315 | CreatedOnToolsVersion = 10.2;
316 | };
317 | };
318 | };
319 | buildConfigurationList = E5583BAA2265DC1F0081702C /* Build configuration list for PBXProject "RxScreenProtectKit" */;
320 | compatibilityVersion = "Xcode 9.3";
321 | developmentRegion = en;
322 | hasScannedForEncodings = 0;
323 | knownRegions = (
324 | en,
325 | Base,
326 | );
327 | mainGroup = E5583BA62265DC1F0081702C;
328 | productRefGroup = E5583BB12265DC1F0081702C /* Products */;
329 | projectDirPath = "";
330 | projectRoot = "";
331 | targets = (
332 | E5583BAF2265DC1F0081702C /* RxScreenProtectKit */,
333 | E5583BB82265DC1F0081702C /* RxScreenProtectKitTests */,
334 | E5583BD52265FBB20081702C /* RxScreenProtectKitExample */,
335 | );
336 | };
337 | /* End PBXProject section */
338 |
339 | /* Begin PBXResourcesBuildPhase section */
340 | E5583BAE2265DC1F0081702C /* Resources */ = {
341 | isa = PBXResourcesBuildPhase;
342 | buildActionMask = 2147483647;
343 | files = (
344 | );
345 | runOnlyForDeploymentPostprocessing = 0;
346 | };
347 | E5583BB72265DC1F0081702C /* Resources */ = {
348 | isa = PBXResourcesBuildPhase;
349 | buildActionMask = 2147483647;
350 | files = (
351 | );
352 | runOnlyForDeploymentPostprocessing = 0;
353 | };
354 | E5583BD42265FBB20081702C /* Resources */ = {
355 | isa = PBXResourcesBuildPhase;
356 | buildActionMask = 2147483647;
357 | files = (
358 | E5583BE32265FBB60081702C /* LaunchScreen.storyboard in Resources */,
359 | E5583BE02265FBB60081702C /* Assets.xcassets in Resources */,
360 | E5583BDE2265FBB20081702C /* Main.storyboard in Resources */,
361 | );
362 | runOnlyForDeploymentPostprocessing = 0;
363 | };
364 | /* End PBXResourcesBuildPhase section */
365 |
366 | /* Begin PBXShellScriptBuildPhase section */
367 | E5583BEE226607940081702C /* Carthage */ = {
368 | isa = PBXShellScriptBuildPhase;
369 | buildActionMask = 2147483647;
370 | files = (
371 | );
372 | inputFileListPaths = (
373 | );
374 | inputPaths = (
375 | "$(SRCROOT)/Carthage/Build/iOS/RxCocoa.framework",
376 | "$(SRCROOT)/Carthage/Build/iOS/RxSwift.framework",
377 | "$(SRCROOT)/Carthage/Build/iOS/RxRelay.framework",
378 | "$(SRCROOT)/Carthage/Build/iOS/RxBlocking.framework",
379 | );
380 | name = Carthage;
381 | outputFileListPaths = (
382 | );
383 | outputPaths = (
384 | );
385 | runOnlyForDeploymentPostprocessing = 0;
386 | shellPath = /bin/sh;
387 | shellScript = "/usr/local/bin/carthage copy-frameworks\n";
388 | };
389 | E5AA57AA22D33556005E38BE /* SwiftLint */ = {
390 | isa = PBXShellScriptBuildPhase;
391 | buildActionMask = 2147483647;
392 | files = (
393 | );
394 | inputFileListPaths = (
395 | );
396 | inputPaths = (
397 | );
398 | name = SwiftLint;
399 | outputFileListPaths = (
400 | );
401 | outputPaths = (
402 | );
403 | runOnlyForDeploymentPostprocessing = 0;
404 | shellPath = /bin/sh;
405 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
406 | };
407 | EAC85EF422CFC1DE0048ED3B /* SwiftLint */ = {
408 | isa = PBXShellScriptBuildPhase;
409 | buildActionMask = 2147483647;
410 | files = (
411 | );
412 | inputFileListPaths = (
413 | );
414 | inputPaths = (
415 | );
416 | name = SwiftLint;
417 | outputFileListPaths = (
418 | );
419 | outputPaths = (
420 | );
421 | runOnlyForDeploymentPostprocessing = 0;
422 | shellPath = /bin/sh;
423 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
424 | };
425 | /* End PBXShellScriptBuildPhase section */
426 |
427 | /* Begin PBXSourcesBuildPhase section */
428 | E5583BAC2265DC1F0081702C /* Sources */ = {
429 | isa = PBXSourcesBuildPhase;
430 | buildActionMask = 2147483647;
431 | files = (
432 | E5AA57A922D32B96005E38BE /* UIViewController+Rx.swift in Sources */,
433 | EACE9D432316EE8D0003B077 /* CALayer+Mosaic.swift in Sources */,
434 | EA57C4D62316E5FA002AF7C6 /* NotificationCenter+Rx.swift in Sources */,
435 | E5C220FA234F44520061235B /* SPLabel.swift in Sources */,
436 | E5C080C52327EE43009B2DF1 /* SPImageView.swift in Sources */,
437 | E507024C22E0155E00523EA9 /* UIView+Rx.swift in Sources */,
438 | E5583BCF2265EF960081702C /* CALayer+Rx.swift in Sources */,
439 | E538577522CCC4EA0052A792 /* ScreenProtectKit.swift in Sources */,
440 | );
441 | runOnlyForDeploymentPostprocessing = 0;
442 | };
443 | E5583BB52265DC1F0081702C /* Sources */ = {
444 | isa = PBXSourcesBuildPhase;
445 | buildActionMask = 2147483647;
446 | files = (
447 | E5111EE222E57A3E00C53F1F /* RxBlocking+.swift in Sources */,
448 | E5583BBF2265DC1F0081702C /* RxScreenProtectKitTests.swift in Sources */,
449 | );
450 | runOnlyForDeploymentPostprocessing = 0;
451 | };
452 | E5583BD22265FBB20081702C /* Sources */ = {
453 | isa = PBXSourcesBuildPhase;
454 | buildActionMask = 2147483647;
455 | files = (
456 | E5583BDB2265FBB20081702C /* ViewController.swift in Sources */,
457 | E5583BD92265FBB20081702C /* AppDelegate.swift in Sources */,
458 | );
459 | runOnlyForDeploymentPostprocessing = 0;
460 | };
461 | /* End PBXSourcesBuildPhase section */
462 |
463 | /* Begin PBXTargetDependency section */
464 | E5583BBC2265DC1F0081702C /* PBXTargetDependency */ = {
465 | isa = PBXTargetDependency;
466 | target = E5583BAF2265DC1F0081702C /* RxScreenProtectKit */;
467 | targetProxy = E5583BBB2265DC1F0081702C /* PBXContainerItemProxy */;
468 | };
469 | E5589C1522D86DDC001BD58D /* PBXTargetDependency */ = {
470 | isa = PBXTargetDependency;
471 | target = E5583BAF2265DC1F0081702C /* RxScreenProtectKit */;
472 | targetProxy = E5589C1422D86DDC001BD58D /* PBXContainerItemProxy */;
473 | };
474 | E5AA576D22CDBB57005E38BE /* PBXTargetDependency */ = {
475 | isa = PBXTargetDependency;
476 | target = E5583BD52265FBB20081702C /* RxScreenProtectKitExample */;
477 | targetProxy = E5AA576C22CDBB57005E38BE /* PBXContainerItemProxy */;
478 | };
479 | /* End PBXTargetDependency section */
480 |
481 | /* Begin PBXVariantGroup section */
482 | E5583BDC2265FBB20081702C /* Main.storyboard */ = {
483 | isa = PBXVariantGroup;
484 | children = (
485 | E5583BDD2265FBB20081702C /* Base */,
486 | );
487 | name = Main.storyboard;
488 | sourceTree = "";
489 | };
490 | E5583BE12265FBB60081702C /* LaunchScreen.storyboard */ = {
491 | isa = PBXVariantGroup;
492 | children = (
493 | E5583BE22265FBB60081702C /* Base */,
494 | );
495 | name = LaunchScreen.storyboard;
496 | sourceTree = "";
497 | };
498 | /* End PBXVariantGroup section */
499 |
500 | /* Begin XCBuildConfiguration section */
501 | E5583BC22265DC1F0081702C /* Debug */ = {
502 | isa = XCBuildConfiguration;
503 | baseConfigurationReference = E5583BCC2265EBA90081702C /* ScreenProtectKit.xcconfig */;
504 | buildSettings = {
505 | ALWAYS_SEARCH_USER_PATHS = NO;
506 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
507 | CLANG_ANALYZER_NONNULL = YES;
508 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
509 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
510 | CLANG_CXX_LIBRARY = "libc++";
511 | CLANG_ENABLE_MODULES = YES;
512 | CLANG_ENABLE_OBJC_ARC = YES;
513 | CLANG_ENABLE_OBJC_WEAK = YES;
514 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
515 | CLANG_WARN_BOOL_CONVERSION = YES;
516 | CLANG_WARN_COMMA = YES;
517 | CLANG_WARN_CONSTANT_CONVERSION = YES;
518 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
519 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
520 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
521 | CLANG_WARN_EMPTY_BODY = YES;
522 | CLANG_WARN_ENUM_CONVERSION = YES;
523 | CLANG_WARN_INFINITE_RECURSION = YES;
524 | CLANG_WARN_INT_CONVERSION = YES;
525 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
526 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
527 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
528 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
529 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
530 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
531 | CLANG_WARN_STRICT_PROTOTYPES = YES;
532 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
533 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
534 | CLANG_WARN_UNREACHABLE_CODE = YES;
535 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
536 | CODE_SIGN_IDENTITY = "iPhone Developer";
537 | COPY_PHASE_STRIP = NO;
538 | CURRENT_PROJECT_VERSION = 1;
539 | DEBUG_INFORMATION_FORMAT = dwarf;
540 | ENABLE_STRICT_OBJC_MSGSEND = YES;
541 | ENABLE_TESTABILITY = YES;
542 | GCC_C_LANGUAGE_STANDARD = gnu11;
543 | GCC_DYNAMIC_NO_PIC = NO;
544 | GCC_NO_COMMON_BLOCKS = YES;
545 | GCC_OPTIMIZATION_LEVEL = 0;
546 | GCC_PREPROCESSOR_DEFINITIONS = (
547 | "DEBUG=1",
548 | "$(inherited)",
549 | );
550 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
551 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
552 | GCC_WARN_UNDECLARED_SELECTOR = YES;
553 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
554 | GCC_WARN_UNUSED_FUNCTION = YES;
555 | GCC_WARN_UNUSED_VARIABLE = YES;
556 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
557 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
558 | MTL_FAST_MATH = YES;
559 | ONLY_ACTIVE_ARCH = YES;
560 | SDKROOT = iphoneos;
561 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
562 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
563 | VERSIONING_SYSTEM = "apple-generic";
564 | VERSION_INFO_PREFIX = "";
565 | };
566 | name = Debug;
567 | };
568 | E5583BC32265DC1F0081702C /* Release */ = {
569 | isa = XCBuildConfiguration;
570 | baseConfigurationReference = E5583BCC2265EBA90081702C /* ScreenProtectKit.xcconfig */;
571 | buildSettings = {
572 | ALWAYS_SEARCH_USER_PATHS = NO;
573 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
574 | CLANG_ANALYZER_NONNULL = YES;
575 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
576 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
577 | CLANG_CXX_LIBRARY = "libc++";
578 | CLANG_ENABLE_MODULES = YES;
579 | CLANG_ENABLE_OBJC_ARC = YES;
580 | CLANG_ENABLE_OBJC_WEAK = YES;
581 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
582 | CLANG_WARN_BOOL_CONVERSION = YES;
583 | CLANG_WARN_COMMA = YES;
584 | CLANG_WARN_CONSTANT_CONVERSION = YES;
585 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
586 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
587 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
588 | CLANG_WARN_EMPTY_BODY = YES;
589 | CLANG_WARN_ENUM_CONVERSION = YES;
590 | CLANG_WARN_INFINITE_RECURSION = YES;
591 | CLANG_WARN_INT_CONVERSION = YES;
592 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
593 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
594 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
595 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
596 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
597 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
598 | CLANG_WARN_STRICT_PROTOTYPES = YES;
599 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
600 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
601 | CLANG_WARN_UNREACHABLE_CODE = YES;
602 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
603 | CODE_SIGN_IDENTITY = "iPhone Developer";
604 | COPY_PHASE_STRIP = NO;
605 | CURRENT_PROJECT_VERSION = 1;
606 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
607 | ENABLE_NS_ASSERTIONS = NO;
608 | ENABLE_STRICT_OBJC_MSGSEND = YES;
609 | GCC_C_LANGUAGE_STANDARD = gnu11;
610 | GCC_NO_COMMON_BLOCKS = YES;
611 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
612 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
613 | GCC_WARN_UNDECLARED_SELECTOR = YES;
614 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
615 | GCC_WARN_UNUSED_FUNCTION = YES;
616 | GCC_WARN_UNUSED_VARIABLE = YES;
617 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
618 | MTL_ENABLE_DEBUG_INFO = NO;
619 | MTL_FAST_MATH = YES;
620 | SDKROOT = iphoneos;
621 | SWIFT_COMPILATION_MODE = wholemodule;
622 | SWIFT_OPTIMIZATION_LEVEL = "-O";
623 | VALIDATE_PRODUCT = YES;
624 | VERSIONING_SYSTEM = "apple-generic";
625 | VERSION_INFO_PREFIX = "";
626 | };
627 | name = Release;
628 | };
629 | E5583BC52265DC1F0081702C /* Debug */ = {
630 | isa = XCBuildConfiguration;
631 | buildSettings = {
632 | APPLICATION_EXTENSION_API_ONLY = YES;
633 | BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
634 | CLANG_ENABLE_MODULES = YES;
635 | CODE_SIGN_IDENTITY = "";
636 | CODE_SIGN_STYLE = Manual;
637 | DEFINES_MODULE = YES;
638 | DEVELOPMENT_TEAM = "";
639 | DYLIB_COMPATIBILITY_VERSION = 1;
640 | DYLIB_CURRENT_VERSION = 1;
641 | DYLIB_INSTALL_NAME_BASE = "@rpath";
642 | FRAMEWORK_SEARCH_PATHS = (
643 | "$(inherited)",
644 | "$(PROJECT_DIR)/Carthage/Build/iOS",
645 | );
646 | INFOPLIST_FILE = SupportingFiles/Info.plist;
647 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
648 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
649 | LD_RUNPATH_SEARCH_PATHS = (
650 | "$(inherited)",
651 | "@executable_path/Frameworks",
652 | "@loader_path/Frameworks",
653 | );
654 | MARKETING_VERSION = 0.3.0;
655 | PRODUCT_BUNDLE_IDENTIFIER = com.akkeylab.lib.RxScreenProtectKit;
656 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
657 | PROVISIONING_PROFILE_SPECIFIER = "";
658 | SKIP_INSTALL = YES;
659 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
660 | SWIFT_VERSION = 5.0;
661 | TARGETED_DEVICE_FAMILY = "1,2";
662 | };
663 | name = Debug;
664 | };
665 | E5583BC62265DC1F0081702C /* Release */ = {
666 | isa = XCBuildConfiguration;
667 | buildSettings = {
668 | APPLICATION_EXTENSION_API_ONLY = YES;
669 | BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
670 | CLANG_ENABLE_MODULES = YES;
671 | CODE_SIGN_IDENTITY = "";
672 | CODE_SIGN_STYLE = Manual;
673 | DEFINES_MODULE = YES;
674 | DEVELOPMENT_TEAM = "";
675 | DYLIB_COMPATIBILITY_VERSION = 1;
676 | DYLIB_CURRENT_VERSION = 1;
677 | DYLIB_INSTALL_NAME_BASE = "@rpath";
678 | FRAMEWORK_SEARCH_PATHS = (
679 | "$(inherited)",
680 | "$(PROJECT_DIR)/Carthage/Build/iOS",
681 | );
682 | INFOPLIST_FILE = SupportingFiles/Info.plist;
683 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
684 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
685 | LD_RUNPATH_SEARCH_PATHS = (
686 | "$(inherited)",
687 | "@executable_path/Frameworks",
688 | "@loader_path/Frameworks",
689 | );
690 | MARKETING_VERSION = 0.3.0;
691 | PRODUCT_BUNDLE_IDENTIFIER = com.akkeylab.lib.RxScreenProtectKit;
692 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
693 | PROVISIONING_PROFILE_SPECIFIER = "";
694 | SKIP_INSTALL = YES;
695 | SWIFT_VERSION = 5.0;
696 | TARGETED_DEVICE_FAMILY = "1,2";
697 | };
698 | name = Release;
699 | };
700 | E5583BC82265DC1F0081702C /* Debug */ = {
701 | isa = XCBuildConfiguration;
702 | buildSettings = {
703 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
704 | CODE_SIGN_STYLE = Automatic;
705 | DEVELOPMENT_TEAM = Y6Y8U7YGDU;
706 | INFOPLIST_FILE = RxScreenProtectKitTests/Info.plist;
707 | LD_RUNPATH_SEARCH_PATHS = (
708 | "$(inherited)",
709 | "@executable_path/Frameworks",
710 | "@loader_path/Frameworks",
711 | );
712 | PRODUCT_BUNDLE_IDENTIFIER = com.akkeylab.lib.RxScreenProtectKit.RxScreenProtectKitTests;
713 | PRODUCT_NAME = "$(TARGET_NAME)";
714 | SWIFT_VERSION = 5.0;
715 | TARGETED_DEVICE_FAMILY = "1,2";
716 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RxScreenProtectKitExample.app/RxScreenProtectKitExample";
717 | };
718 | name = Debug;
719 | };
720 | E5583BC92265DC1F0081702C /* Release */ = {
721 | isa = XCBuildConfiguration;
722 | buildSettings = {
723 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
724 | CODE_SIGN_STYLE = Automatic;
725 | DEVELOPMENT_TEAM = Y6Y8U7YGDU;
726 | INFOPLIST_FILE = RxScreenProtectKitTests/Info.plist;
727 | LD_RUNPATH_SEARCH_PATHS = (
728 | "$(inherited)",
729 | "@executable_path/Frameworks",
730 | "@loader_path/Frameworks",
731 | );
732 | PRODUCT_BUNDLE_IDENTIFIER = com.akkeylab.lib.RxScreenProtectKit.RxScreenProtectKitTests;
733 | PRODUCT_NAME = "$(TARGET_NAME)";
734 | SWIFT_VERSION = 5.0;
735 | TARGETED_DEVICE_FAMILY = "1,2";
736 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RxScreenProtectKitExample.app/RxScreenProtectKitExample";
737 | };
738 | name = Release;
739 | };
740 | E5583BE62265FBB60081702C /* Debug */ = {
741 | isa = XCBuildConfiguration;
742 | buildSettings = {
743 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
744 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
745 | BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
746 | CODE_SIGN_STYLE = Automatic;
747 | CURRENT_PROJECT_VERSION = 1.2.0.0;
748 | DEVELOPMENT_TEAM = Y6Y8U7YGDU;
749 | FRAMEWORK_SEARCH_PATHS = (
750 | "$(inherited)",
751 | "$(PROJECT_DIR)/Carthage/Build/iOS",
752 | );
753 | INFOPLIST_FILE = RxScreenProtectKitExample/Info.plist;
754 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
755 | LD_RUNPATH_SEARCH_PATHS = (
756 | "$(inherited)",
757 | "@executable_path/Frameworks",
758 | );
759 | MARKETING_VERSION = 1.2.0;
760 | PRODUCT_BUNDLE_IDENTIFIER = com.akkeylab.lib.RxScreenProtectKitExample;
761 | PRODUCT_NAME = "$(TARGET_NAME)";
762 | SWIFT_VERSION = 5.0;
763 | TARGETED_DEVICE_FAMILY = 1;
764 | };
765 | name = Debug;
766 | };
767 | E5583BE72265FBB60081702C /* Release */ = {
768 | isa = XCBuildConfiguration;
769 | buildSettings = {
770 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
771 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
772 | BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
773 | CODE_SIGN_STYLE = Automatic;
774 | CURRENT_PROJECT_VERSION = 1.2.0.0;
775 | DEVELOPMENT_TEAM = Y6Y8U7YGDU;
776 | FRAMEWORK_SEARCH_PATHS = (
777 | "$(inherited)",
778 | "$(PROJECT_DIR)/Carthage/Build/iOS",
779 | );
780 | INFOPLIST_FILE = RxScreenProtectKitExample/Info.plist;
781 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
782 | LD_RUNPATH_SEARCH_PATHS = (
783 | "$(inherited)",
784 | "@executable_path/Frameworks",
785 | );
786 | MARKETING_VERSION = 1.2.0;
787 | PRODUCT_BUNDLE_IDENTIFIER = com.akkeylab.lib.RxScreenProtectKitExample;
788 | PRODUCT_NAME = "$(TARGET_NAME)";
789 | SWIFT_VERSION = 5.0;
790 | TARGETED_DEVICE_FAMILY = 1;
791 | };
792 | name = Release;
793 | };
794 | /* End XCBuildConfiguration section */
795 |
796 | /* Begin XCConfigurationList section */
797 | E5583BAA2265DC1F0081702C /* Build configuration list for PBXProject "RxScreenProtectKit" */ = {
798 | isa = XCConfigurationList;
799 | buildConfigurations = (
800 | E5583BC22265DC1F0081702C /* Debug */,
801 | E5583BC32265DC1F0081702C /* Release */,
802 | );
803 | defaultConfigurationIsVisible = 0;
804 | defaultConfigurationName = Release;
805 | };
806 | E5583BC42265DC1F0081702C /* Build configuration list for PBXNativeTarget "RxScreenProtectKit" */ = {
807 | isa = XCConfigurationList;
808 | buildConfigurations = (
809 | E5583BC52265DC1F0081702C /* Debug */,
810 | E5583BC62265DC1F0081702C /* Release */,
811 | );
812 | defaultConfigurationIsVisible = 0;
813 | defaultConfigurationName = Release;
814 | };
815 | E5583BC72265DC1F0081702C /* Build configuration list for PBXNativeTarget "RxScreenProtectKitTests" */ = {
816 | isa = XCConfigurationList;
817 | buildConfigurations = (
818 | E5583BC82265DC1F0081702C /* Debug */,
819 | E5583BC92265DC1F0081702C /* Release */,
820 | );
821 | defaultConfigurationIsVisible = 0;
822 | defaultConfigurationName = Release;
823 | };
824 | E5583BE52265FBB60081702C /* Build configuration list for PBXNativeTarget "RxScreenProtectKitExample" */ = {
825 | isa = XCConfigurationList;
826 | buildConfigurations = (
827 | E5583BE62265FBB60081702C /* Debug */,
828 | E5583BE72265FBB60081702C /* Release */,
829 | );
830 | defaultConfigurationIsVisible = 0;
831 | defaultConfigurationName = Release;
832 | };
833 | /* End XCConfigurationList section */
834 | };
835 | rootObject = E5583BA72265DC1F0081702C /* Project object */;
836 | }
837 |
--------------------------------------------------------------------------------
/RxScreenProtectKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RxScreenProtectKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/RxScreenProtectKit.xcodeproj/xcshareddata/xcschemes/RxScreenProtectKit.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
38 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
64 |
70 |
71 |
72 |
73 |
79 |
80 |
86 |
87 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/RxScreenProtectKit.xcodeproj/xcshareddata/xcschemes/RxScreenProtectKitExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/RxScreenProtectKitExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // RxScreenProtectKitExample
4 | //
5 | // Created by AKIO on 2019/04/16.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func application(_ application: UIApplication,
17 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | return true
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/RxScreenProtectKitExample/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 | }
--------------------------------------------------------------------------------
/RxScreenProtectKitExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/RxScreenProtectKitExample/Assets.xcassets/UserMainPhoto.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "UserMainPhoto.jpg"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/RxScreenProtectKitExample/Assets.xcassets/UserMainPhoto.imageset/UserMainPhoto.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AkkeyLab/RxScreenProtectKit/c482566809f739989941ae6d5315ee39fca2c9c9/RxScreenProtectKitExample/Assets.xcassets/UserMainPhoto.imageset/UserMainPhoto.jpg
--------------------------------------------------------------------------------
/RxScreenProtectKitExample/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 |
--------------------------------------------------------------------------------
/RxScreenProtectKitExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
85 |
91 |
92 |
93 |
94 |
95 |
96 |
102 |
103 |
104 |
105 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
213 |
214 |
215 |
216 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
268 |
274 |
275 |
276 |
277 |
278 |
279 |
285 |
286 |
287 |
288 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
--------------------------------------------------------------------------------
/RxScreenProtectKitExample/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 | APPL
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/RxScreenProtectKitExample/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // RxScreenProtectKitExample
4 | //
5 | // Created by AKIO on 2019/04/16.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import RxScreenProtectKit
10 | import RxSwift
11 | import UIKit
12 |
13 | final class FirstViewController: UIViewController {
14 | // https://www.pakutaso.com
15 | @IBOutlet private weak var mainImageView: UIImageView!
16 | @IBOutlet private weak var nameLabel: UILabel!
17 | @IBOutlet private weak var mainTextView: UITextView!
18 | @IBOutlet private weak var minificationFilterControl: UISegmentedControl!
19 | @IBOutlet private weak var magnificationFilterControl: UISegmentedControl!
20 | @IBOutlet private weak var scaleChanger: UISlider!
21 | @IBOutlet private weak var applyButton: UIButton!
22 | @IBOutlet private weak var scaleLabel: UILabel!
23 | @IBOutlet private weak var validSwitch: UISwitch!
24 | private let bag = DisposeBag()
25 |
26 | override func viewDidLoad() {
27 | super.viewDidLoad()
28 | if #available(iOS 13.0, *) {
29 | self.overrideUserInterfaceStyle = .light
30 | }
31 | self.rx.isScreenRecord
32 | .bind(to: mainImageView.layer.rx.isMosaic)
33 | .disposed(by: bag)
34 | self.rx.isScreenRecord
35 | .bind(to: nameLabel.layer.rx.isMosaic)
36 | .disposed(by: bag)
37 | self.rx.isScreenRecord
38 | .bind(to: mainTextView.layer.rx.isMosaic)
39 | .disposed(by: bag)
40 | applyButton.rx.tap
41 | .subscribe(onNext: { [weak self] in
42 | self?.mainImageView.layer.applyMosaic()
43 | self?.nameLabel.layer.applyMosaic()
44 | self?.mainTextView.layer.applyMosaic()
45 | })
46 | .disposed(by: bag)
47 | validSwitch.rx.value
48 | .subscribe(onNext: { isValid in
49 | ScreenProtectKit.shared.isValid = isValid
50 | })
51 | .disposed(by: bag)
52 | ScreenProtectKit
53 | .shared
54 | .isScreenRecord
55 | .subscribe(onNext: { isRecord in
56 | print(isRecord ? "🔴 Enable Record" : "🔵 Disable Record")
57 | })
58 | .disposed(by: bag)
59 |
60 | changeSettings()
61 | }
62 |
63 | func changeSettings() {
64 | let filterCase: [CALayerContentsFilter] = [.linear, .nearest, .trilinear]
65 | minificationFilterControl.rx.value
66 | .subscribe(onNext: { [weak self] index in
67 | guard let self = self else { return }
68 | ScreenProtectKit
69 | .shared
70 | .config(rasterizationScale: CGFloat(self.scaleChanger.value),
71 | minificationFilter: filterCase[index],
72 | magnificationFilter: filterCase[self.magnificationFilterControl.selectedSegmentIndex])
73 | })
74 | .disposed(by: bag)
75 | magnificationFilterControl.rx.value
76 | .subscribe(onNext: { [weak self] index in
77 | guard let self = self else { return }
78 | ScreenProtectKit
79 | .shared
80 | .config(rasterizationScale: CGFloat(self.scaleChanger.value),
81 | minificationFilter: filterCase[self.minificationFilterControl.selectedSegmentIndex],
82 | magnificationFilter: filterCase[index])
83 | })
84 | .disposed(by: bag)
85 | scaleChanger.rx.value
86 | .subscribe(onNext: { [weak self] value in
87 | guard let self = self else { return }
88 | ScreenProtectKit
89 | .shared
90 | .config(rasterizationScale: CGFloat(self.scaleChanger.value),
91 | minificationFilter: filterCase[self.minificationFilterControl.selectedSegmentIndex],
92 | magnificationFilter: filterCase[self.magnificationFilterControl.selectedSegmentIndex])
93 | self.scaleLabel.text = "\(value)"
94 | })
95 | .disposed(by: bag)
96 | }
97 | }
98 |
99 | final class SecondViewController: UIViewController {
100 | @IBOutlet private weak var bigImageView: UIImageView!
101 | @IBOutlet private weak var normalImageView: UIImageView!
102 | @IBOutlet private weak var shortImageView: UIImageView!
103 | @IBOutlet private weak var minificationFilterControl: UISegmentedControl!
104 | @IBOutlet private weak var magnificationFilterControl: UISegmentedControl!
105 | @IBOutlet private weak var pixelBoxSizeChanger: UISlider!
106 | @IBOutlet private weak var applyButton: UIButton!
107 | @IBOutlet private weak var pixelBoxSizeLabel: UILabel!
108 | @IBOutlet private weak var validSwitch: UISwitch!
109 | private let bag = DisposeBag()
110 |
111 | override func viewDidLoad() {
112 | super.viewDidLoad()
113 | self.rx.isScreenRecord
114 | .bind(to: bigImageView.layer.rx.isMosaic)
115 | .disposed(by: bag)
116 | self.rx.isScreenRecord
117 | .bind(to: normalImageView.layer.rx.isMosaic)
118 | .disposed(by: bag)
119 | self.rx.isScreenRecord
120 | .bind(to: shortImageView.layer.rx.isMosaic)
121 | .disposed(by: bag)
122 | applyButton.rx.tap
123 | .subscribe(onNext: { [weak self] in
124 | self?.bigImageView.layer.applyMosaic()
125 | self?.normalImageView.layer.applyMosaic()
126 | self?.shortImageView.layer.applyMosaic()
127 | })
128 | .disposed(by: bag)
129 | validSwitch.rx.value
130 | .subscribe(onNext: { isValid in
131 | ScreenProtectKit.shared.isValid = isValid
132 | })
133 | .disposed(by: bag)
134 |
135 | changeSettings()
136 | }
137 |
138 | func changeSettings() {
139 | let filterCase: [CALayerContentsFilter] = [.linear, .nearest, .trilinear]
140 | minificationFilterControl.rx.value
141 | .subscribe(onNext: { [weak self] index in
142 | guard let self = self else { return }
143 | ScreenProtectKit
144 | .shared
145 | .config(pixelBoxSize: CGFloat(self.pixelBoxSizeChanger.value),
146 | minificationFilter: filterCase[index],
147 | magnificationFilter: filterCase[self.magnificationFilterControl.selectedSegmentIndex])
148 | })
149 | .disposed(by: bag)
150 | magnificationFilterControl.rx.value
151 | .subscribe(onNext: { [weak self] index in
152 | guard let self = self else { return }
153 | ScreenProtectKit
154 | .shared
155 | .config(pixelBoxSize: CGFloat(self.pixelBoxSizeChanger.value),
156 | minificationFilter: filterCase[self.minificationFilterControl.selectedSegmentIndex],
157 | magnificationFilter: filterCase[index])
158 | })
159 | .disposed(by: bag)
160 | pixelBoxSizeChanger.rx.value
161 | .subscribe(onNext: { [weak self] value in
162 | guard let self = self else { return }
163 | ScreenProtectKit
164 | .shared
165 | .config(pixelBoxSize: CGFloat(self.pixelBoxSizeChanger.value),
166 | minificationFilter: filterCase[self.minificationFilterControl.selectedSegmentIndex],
167 | magnificationFilter: filterCase[self.magnificationFilterControl.selectedSegmentIndex])
168 | self.pixelBoxSizeLabel.text = "\(value)"
169 | })
170 | .disposed(by: bag)
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/RxScreenProtectKitTests/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 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/RxScreenProtectKitTests/RxBlocking+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RxBlocking+.swift
3 | // RxScreenProtectKitTests
4 | //
5 | // Created by AKIO on 2019/07/22.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import RxBlocking
10 | import RxSwift
11 |
12 | extension ObservableType {
13 | func blockingSingle() throws -> Self.Element {
14 | do {
15 | return try self.take(1).toBlocking(timeout: 1).single()
16 | } catch {
17 | throw error
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/RxScreenProtectKitTests/RxScreenProtectKitTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RxScreenProtectKitTests.swift
3 | // RxScreenProtectKitTests
4 | //
5 | // Created by AKIO on 2019/04/16.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 | import XCTest
11 | @testable import RxScreenProtectKit
12 |
13 | final class RxScreenProtectKitTests: XCTestCase {
14 |
15 | override func setUp() {
16 | super.setUp()
17 | ScreenProtectKit.shared.config()
18 | }
19 |
20 | func testMosaicType() {
21 | let type = MosaicType(isValid: true, rasterizationScale: 0.1, pixelBoxSize: 0)
22 | XCTAssertEqual(type.minificationFilter, .trilinear)
23 | XCTAssertEqual(type.rasterizationScale, 0.1)
24 | }
25 |
26 | func testValidAttachMosaic() {
27 | let layer = CALayer()
28 | layer.attachMosaic(type: MosaicType(isValid: true, rasterizationScale: 0.1, pixelBoxSize: 0))
29 | XCTAssertEqual(layer.minificationFilter, .trilinear)
30 | XCTAssertEqual(layer.magnificationFilter, .nearest)
31 | XCTAssertEqual(layer.rasterizationScale, 0.1)
32 | XCTAssertEqual(layer.shouldRasterize, true)
33 | }
34 |
35 | func testInvalidAttachMosaic() {
36 | let layer = CALayer()
37 | layer.attachMosaic(type: MosaicType(isValid: false, rasterizationScale: 0.1, pixelBoxSize: 0))
38 | XCTAssertEqual(layer.minificationFilter, .linear)
39 | XCTAssertEqual(layer.magnificationFilter, .linear)
40 | XCTAssertEqual(layer.rasterizationScale, 0.1)
41 | XCTAssertEqual(layer.shouldRasterize, false)
42 | }
43 |
44 | func testApplyMosaic() {
45 | let layer = CALayer()
46 | layer.shouldRasterize = true
47 | let kit = ScreenProtectKit.shared
48 | kit.config(rasterizationScale: 0.5, minificationFilter: .trilinear, magnificationFilter: .trilinear)
49 |
50 | layer.applyMosaic(kit: kit)
51 | XCTAssertEqual(layer.shouldRasterize, true)
52 | XCTAssertEqual(layer.rasterizationScale, 0.5)
53 | XCTAssertEqual(layer.minificationFilter, .trilinear)
54 | XCTAssertEqual(layer.magnificationFilter, .trilinear)
55 | }
56 |
57 | func testLoadValidFlag() {
58 | let kit = ScreenProtectKit.shared
59 | kit.isValid = false
60 | XCTAssertEqual(kit.isValid, false)
61 | }
62 |
63 | // swiftlint:disable force_try
64 | func testFlagValidRecodeNotificationForViewController() {
65 | let viewController = UIViewController()
66 | ScreenProtectKit.shared.isValid = true
67 |
68 | DispatchQueue.main.async {
69 | viewController.viewWillAppear(false)
70 | }
71 |
72 | let isRecord = try! viewController.rx.isScreenRecord
73 | .skip(1)
74 | .blockingSingle()
75 | XCTAssertEqual(isRecord, false)
76 | }
77 |
78 | func testFlagValidRecodeNotificationForView() {
79 | let view = UIView()
80 | ScreenProtectKit.shared.isValid = true
81 |
82 | DispatchQueue.main.async {
83 | view.layoutSubviews()
84 | }
85 |
86 | let isRecord = try! view.rx.isScreenRecord
87 | .skip(1)
88 | .blockingSingle()
89 | XCTAssertEqual(isRecord, false)
90 | }
91 |
92 | func testFlagInvalidRecodeNotificationForViewController() {
93 | let viewController = UIViewController()
94 | let bag = DisposeBag()
95 | ScreenProtectKit.shared.isValid = false
96 |
97 | viewController.rx.isScreenRecord
98 | .skip(1)
99 | .subscribe(onNext: { _ in
100 | XCTAssert(false)
101 | })
102 | .disposed(by: bag)
103 |
104 | viewController.viewWillAppear(false)
105 | }
106 |
107 | func testFlagInvalidRecodeNotificationForView() {
108 | let view = UIView()
109 | let bag = DisposeBag()
110 | ScreenProtectKit.shared.isValid = false
111 |
112 | _ = view.rx.isScreenRecord
113 | .skip(1)
114 | .subscribe(onNext: { _ in
115 | XCTAssert(false)
116 | })
117 | .disposed(by: bag)
118 |
119 | view.layoutSubviews()
120 | }
121 |
122 | func testFlagValidForViewController() {
123 | let viewController = UIViewController()
124 | let kit = ScreenProtectKit.shared
125 |
126 | kit.isValid = false // For distinctUntilChanged()
127 | DispatchQueue.main.async {
128 | kit.isValid = true
129 | }
130 |
131 | let isRecord = try! viewController.rx.isScreenRecord
132 | .skip(1)
133 | .blockingSingle()
134 | XCTAssertEqual(isRecord, false)
135 | }
136 |
137 | func testFlagInvalidForViewController() {
138 | let viewController = UIViewController()
139 | let kit = ScreenProtectKit.shared
140 |
141 | kit.isValid = true // For distinctUntilChanged()
142 | DispatchQueue.main.async {
143 | kit.isValid = false
144 | }
145 |
146 | let isRecord = try! viewController.rx.isScreenRecord
147 | .skip(1)
148 | .blockingSingle()
149 | XCTAssertEqual(isRecord, false)
150 | }
151 |
152 | func testFlagValidForView() {
153 | let view = UIView()
154 | let kit = ScreenProtectKit.shared
155 |
156 | kit.isValid = false // For distinctUntilChanged()
157 | DispatchQueue.main.async {
158 | kit.isValid = true
159 | }
160 |
161 | let isRecord = try! view.rx.isScreenRecord
162 | .skip(1)
163 | .blockingSingle()
164 | XCTAssertEqual(isRecord, false)
165 | }
166 |
167 | func testFlagInvalidForView() {
168 | let view = UIView()
169 | let kit = ScreenProtectKit.shared
170 |
171 | kit.isValid = true // For distinctUntilChanged()
172 | DispatchQueue.main.async {
173 | kit.isValid = false
174 | }
175 |
176 | let isRecord = try! view.rx.isScreenRecord
177 | .skip(1)
178 | .blockingSingle()
179 | XCTAssertEqual(isRecord, false)
180 | }
181 |
182 | func testSPImageView() {
183 | let view = SPImageView()
184 | view.layoutSubviews()
185 | XCTAssertEqual(view.image, nil)
186 | view.image = UIImage(named: "UserMainPhoto")
187 | XCTAssertEqual(view.layer.minificationFilter, .linear)
188 | XCTAssertEqual(view.layer.magnificationFilter, .linear)
189 | XCTAssertEqual(view.layer.rasterizationScale, 0.1)
190 | XCTAssertEqual(view.layer.shouldRasterize, false)
191 | XCTAssertEqual(view.image, UIImage(named: "UserMainPhoto"))
192 | }
193 |
194 | func testSPLabel() {
195 | let changeTextExpectation: XCTestExpectation? = self.expectation(description: "Change Text")
196 | let label = SPLabel()
197 | label.layoutSubviews()
198 | XCTAssertEqual(label.text, nil)
199 | label.text = "original"
200 | label.protectText = "protect"
201 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
202 | XCTAssertEqual(label.text, "original")
203 | changeTextExpectation?.fulfill()
204 | }
205 | self.waitForExpectations(timeout: 1, handler: nil)
206 | }
207 |
208 | func testPixelBoxSize() {
209 | let layer = CALayer()
210 | layer.frame = CGRect(x: 0, y: 0, width: 150, height: 50)
211 | layer.attachMosaic(type: MosaicType(isValid: true, rasterizationScale: 0.1, pixelBoxSize: 5))
212 | XCTAssertEqual(layer.minificationFilter, .trilinear)
213 | XCTAssertEqual(layer.magnificationFilter, .nearest)
214 | XCTAssertEqual(layer.rasterizationScale, 0.05)
215 | XCTAssertEqual(layer.shouldRasterize, true)
216 | layer.attachMosaic(type: MosaicType(isValid: true, rasterizationScale: 0.1, pixelBoxSize: 10))
217 | XCTAssertEqual(layer.rasterizationScale, 0.1)
218 | }
219 |
220 | func testFrameResizeSPImageView() {
221 | ScreenProtectKit.shared.config(pixelBoxSize: 5, minificationFilter: .trilinear, magnificationFilter: .trilinear)
222 | let changeFrameExpectation: XCTestExpectation? = self.expectation(description: "Change frame")
223 | let view = SPImageView()
224 | view.layoutSubviews()
225 | XCTAssertEqual(view.layer.rasterizationScale, 0.1)
226 |
227 | view.frame = CGRect(x: 0, y: 0, width: 150, height: 50)
228 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
229 | XCTAssertEqual(view.layer.rasterizationScale, 0.05)
230 | changeFrameExpectation?.fulfill()
231 | }
232 | self.waitForExpectations(timeout: 1, handler: nil)
233 | }
234 |
235 | func testBoundsResizeSPImageView() {
236 | ScreenProtectKit.shared.config(pixelBoxSize: 5, minificationFilter: .trilinear, magnificationFilter: .trilinear)
237 | let changeFrameExpectation: XCTestExpectation? = self.expectation(description: "Change frame")
238 | let view = SPImageView()
239 | view.layoutSubviews()
240 | XCTAssertEqual(view.layer.rasterizationScale, 0.1)
241 |
242 | view.bounds = CGRect(x: 0, y: 0, width: 150, height: 50)
243 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
244 | XCTAssertEqual(view.layer.rasterizationScale, 0.05)
245 | changeFrameExpectation?.fulfill()
246 | }
247 | self.waitForExpectations(timeout: 1, handler: nil)
248 | }
249 |
250 | func testIsScreenRecordSubscribeFromShared() {
251 | let kit = ScreenProtectKit.shared
252 |
253 | let isScreenRecord = try! kit.isScreenRecord
254 | .blockingSingle()
255 | XCTAssertEqual(isScreenRecord, false)
256 | }
257 |
258 | func testIsScreenRecordEnableFromShared() {
259 | let kit = ScreenProtectKit.shared
260 |
261 | kit.isValid = false // For distinctUntilChanged()
262 | DispatchQueue.main.async {
263 | kit.isValid = true
264 | }
265 |
266 | let isScreenRecord = try! kit.isScreenRecord
267 | .skip(1)
268 | .blockingSingle()
269 | XCTAssertEqual(isScreenRecord, false)
270 | }
271 |
272 | func testIsScreenRecordDisableFromShared() {
273 | let kit = ScreenProtectKit.shared
274 |
275 | kit.isValid = true // For distinctUntilChanged()
276 | DispatchQueue.main.async {
277 | kit.isValid = false
278 | }
279 |
280 | let isScreenRecord = try! kit.isScreenRecord
281 | .skip(1)
282 | .blockingSingle()
283 | XCTAssertEqual(isScreenRecord, false)
284 | }
285 | // swiftlint:enable force_try
286 | }
287 |
--------------------------------------------------------------------------------
/Source/Extensions/CALayer+Mosaic.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CALayer+Mosaic.swift
3 | // RxScreenProtectKit
4 | //
5 | // Created by AKIO on 2019/08/29.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import QuartzCore
10 | import RxCocoa
11 | import RxSwift
12 |
13 | struct MosaicType {
14 | /// This flag indicates the presence or absence of a mosaic.
15 | var isValid: Bool
16 | /// The scale at which to rasterize content, relative to the coordinate space of the layer.
17 | var rasterizationScale: CGFloat
18 | /// When compressing an image, specify the number of pixels to be compressed to one pixel.
19 | var pixelBoxSize: CGFloat
20 | /// The filter used when reducing the size of the content.
21 | var minificationFilter: CALayerContentsFilter
22 | /// The filter used when increasing the size of the content.
23 | var magnificationFilter: CALayerContentsFilter
24 |
25 | /**
26 | This type indicates a mosaic.
27 |
28 | - parameters:
29 | - isValid: This flag indicates the presence or absence of a mosaic.
30 | - rasterizationScale: The scale at which to rasterize content, relative to the coordinate space of the layer.
31 | - pixelBoxSize: When compressing an image, specify the number of pixels to be compressed to one pixel.
32 | If this is non-zero, `rasterizationScale` will be ignored.
33 | - minificationFilter: The filter used when reducing the size of the content.
34 | - magnificationFilter: The filter used when increasing the size of the content.
35 | */
36 | init (isValid: Bool,
37 | rasterizationScale: CGFloat,
38 | pixelBoxSize: CGFloat,
39 | minificationFilter: CALayerContentsFilter = .trilinear,
40 | magnificationFilter: CALayerContentsFilter = .nearest) {
41 | self.isValid = isValid
42 | self.rasterizationScale = rasterizationScale
43 | self.pixelBoxSize = pixelBoxSize
44 | self.minificationFilter = minificationFilter
45 | self.magnificationFilter = magnificationFilter
46 | }
47 | }
48 |
49 | extension CALayer {
50 | /**
51 | attach the mosaic.
52 |
53 | - parameters:
54 | - type: This type indicates a mosaic.
55 | */
56 | func attachMosaic(type: MosaicType) {
57 | let absoluteScale = (2 * type.pixelBoxSize) / (bounds.height + bounds.width)
58 | let isValidityScale = absoluteScale > 0 && !absoluteScale.isNaN && !absoluteScale.isInfinite
59 | rasterizationScale = isValidityScale ? absoluteScale : type.rasterizationScale
60 | shouldRasterize = type.isValid
61 | minificationFilter = type.isValid ? type.minificationFilter : .linear
62 | magnificationFilter = type.isValid ? type.magnificationFilter : .linear
63 | }
64 |
65 | /// Reflects the parameter change.
66 | /// Use in a production environment is deprecated.
67 | public func applyMosaic(kit: ScreenProtectKit = .shared) {
68 | attachMosaic(type: .init(isValid: shouldRasterize,
69 | rasterizationScale: kit.rasterizationScale,
70 | pixelBoxSize: kit.pixelBoxSize,
71 | minificationFilter: kit.minificationFilter,
72 | magnificationFilter: kit.magnificationFilter)
73 | )
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Source/Extensions/CALayer+Rx.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CALayer+Rx.swift
3 | // RxScreenProtectKit
4 | //
5 | // Created by AKIO on 2019/04/16.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import QuartzCore
10 | import RxCocoa
11 | import RxSwift
12 |
13 | public extension Reactive where Base: CALayer {
14 | /// Bindable sink for Mosaic.
15 | var isMosaic: Binder {
16 | return Binder(self.base) { layer, isValid in
17 | let kit = ScreenProtectKit.shared
18 | layer.attachMosaic(type: .init(isValid: isValid,
19 | rasterizationScale: kit.rasterizationScale,
20 | pixelBoxSize: kit.pixelBoxSize,
21 | minificationFilter: kit.minificationFilter,
22 | magnificationFilter: kit.magnificationFilter)
23 | )
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Source/Extensions/NotificationCenter+Rx.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NotificationCenter+Rx.swift
3 | // RxScreenProtectKit
4 | //
5 | // Created by AKIO on 2019/08/29.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 | import UIKit
11 |
12 | extension Reactive where Base: NotificationCenter {
13 | var updateTrigger: Observable {
14 | return Observable.of(
15 | NotificationCenter.default.rx
16 | .notification(UIScreen.capturedDidChangeNotification),
17 | NotificationCenter.default.rx
18 | .notification(UIApplication.willEnterForegroundNotification),
19 | NotificationCenter.default.rx
20 | .notification(UIApplication.didEnterBackgroundNotification)
21 | )
22 | .merge()
23 | .withLatestFrom(ScreenProtectKit.shared.isValidState)
24 | .filter { $0 }
25 | .map { _ in () }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Source/Extensions/UIView+Rx.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Rx.swift
3 | // RxScreenProtectKit
4 | //
5 | // Created by AKIO on 2019/07/18.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 | import UIKit
11 |
12 | extension Reactive where Base: UIView {
13 | var layoutSubviews: Observable {
14 | return sentMessage(#selector(base.layoutSubviews))
15 | .map { _ in () }
16 | .share(replay: 1, scope: .forever)
17 | }
18 |
19 | /// A value that indicates whether the contents of the screen are being cloned to another destination.
20 | public var isScreenRecord: Observable {
21 | return Observable
22 | .of(updateTrigger, NotificationCenter.default.rx.updateTrigger)
23 | .merge()
24 | .withLatestFrom(ScreenProtectKit.shared.isValidState)
25 | .map { isValid in
26 | return isValid ? UIScreen.main.isCaptured : false
27 | }
28 | .share(replay: 1, scope: .forever)
29 | }
30 |
31 | private var updateTrigger: Observable {
32 | return Observable.of(
33 | base.rx.layoutSubviews
34 | .withLatestFrom(ScreenProtectKit.shared.isValidState)
35 | .filter { $0 }
36 | .map { _ in () },
37 | ScreenProtectKit.shared.isValidState
38 | .map { _ in () }
39 | )
40 | .merge()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Source/Extensions/UIViewController+Rx.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Rx.swift
3 | // RxScreenProtectKit
4 | //
5 | // Created by AKIO on 2019/07/08.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 | import UIKit
11 |
12 | extension Reactive where Base: UIViewController {
13 | var viewWillAppear: Observable {
14 | return sentMessage(#selector(base.viewWillAppear(_:)))
15 | .map { _ in () }
16 | .share(replay: 1, scope: .forever)
17 | }
18 |
19 | /// A value that indicates whether the contents of the screen are being cloned to another destination.
20 | public var isScreenRecord: Observable {
21 | return Observable
22 | .of(updateTrigger, NotificationCenter.default.rx.updateTrigger)
23 | .merge()
24 | .withLatestFrom(ScreenProtectKit.shared.isValidState)
25 | .map { isValid in
26 | return isValid ? UIScreen.main.isCaptured : false
27 | }
28 | .share(replay: 1, scope: .forever)
29 | }
30 |
31 | private var updateTrigger: Observable {
32 | return Observable.of(
33 | base.rx.viewWillAppear
34 | .withLatestFrom(ScreenProtectKit.shared.isValidState)
35 | .filter { $0 }
36 | .map { _ in () },
37 | ScreenProtectKit.shared.isValidState
38 | .map { _ in () }
39 | )
40 | .merge()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Source/SPImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SPImageView.swift
3 | // RxScreenProtectKit
4 | //
5 | // Created by AKIO on 2019/09/10.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 | import UIKit
11 |
12 | public final class SPImageView: UIImageView {
13 | private var load = PublishSubject()
14 |
15 | override public var image: UIImage? {
16 | get {
17 | return super.image
18 | }
19 | set {
20 | super.image = newValue
21 | if newValue != nil {
22 | load.onNext(())
23 | }
24 | }
25 | }
26 |
27 | override public var frame: CGRect {
28 | get {
29 | return super.frame
30 | }
31 | set {
32 | super.frame = newValue
33 | load.onNext(())
34 | }
35 | }
36 |
37 | override public var bounds: CGRect {
38 | get {
39 | return super.bounds
40 | }
41 | set {
42 | super.bounds = newValue
43 | load.onNext(())
44 | }
45 | }
46 |
47 | override public func layoutSubviews() {
48 | super.layoutSubviews()
49 | setup()
50 | }
51 |
52 | private lazy var setup: (() -> Void) = {
53 | _ = Observable.of(
54 | self.rx.isScreenRecord.map { _ in () },
55 | load
56 | )
57 | .merge()
58 | .withLatestFrom(self.rx.isScreenRecord)
59 | .take(until: self.rx.deallocated)
60 | .bind(to: self.layer.rx.isMosaic)
61 | return {}
62 | }()
63 | }
64 |
--------------------------------------------------------------------------------
/Source/SPLabel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SPLabel.swift
3 | // RxScreenProtectKit
4 | //
5 | // Created by AKIO on 2019/10/10.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import RxSwift
10 | import UIKit
11 |
12 | public final class SPLabel: UILabel {
13 | private var load = PublishSubject()
14 | private var original: String?
15 | /// Specify the character string to be replaced during recording.
16 | /// If nothing is specified, `nil` is assigned to `text` when a recording is detected.
17 | public var protectText: String? {
18 | didSet {
19 | load.onNext(())
20 | }
21 | }
22 | /// The current text that is displayed by the label.
23 | /// Please note that when recording, a different string will be returned from the original string.
24 | override public var text: String? {
25 | get {
26 | return super.text
27 | }
28 | set {
29 | super.text = newValue
30 | original = newValue
31 | load.onNext(())
32 | }
33 | }
34 |
35 | override public func layoutSubviews() {
36 | super.layoutSubviews()
37 | setup()
38 | }
39 |
40 | private lazy var setup: (() -> Void) = {
41 | _ = Observable.of(
42 | self.rx.isScreenRecord.map { _ in () },
43 | load
44 | )
45 | .merge()
46 | .withLatestFrom(self.rx.isScreenRecord)
47 | .take(until: self.rx.deallocated)
48 | .subscribe(onNext: { [weak self] isScreenRecord in
49 | guard let self = self else { return }
50 | self.internalChanges(text: isScreenRecord ? self.protectText : self.original)
51 | })
52 | return {}
53 | }()
54 |
55 | private func internalChanges(text: String?) {
56 | super.text = text
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Source/ScreenProtectKit.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScreenProtectKit.swift
3 | // RxScreenProtectKit
4 | //
5 | // Created by AKIO on 2019/07/03.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | import QuartzCore
10 | import RxCocoa
11 | import RxSwift
12 | import UIKit
13 |
14 | public final class ScreenProtectKit {
15 | public static let shared = ScreenProtectKit()
16 |
17 | internal var rasterizationScale: CGFloat = 0.1
18 | internal var pixelBoxSize: CGFloat = 0
19 | internal var minificationFilter: CALayerContentsFilter = .trilinear
20 | internal var magnificationFilter: CALayerContentsFilter = .nearest
21 | internal lazy var isValidState = _isValid.distinctUntilChanged()
22 |
23 | private var _isValid = BehaviorRelay(value: true)
24 | /**
25 | You can switch whether or not to subscribe to the screen recording status. Initially enabled.
26 | In addition, immediately after switching between disabled and enabled,
27 | false will flow once from `isScreenRecord` regardless of whether screen recording is performed.
28 | */
29 | public var isValid: Bool {
30 | get {
31 | return _isValid.value
32 | }
33 | set {
34 | _isValid.accept(newValue)
35 | }
36 | }
37 |
38 | /**
39 | A value that indicates whether the contents of the screen are being cloned to another destination.
40 |
41 | Conditions under which the value flows:
42 | - When you subscribe
43 | - When the app moves to the background
44 | - When the app moves to the foreground
45 | - When mirroring starts
46 | - When mirroring is completed
47 | - When ScreenProtect Kit is disabled
48 | - When ScreenProtect Kit is enabled
49 | */
50 | public var isScreenRecord: Observable {
51 | return Observable
52 | .of(NotificationCenter.default.rx.updateTrigger, isValidState.map { _ in () })
53 | .merge()
54 | .withLatestFrom(_isValid)
55 | .map { isValid in
56 | return isValid ? UIScreen.main.isCaptured : false
57 | }
58 | .share(replay: 1, scope: .forever)
59 | }
60 |
61 | private init() {}
62 |
63 | /**
64 | Set up various parameters.
65 |
66 | - parameters:
67 | - rasterizationScale: The scale at which to rasterize content, relative to the coordinate space of the layer.
68 | - pixelBoxSize: When compressing an image, specify the number of pixels to be compressed to one pixel.
69 | If this is non-zero, `rasterizationScale` will be ignored.
70 | - minificationFilter: The filter used when reducing the size of the content.
71 | - magnificationFilter: The filter used when increasing the size of the content.
72 | */
73 | public func config(rasterizationScale: CGFloat = 0.1,
74 | pixelBoxSize: CGFloat = 0,
75 | minificationFilter: CALayerContentsFilter = .trilinear,
76 | magnificationFilter: CALayerContentsFilter = .nearest) {
77 | self.rasterizationScale = rasterizationScale
78 | self.pixelBoxSize = pixelBoxSize
79 | self.minificationFilter = minificationFilter
80 | self.magnificationFilter = magnificationFilter
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/SupportingFiles/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 | FMWK
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SupportingFiles/ScreenProtectKit.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // ScreenProtectKit.xcconfig
3 | // RxScreenProtectKit
4 | //
5 | // Created by AKIO on 2019/04/16.
6 | // Copyright © 2019 AKIO. All rights reserved.
7 | //
8 |
9 | // Configuration settings file format documentation can be found at:
10 | // https://help.apple.com/xcode/#/dev745c5c974
11 |
12 | SUPPORTED_PLATFORMS = iphoneos iphonesimulator
13 |
14 | SWIFT_VERSION = 5.0
15 |
16 | IPHONEOS_DEPLOYMENT_TARGET = 11.0
17 |
18 | FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*] = $(PROJECT_DIR)/Carthage/Build/iOS
19 | FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*] = $(PROJECT_DIR)/Carthage/Build/iOS
20 |
--------------------------------------------------------------------------------
/carthage.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # carthage.sh
4 | # Usage example: ./carthage.sh build --platform iOS
5 |
6 | set -euo pipefail
7 |
8 | xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
9 | trap 'rm -f "$xcconfig"' INT TERM HUP EXIT
10 |
11 | # For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise
12 | # the build will fail on lipo due to duplicate architectures.
13 | echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = arm64 arm64e armv7 armv7s armv6 armv8' >> $xcconfig
14 | echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig
15 |
16 | export XCODE_XCCONFIG_FILE="$xcconfig"
17 | carthage "$@"
18 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | - "RxScreenProtectKitExample"
3 | - "RxScreenProtectKitTests"
4 |
5 |
--------------------------------------------------------------------------------
/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | function command_exists {
4 | command -v "$1" > /dev/null;
5 | }
6 |
7 | echo "git \"file://$($(dirname $0); pwd)\"" >> Cartfile
8 |
9 | # carthage
10 | if ! command_exists brew ; then
11 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
12 | brew update
13 | brew upgrade --all
14 | brew doctor
15 | brew -v
16 | fi
17 |
18 | if ! command_exists carthage ; then
19 | brew install carthage
20 | fi
21 |
22 | carthage update --platform iOS --no-use-binaries --cache-builds
23 |
24 | # SwiftLint
25 | if ! command_exists swiftlint ; then
26 | brew install swiftlint
27 | fi
28 |
29 |
--------------------------------------------------------------------------------