├── .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 | Miss Electric Eel 2016 RxScreenProtectKit 2 | ====================================== 3 | [![RxScreenProtectKit](https://cocoapod-badges.herokuapp.com/v/RxScreenProtectKit/badge.png)](https://cocoapods.org/pods/RxScreenProtectKit) 4 | ![ios](https://cocoapod-badges.herokuapp.com/p/RxScreenProtectKit/badge.png) 5 | ![MIT](https://cocoapod-badges.herokuapp.com/l/RxScreenProtectKit/badge.png) 6 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 7 | [![Build Status](https://travis-ci.com/AkkeyLab/RxScreenProtectKit.svg?branch=master)](https://travis-ci.com/AkkeyLab/RxScreenProtectKit) 8 | [![codecov](https://codecov.io/gh/AkkeyLab/RxScreenProtectKit/branch/master/graph/badge.svg)](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 |
14 | 15 |
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 | --------------------------------------------------------------------------------