The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitignore
├── .travis.yml
├── LICENSE
├── Permission.podspec
├── Permission.xcodeproj
    ├── project.pbxproj
    ├── project.xcworkspace
    │   ├── contents.xcworkspacedata
    │   └── xcshareddata
    │   │   └── IDEWorkspaceChecks.plist
    └── xcshareddata
    │   └── xcschemes
    │       ├── Permission.xcscheme
    │       └── PermissionTests.xcscheme
├── PermissionConfiguration.xcconfig
├── README.md
├── Source
    ├── Permission.swift
    ├── PermissionAlert.swift
    ├── PermissionButton.swift
    ├── PermissionSet.swift
    ├── PermissionStatus.swift
    ├── PermissionType.swift
    ├── Supporting Files
    │   ├── Info.plist
    │   ├── Permission.h
    │   ├── PermissionFlags.xcconfig
    │   └── Utilities.swift
    └── Types
    │   ├── Bluetooth.swift
    │   ├── Camera.swift
    │   ├── Contacts.swift
    │   ├── Events.swift
    │   ├── Location.swift
    │   ├── LocationAlways.swift
    │   ├── LocationWhenInUse.swift
    │   ├── MediaLibrary.swift
    │   ├── Microphone.swift
    │   ├── Motion.swift
    │   ├── Notifications.swift
    │   ├── Photos.swift
    │   ├── Reminders.swift
    │   ├── Siri.swift
    │   └── SpeechRecognizer.swift
└── Tests
    ├── PermissionTests.swift
    └── Supporting Files
        └── Info.plist


/.gitignore:
--------------------------------------------------------------------------------
 1 | # Xcode
 2 | #
 3 | # gitignore contributors: remember to update Global/Xcode.gitignore,
 4 | # Objective-C.gitignore & Swift.gitignore
 5 | 
 6 | ## Build generated
 7 | build/
 8 | DerivedData
 9 | 
10 | ## Various settings
11 | *.pbxuser
12 | !default.pbxuser
13 | *.mode1v3
14 | !default.mode1v3
15 | *.mode2v3
16 | !default.mode2v3
17 | *.perspectivev3
18 | !default.perspectivev3
19 | xcuserdata
20 | 
21 | ## Other
22 | *.xccheckout
23 | *.moved-aside
24 | *.xcuserstate
25 | *.xcscmblueprint
26 | 
27 | ## Obj-C/Swift specific
28 | *.hmap
29 | *.ipa
30 | 
31 | # Carthage
32 | Carthage/Build
33 | 


--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
 1 | language: swift
 2 | 
 3 | branches:
 4 |   only:
 5 |     - master
 6 | 
 7 | xcode_project: Permission.xcodeproj
 8 | xcode_scheme: PermissionTests
 9 | osx_image: xcode11.2
10 | 
11 | script:
12 |   - xcodebuild test -project Permission.xcodeproj -scheme PermissionTests -destination "platform=iOS Simulator,name=iPhone 11"
13 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2015-2016 Damien (http://delba.io)
 2 | 
 3 | Permission is hereby granted, free of charge, to any person obtaining a copy
 4 | of this software and associated documentation files (the "Software"), to deal
 5 | in the Software without restriction, including without limitation the rights
 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 7 | copies of the Software, and to permit persons to whom the Software is
 8 | furnished to do so, subject to the following conditions:
 9 | 
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 | 
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 | 


--------------------------------------------------------------------------------
/Permission.podspec:
--------------------------------------------------------------------------------
 1 | Pod::Spec.new do |s|
 2 |   s.name          = 'Permission'
 3 |   s.version       = '3.1.2'
 4 |   s.license       = 'MIT'
 5 |   s.homepage      = 'https://github.com/delba/Permission'
 6 |   s.author        = { 'Damien' => 'damien@delba.io' }
 7 |   s.summary       = 'A unified API to ask for permissions on iOS'
 8 |   s.source        = { :git => 'https://github.com/delba/Permission.git', :tag => s.version }
 9 |   s.swift_version = '5.1'
10 | 
11 |   s.weak_framework = 'Speech'
12 | 
13 |   s.ios.deployment_target = '10.0'
14 | 
15 |   s.requires_arc = true
16 | 
17 |   s.default_subspec = 'Core'
18 | 
19 |   s.subspec 'Core' do |co|
20 |     co.source_files = 'Source/**/*.{swift, h}'
21 |   end
22 | 
23 |   s.subspec 'Bluetooth' do |bl|
24 |     bl.dependency 'Permission/Core'
25 |     bl.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_BLUETOOTH' }
26 |   end
27 | 
28 |   s.subspec 'Camera' do |cm|
29 |     cm.dependency 'Permission/Core'
30 |     cm.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_CAMERA' }
31 |   end
32 | 
33 |   s.subspec 'Contacts' do |cn|
34 |     cn.dependency 'Permission/Core'
35 |     cn.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_CONTACTS' }
36 |   end
37 | 
38 |   s.subspec 'Events' do |ev|
39 |     ev.dependency 'Permission/Core'
40 |     ev.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_EVENTS' }
41 |   end
42 | 
43 |   s.subspec 'Location' do |lo|
44 |     lo.dependency 'Permission/Core'
45 |     lo.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_LOCATION' }
46 |   end
47 | 
48 |   s.subspec 'Microphone' do |mi|
49 |     mi.dependency 'Permission/Core'
50 |     mi.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_MICROPHONE' }
51 |   end
52 | 
53 |   s.subspec 'Motion' do |mo|
54 |     mo.dependency 'Permission/Core'
55 |     mo.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_MOTION' }
56 |   end
57 | 
58 |   s.subspec 'Notifications' do |no|
59 |     no.dependency 'Permission/Core'
60 |     no.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_NOTIFICATIONS' }
61 |   end
62 | 
63 |   s.subspec 'Photos' do |ph|
64 |     ph.dependency 'Permission/Core'
65 |     ph.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_PHOTOS' }
66 |   end
67 | 
68 |   s.subspec 'Reminders' do |re|
69 |     re.dependency 'Permission/Core'
70 |     re.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_REMINDERS' }
71 |   end
72 | 
73 |   s.subspec 'SpeechRecognizer' do |rs|
74 |     rs.dependency 'Permission/Core'
75 |     rs.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_SPEECH_RECOGNIZER' }
76 |   end
77 | 
78 |   s.subspec 'MediaLibrary' do |ml|
79 |     ml.dependency 'Permission/Core'
80 |     ml.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_MEDIA_LIBRARY' }
81 |   end
82 | 
83 |   s.subspec 'Siri' do |ab|
84 |     ab.dependency 'Permission/Core'
85 |     ab.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS'  => 'PERMISSION_SIRI' }
86 |   end
87 | end
88 | 


--------------------------------------------------------------------------------
/Permission.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
  1 | // !$*UTF8*$!
  2 | {
  3 | 	archiveVersion = 1;
  4 | 	classes = {
  5 | 	};
  6 | 	objectVersion = 46;
  7 | 	objects = {
  8 | 
  9 | /* Begin PBXBuildFile section */
 10 | 		3D42A7DB1D5F66B300236ABA /* SpeechRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D42A7DA1D5F66B300236ABA /* SpeechRecognizer.swift */; };
 11 | 		3DC217D31D6EFD4A00600DFE /* MediaLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC217D21D6EFD4A00600DFE /* MediaLibrary.swift */; };
 12 | 		3F21DC7E1E30E0B900B3EF65 /* Siri.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F21DC7C1E30B6DB00B3EF65 /* Siri.swift */; };
 13 | 		6D0EBDBD1BFCF8B700C35F8E /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D0EBDBC1BFCF8B700C35F8E /* Utilities.swift */; };
 14 | 		6D491E781C9CA90B00611006 /* PermissionStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D491E771C9CA90B00611006 /* PermissionStatus.swift */; };
 15 | 		6D7D08D51C9DFD9D00746121 /* PermissionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D7D08D41C9DFD9D00746121 /* PermissionTests.swift */; };
 16 | 		6D86A9B61BEBDC7D00E3DD5A /* Permission.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D86A9B51BEBDC7D00E3DD5A /* Permission.h */; settings = {ATTRIBUTES = (Public, ); }; };
 17 | 		6D86A9BD1BEBDC7D00E3DD5A /* Permission.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D86A9B21BEBDC7D00E3DD5A /* Permission.framework */; };
 18 | 		6D86A9CD1BEBDC9000E3DD5A /* Permission.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D86A9CC1BEBDC9000E3DD5A /* Permission.swift */; };
 19 | 		6D935F5D1C9A0FEA00BB39E3 /* Bluetooth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D935F5C1C9A0FEA00BB39E3 /* Bluetooth.swift */; };
 20 | 		6D935F5F1C9A14AB00BB39E3 /* Motion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D935F5E1C9A14AB00BB39E3 /* Motion.swift */; };
 21 | 		6DA8B4B01BFB80E9007A94FC /* PermissionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DA8B4AF1BFB80E9007A94FC /* PermissionButton.swift */; };
 22 | 		6DA8B4B21BFB8AD8007A94FC /* PermissionAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DA8B4B11BFB8AD8007A94FC /* PermissionAlert.swift */; };
 23 | 		6DF9C2AF1C8F4F2A000710C1 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2AE1C8F4F2A000710C1 /* Contacts.swift */; };
 24 | 		6DF9C2B21C8F4F45000710C1 /* LocationAlways.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2B11C8F4F45000710C1 /* LocationAlways.swift */; };
 25 | 		6DF9C2B41C8F4F54000710C1 /* LocationWhenInUse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2B31C8F4F54000710C1 /* LocationWhenInUse.swift */; };
 26 | 		6DF9C2B61C8F4F69000710C1 /* Reminders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2B51C8F4F69000710C1 /* Reminders.swift */; };
 27 | 		6DF9C2B81C8F4F8F000710C1 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2B71C8F4F8F000710C1 /* Notifications.swift */; };
 28 | 		6DF9C2BA1C8F4FAC000710C1 /* Microphone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2B91C8F4FAC000710C1 /* Microphone.swift */; };
 29 | 		6DF9C2BC1C8F4FDA000710C1 /* Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2BB1C8F4FDA000710C1 /* Camera.swift */; };
 30 | 		6DF9C2BE1C8F4FE5000710C1 /* Photos.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2BD1C8F4FE5000710C1 /* Photos.swift */; };
 31 | 		6DF9C2C01C8F5003000710C1 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2BF1C8F5003000710C1 /* Events.swift */; };
 32 | 		6DF9C2C61C8F5B4C000710C1 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2C51C8F5B4C000710C1 /* Location.swift */; };
 33 | 		84742782230896D2007A7922 /* PermissionSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84742781230896D2007A7922 /* PermissionSet.swift */; };
 34 | 		8474278423089751007A7922 /* PermissionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8474278323089751007A7922 /* PermissionType.swift */; };
 35 | /* End PBXBuildFile section */
 36 | 
 37 | /* Begin PBXContainerItemProxy section */
 38 | 		6D86A9BE1BEBDC7D00E3DD5A /* PBXContainerItemProxy */ = {
 39 | 			isa = PBXContainerItemProxy;
 40 | 			containerPortal = 6D86A9A91BEBDC7C00E3DD5A /* Project object */;
 41 | 			proxyType = 1;
 42 | 			remoteGlobalIDString = 6D86A9B11BEBDC7C00E3DD5A;
 43 | 			remoteInfo = Sorry;
 44 | 		};
 45 | /* End PBXContainerItemProxy section */
 46 | 
 47 | /* Begin PBXFileReference section */
 48 | 		3D42A7DA1D5F66B300236ABA /* SpeechRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpeechRecognizer.swift; sourceTree = "<group>"; };
 49 | 		3DC217D21D6EFD4A00600DFE /* MediaLibrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaLibrary.swift; sourceTree = "<group>"; };
 50 | 		3F21DC7C1E30B6DB00B3EF65 /* Siri.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Siri.swift; sourceTree = "<group>"; };
 51 | 		6D0EBDBC1BFCF8B700C35F8E /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
 52 | 		6D491E771C9CA90B00611006 /* PermissionStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PermissionStatus.swift; sourceTree = "<group>"; };
 53 | 		6D7D08D41C9DFD9D00746121 /* PermissionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PermissionTests.swift; sourceTree = "<group>"; };
 54 | 		6D86A9B21BEBDC7D00E3DD5A /* Permission.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Permission.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 55 | 		6D86A9B51BEBDC7D00E3DD5A /* Permission.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Permission.h; sourceTree = "<group>"; };
 56 | 		6D86A9B71BEBDC7D00E3DD5A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 57 | 		6D86A9BC1BEBDC7D00E3DD5A /* Permission.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Permission.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 58 | 		6D86A9C31BEBDC7D00E3DD5A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 59 | 		6D86A9CC1BEBDC9000E3DD5A /* Permission.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Permission.swift; sourceTree = "<group>"; };
 60 | 		6D935F5C1C9A0FEA00BB39E3 /* Bluetooth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bluetooth.swift; sourceTree = "<group>"; };
 61 | 		6D935F5E1C9A14AB00BB39E3 /* Motion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Motion.swift; sourceTree = "<group>"; };
 62 | 		6DA8B4AF1BFB80E9007A94FC /* PermissionButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PermissionButton.swift; sourceTree = "<group>"; };
 63 | 		6DA8B4B11BFB8AD8007A94FC /* PermissionAlert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PermissionAlert.swift; sourceTree = "<group>"; };
 64 | 		6DF9C2AE1C8F4F2A000710C1 /* Contacts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = "<group>"; };
 65 | 		6DF9C2B11C8F4F45000710C1 /* LocationAlways.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationAlways.swift; sourceTree = "<group>"; };
 66 | 		6DF9C2B31C8F4F54000710C1 /* LocationWhenInUse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationWhenInUse.swift; sourceTree = "<group>"; };
 67 | 		6DF9C2B51C8F4F69000710C1 /* Reminders.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reminders.swift; sourceTree = "<group>"; };
 68 | 		6DF9C2B71C8F4F8F000710C1 /* Notifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
 69 | 		6DF9C2B91C8F4FAC000710C1 /* Microphone.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Microphone.swift; sourceTree = "<group>"; };
 70 | 		6DF9C2BB1C8F4FDA000710C1 /* Camera.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Camera.swift; sourceTree = "<group>"; };
 71 | 		6DF9C2BD1C8F4FE5000710C1 /* Photos.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Photos.swift; sourceTree = "<group>"; };
 72 | 		6DF9C2BF1C8F5003000710C1 /* Events.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = "<group>"; };
 73 | 		6DF9C2C51C8F5B4C000710C1 /* Location.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
 74 | 		84742781230896D2007A7922 /* PermissionSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PermissionSet.swift; sourceTree = "<group>"; };
 75 | 		8474278323089751007A7922 /* PermissionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PermissionType.swift; sourceTree = "<group>"; };
 76 | 		D08FF2891DC3AD2900F28088 /* PermissionFlags.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = PermissionFlags.xcconfig; sourceTree = "<group>"; };
 77 | /* End PBXFileReference section */
 78 | 
 79 | /* Begin PBXFrameworksBuildPhase section */
 80 | 		6D86A9AE1BEBDC7C00E3DD5A /* Frameworks */ = {
 81 | 			isa = PBXFrameworksBuildPhase;
 82 | 			buildActionMask = 2147483647;
 83 | 			files = (
 84 | 			);
 85 | 			runOnlyForDeploymentPostprocessing = 0;
 86 | 		};
 87 | 		6D86A9B91BEBDC7D00E3DD5A /* Frameworks */ = {
 88 | 			isa = PBXFrameworksBuildPhase;
 89 | 			buildActionMask = 2147483647;
 90 | 			files = (
 91 | 				6D86A9BD1BEBDC7D00E3DD5A /* Permission.framework in Frameworks */,
 92 | 			);
 93 | 			runOnlyForDeploymentPostprocessing = 0;
 94 | 		};
 95 | /* End PBXFrameworksBuildPhase section */
 96 | 
 97 | /* Begin PBXGroup section */
 98 | 		6D86A9A81BEBDC7C00E3DD5A = {
 99 | 			isa = PBXGroup;
100 | 			children = (
101 | 				6D86A9B31BEBDC7D00E3DD5A /* Products */,
102 | 				6D86A9B41BEBDC7D00E3DD5A /* Source */,
103 | 				6D86A9C01BEBDC7D00E3DD5A /* Tests */,
104 | 			);
105 | 			sourceTree = "<group>";
106 | 		};
107 | 		6D86A9B31BEBDC7D00E3DD5A /* Products */ = {
108 | 			isa = PBXGroup;
109 | 			children = (
110 | 				6D86A9B21BEBDC7D00E3DD5A /* Permission.framework */,
111 | 				6D86A9BC1BEBDC7D00E3DD5A /* Permission.xctest */,
112 | 			);
113 | 			name = Products;
114 | 			sourceTree = "<group>";
115 | 		};
116 | 		6D86A9B41BEBDC7D00E3DD5A /* Source */ = {
117 | 			isa = PBXGroup;
118 | 			children = (
119 | 				6DF9C2C21C8F53C3000710C1 /* Supporting Files */,
120 | 				6DF9C2B01C8F4F30000710C1 /* Types */,
121 | 				6DA8B4B11BFB8AD8007A94FC /* PermissionAlert.swift */,
122 | 				6DA8B4AF1BFB80E9007A94FC /* PermissionButton.swift */,
123 | 				6D86A9CC1BEBDC9000E3DD5A /* Permission.swift */,
124 | 				84742781230896D2007A7922 /* PermissionSet.swift */,
125 | 				6D491E771C9CA90B00611006 /* PermissionStatus.swift */,
126 | 				8474278323089751007A7922 /* PermissionType.swift */,
127 | 			);
128 | 			path = Source;
129 | 			sourceTree = "<group>";
130 | 		};
131 | 		6D86A9C01BEBDC7D00E3DD5A /* Tests */ = {
132 | 			isa = PBXGroup;
133 | 			children = (
134 | 				6DF9C2C11C8F53B7000710C1 /* Supporting Files */,
135 | 				6D7D08D41C9DFD9D00746121 /* PermissionTests.swift */,
136 | 			);
137 | 			path = Tests;
138 | 			sourceTree = "<group>";
139 | 		};
140 | 		6DF9C2B01C8F4F30000710C1 /* Types */ = {
141 | 			isa = PBXGroup;
142 | 			children = (
143 | 				6D935F5C1C9A0FEA00BB39E3 /* Bluetooth.swift */,
144 | 				6DF9C2BB1C8F4FDA000710C1 /* Camera.swift */,
145 | 				6DF9C2AE1C8F4F2A000710C1 /* Contacts.swift */,
146 | 				6DF9C2BF1C8F5003000710C1 /* Events.swift */,
147 | 				6DF9C2C51C8F5B4C000710C1 /* Location.swift */,
148 | 				6DF9C2B11C8F4F45000710C1 /* LocationAlways.swift */,
149 | 				6DF9C2B31C8F4F54000710C1 /* LocationWhenInUse.swift */,
150 | 				3DC217D21D6EFD4A00600DFE /* MediaLibrary.swift */,
151 | 				6DF9C2B91C8F4FAC000710C1 /* Microphone.swift */,
152 | 				6D935F5E1C9A14AB00BB39E3 /* Motion.swift */,
153 | 				6DF9C2B71C8F4F8F000710C1 /* Notifications.swift */,
154 | 				6DF9C2BD1C8F4FE5000710C1 /* Photos.swift */,
155 | 				6DF9C2B51C8F4F69000710C1 /* Reminders.swift */,
156 | 				3F21DC7C1E30B6DB00B3EF65 /* Siri.swift */,
157 | 				3D42A7DA1D5F66B300236ABA /* SpeechRecognizer.swift */,
158 | 			);
159 | 			path = Types;
160 | 			sourceTree = "<group>";
161 | 		};
162 | 		6DF9C2C11C8F53B7000710C1 /* Supporting Files */ = {
163 | 			isa = PBXGroup;
164 | 			children = (
165 | 				6D86A9C31BEBDC7D00E3DD5A /* Info.plist */,
166 | 			);
167 | 			path = "Supporting Files";
168 | 			sourceTree = "<group>";
169 | 		};
170 | 		6DF9C2C21C8F53C3000710C1 /* Supporting Files */ = {
171 | 			isa = PBXGroup;
172 | 			children = (
173 | 				6D86A9B71BEBDC7D00E3DD5A /* Info.plist */,
174 | 				6D86A9B51BEBDC7D00E3DD5A /* Permission.h */,
175 | 				D08FF2891DC3AD2900F28088 /* PermissionFlags.xcconfig */,
176 | 				6D0EBDBC1BFCF8B700C35F8E /* Utilities.swift */,
177 | 			);
178 | 			path = "Supporting Files";
179 | 			sourceTree = "<group>";
180 | 		};
181 | /* End PBXGroup section */
182 | 
183 | /* Begin PBXHeadersBuildPhase section */
184 | 		6D86A9AF1BEBDC7C00E3DD5A /* Headers */ = {
185 | 			isa = PBXHeadersBuildPhase;
186 | 			buildActionMask = 2147483647;
187 | 			files = (
188 | 				6D86A9B61BEBDC7D00E3DD5A /* Permission.h in Headers */,
189 | 			);
190 | 			runOnlyForDeploymentPostprocessing = 0;
191 | 		};
192 | /* End PBXHeadersBuildPhase section */
193 | 
194 | /* Begin PBXNativeTarget section */
195 | 		6D86A9B11BEBDC7C00E3DD5A /* Permission */ = {
196 | 			isa = PBXNativeTarget;
197 | 			buildConfigurationList = 6D86A9C61BEBDC7D00E3DD5A /* Build configuration list for PBXNativeTarget "Permission" */;
198 | 			buildPhases = (
199 | 				6D86A9AD1BEBDC7C00E3DD5A /* Sources */,
200 | 				6D86A9AE1BEBDC7C00E3DD5A /* Frameworks */,
201 | 				6D86A9AF1BEBDC7C00E3DD5A /* Headers */,
202 | 				6D86A9B01BEBDC7C00E3DD5A /* Resources */,
203 | 			);
204 | 			buildRules = (
205 | 			);
206 | 			dependencies = (
207 | 			);
208 | 			name = Permission;
209 | 			productName = Sorry;
210 | 			productReference = 6D86A9B21BEBDC7D00E3DD5A /* Permission.framework */;
211 | 			productType = "com.apple.product-type.framework";
212 | 		};
213 | 		6D86A9BB1BEBDC7D00E3DD5A /* PermissionTests */ = {
214 | 			isa = PBXNativeTarget;
215 | 			buildConfigurationList = 6D86A9C91BEBDC7D00E3DD5A /* Build configuration list for PBXNativeTarget "PermissionTests" */;
216 | 			buildPhases = (
217 | 				6D86A9B81BEBDC7D00E3DD5A /* Sources */,
218 | 				6D86A9B91BEBDC7D00E3DD5A /* Frameworks */,
219 | 				6D86A9BA1BEBDC7D00E3DD5A /* Resources */,
220 | 			);
221 | 			buildRules = (
222 | 			);
223 | 			dependencies = (
224 | 				6D86A9BF1BEBDC7D00E3DD5A /* PBXTargetDependency */,
225 | 			);
226 | 			name = PermissionTests;
227 | 			productName = SorryTests;
228 | 			productReference = 6D86A9BC1BEBDC7D00E3DD5A /* Permission.xctest */;
229 | 			productType = "com.apple.product-type.bundle.unit-test";
230 | 		};
231 | /* End PBXNativeTarget section */
232 | 
233 | /* Begin PBXProject section */
234 | 		6D86A9A91BEBDC7C00E3DD5A /* Project object */ = {
235 | 			isa = PBXProject;
236 | 			attributes = {
237 | 				LastSwiftUpdateCheck = 0710;
238 | 				LastUpgradeCheck = 1030;
239 | 				ORGANIZATIONNAME = delba;
240 | 				TargetAttributes = {
241 | 					6D86A9B11BEBDC7C00E3DD5A = {
242 | 						CreatedOnToolsVersion = 7.1;
243 | 						LastSwiftMigration = 0800;
244 | 					};
245 | 					6D86A9BB1BEBDC7D00E3DD5A = {
246 | 						CreatedOnToolsVersion = 7.1;
247 | 						LastSwiftMigration = 0800;
248 | 					};
249 | 				};
250 | 			};
251 | 			buildConfigurationList = 6D86A9AC1BEBDC7C00E3DD5A /* Build configuration list for PBXProject "Permission" */;
252 | 			compatibilityVersion = "Xcode 3.2";
253 | 			developmentRegion = en;
254 | 			hasScannedForEncodings = 0;
255 | 			knownRegions = (
256 | 				en,
257 | 				Base,
258 | 			);
259 | 			mainGroup = 6D86A9A81BEBDC7C00E3DD5A;
260 | 			productRefGroup = 6D86A9B31BEBDC7D00E3DD5A /* Products */;
261 | 			projectDirPath = "";
262 | 			projectRoot = "";
263 | 			targets = (
264 | 				6D86A9B11BEBDC7C00E3DD5A /* Permission */,
265 | 				6D86A9BB1BEBDC7D00E3DD5A /* PermissionTests */,
266 | 			);
267 | 		};
268 | /* End PBXProject section */
269 | 
270 | /* Begin PBXResourcesBuildPhase section */
271 | 		6D86A9B01BEBDC7C00E3DD5A /* Resources */ = {
272 | 			isa = PBXResourcesBuildPhase;
273 | 			buildActionMask = 2147483647;
274 | 			files = (
275 | 			);
276 | 			runOnlyForDeploymentPostprocessing = 0;
277 | 		};
278 | 		6D86A9BA1BEBDC7D00E3DD5A /* Resources */ = {
279 | 			isa = PBXResourcesBuildPhase;
280 | 			buildActionMask = 2147483647;
281 | 			files = (
282 | 			);
283 | 			runOnlyForDeploymentPostprocessing = 0;
284 | 		};
285 | /* End PBXResourcesBuildPhase section */
286 | 
287 | /* Begin PBXSourcesBuildPhase section */
288 | 		6D86A9AD1BEBDC7C00E3DD5A /* Sources */ = {
289 | 			isa = PBXSourcesBuildPhase;
290 | 			buildActionMask = 2147483647;
291 | 			files = (
292 | 				6D491E781C9CA90B00611006 /* PermissionStatus.swift in Sources */,
293 | 				6D935F5F1C9A14AB00BB39E3 /* Motion.swift in Sources */,
294 | 				6D935F5D1C9A0FEA00BB39E3 /* Bluetooth.swift in Sources */,
295 | 				6DF9C2B81C8F4F8F000710C1 /* Notifications.swift in Sources */,
296 | 				6DF9C2B41C8F4F54000710C1 /* LocationWhenInUse.swift in Sources */,
297 | 				6DA8B4B01BFB80E9007A94FC /* PermissionButton.swift in Sources */,
298 | 				6DF9C2C01C8F5003000710C1 /* Events.swift in Sources */,
299 | 				6DF9C2B61C8F4F69000710C1 /* Reminders.swift in Sources */,
300 | 				6DF9C2AF1C8F4F2A000710C1 /* Contacts.swift in Sources */,
301 | 				8474278423089751007A7922 /* PermissionType.swift in Sources */,
302 | 				6DF9C2C61C8F5B4C000710C1 /* Location.swift in Sources */,
303 | 				3F21DC7E1E30E0B900B3EF65 /* Siri.swift in Sources */,
304 | 				6DA8B4B21BFB8AD8007A94FC /* PermissionAlert.swift in Sources */,
305 | 				3D42A7DB1D5F66B300236ABA /* SpeechRecognizer.swift in Sources */,
306 | 				6DF9C2BE1C8F4FE5000710C1 /* Photos.swift in Sources */,
307 | 				6DF9C2BA1C8F4FAC000710C1 /* Microphone.swift in Sources */,
308 | 				6DF9C2BC1C8F4FDA000710C1 /* Camera.swift in Sources */,
309 | 				84742782230896D2007A7922 /* PermissionSet.swift in Sources */,
310 | 				6DF9C2B21C8F4F45000710C1 /* LocationAlways.swift in Sources */,
311 | 				3DC217D31D6EFD4A00600DFE /* MediaLibrary.swift in Sources */,
312 | 				6D86A9CD1BEBDC9000E3DD5A /* Permission.swift in Sources */,
313 | 				6D0EBDBD1BFCF8B700C35F8E /* Utilities.swift in Sources */,
314 | 			);
315 | 			runOnlyForDeploymentPostprocessing = 0;
316 | 		};
317 | 		6D86A9B81BEBDC7D00E3DD5A /* Sources */ = {
318 | 			isa = PBXSourcesBuildPhase;
319 | 			buildActionMask = 2147483647;
320 | 			files = (
321 | 				6D7D08D51C9DFD9D00746121 /* PermissionTests.swift in Sources */,
322 | 			);
323 | 			runOnlyForDeploymentPostprocessing = 0;
324 | 		};
325 | /* End PBXSourcesBuildPhase section */
326 | 
327 | /* Begin PBXTargetDependency section */
328 | 		6D86A9BF1BEBDC7D00E3DD5A /* PBXTargetDependency */ = {
329 | 			isa = PBXTargetDependency;
330 | 			target = 6D86A9B11BEBDC7C00E3DD5A /* Permission */;
331 | 			targetProxy = 6D86A9BE1BEBDC7D00E3DD5A /* PBXContainerItemProxy */;
332 | 		};
333 | /* End PBXTargetDependency section */
334 | 
335 | /* Begin XCBuildConfiguration section */
336 | 		6D86A9C41BEBDC7D00E3DD5A /* Debug */ = {
337 | 			isa = XCBuildConfiguration;
338 | 			baseConfigurationReference = D08FF2891DC3AD2900F28088 /* PermissionFlags.xcconfig */;
339 | 			buildSettings = {
340 | 				ALWAYS_SEARCH_USER_PATHS = NO;
341 | 				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
342 | 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
343 | 				CLANG_CXX_LIBRARY = "libc++";
344 | 				CLANG_ENABLE_MODULES = YES;
345 | 				CLANG_ENABLE_OBJC_ARC = YES;
346 | 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
347 | 				CLANG_WARN_BOOL_CONVERSION = YES;
348 | 				CLANG_WARN_COMMA = YES;
349 | 				CLANG_WARN_CONSTANT_CONVERSION = YES;
350 | 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
351 | 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
352 | 				CLANG_WARN_EMPTY_BODY = YES;
353 | 				CLANG_WARN_ENUM_CONVERSION = YES;
354 | 				CLANG_WARN_INFINITE_RECURSION = YES;
355 | 				CLANG_WARN_INT_CONVERSION = YES;
356 | 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
357 | 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
358 | 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
359 | 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
360 | 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
361 | 				CLANG_WARN_STRICT_PROTOTYPES = YES;
362 | 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
363 | 				CLANG_WARN_UNREACHABLE_CODE = YES;
364 | 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
365 | 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
366 | 				COPY_PHASE_STRIP = NO;
367 | 				CURRENT_PROJECT_VERSION = 1;
368 | 				DEBUG_INFORMATION_FORMAT = dwarf;
369 | 				ENABLE_STRICT_OBJC_MSGSEND = YES;
370 | 				ENABLE_TESTABILITY = YES;
371 | 				GCC_C_LANGUAGE_STANDARD = gnu99;
372 | 				GCC_DYNAMIC_NO_PIC = NO;
373 | 				GCC_NO_COMMON_BLOCKS = YES;
374 | 				GCC_OPTIMIZATION_LEVEL = 0;
375 | 				GCC_PREPROCESSOR_DEFINITIONS = (
376 | 					"DEBUG=1",
377 | 					"$(inherited)",
378 | 				);
379 | 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
380 | 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
381 | 				GCC_WARN_UNDECLARED_SELECTOR = YES;
382 | 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
383 | 				GCC_WARN_UNUSED_FUNCTION = YES;
384 | 				GCC_WARN_UNUSED_VARIABLE = YES;
385 | 				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
386 | 				MTL_ENABLE_DEBUG_INFO = YES;
387 | 				ONLY_ACTIVE_ARCH = YES;
388 | 				SDKROOT = iphoneos;
389 | 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(INHERITED)";
390 | 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
391 | 				SWIFT_VERSION = 5.0;
392 | 				TARGETED_DEVICE_FAMILY = "1,2";
393 | 				VERSIONING_SYSTEM = "apple-generic";
394 | 				VERSION_INFO_PREFIX = "";
395 | 			};
396 | 			name = Debug;
397 | 		};
398 | 		6D86A9C51BEBDC7D00E3DD5A /* Release */ = {
399 | 			isa = XCBuildConfiguration;
400 | 			baseConfigurationReference = D08FF2891DC3AD2900F28088 /* PermissionFlags.xcconfig */;
401 | 			buildSettings = {
402 | 				ALWAYS_SEARCH_USER_PATHS = NO;
403 | 				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
404 | 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
405 | 				CLANG_CXX_LIBRARY = "libc++";
406 | 				CLANG_ENABLE_MODULES = YES;
407 | 				CLANG_ENABLE_OBJC_ARC = YES;
408 | 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
409 | 				CLANG_WARN_BOOL_CONVERSION = YES;
410 | 				CLANG_WARN_COMMA = YES;
411 | 				CLANG_WARN_CONSTANT_CONVERSION = YES;
412 | 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
413 | 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
414 | 				CLANG_WARN_EMPTY_BODY = YES;
415 | 				CLANG_WARN_ENUM_CONVERSION = YES;
416 | 				CLANG_WARN_INFINITE_RECURSION = YES;
417 | 				CLANG_WARN_INT_CONVERSION = YES;
418 | 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
419 | 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
420 | 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
421 | 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
422 | 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
423 | 				CLANG_WARN_STRICT_PROTOTYPES = YES;
424 | 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
425 | 				CLANG_WARN_UNREACHABLE_CODE = YES;
426 | 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
427 | 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
428 | 				COPY_PHASE_STRIP = NO;
429 | 				CURRENT_PROJECT_VERSION = 1;
430 | 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
431 | 				ENABLE_NS_ASSERTIONS = NO;
432 | 				ENABLE_STRICT_OBJC_MSGSEND = YES;
433 | 				GCC_C_LANGUAGE_STANDARD = gnu99;
434 | 				GCC_NO_COMMON_BLOCKS = YES;
435 | 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
436 | 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
437 | 				GCC_WARN_UNDECLARED_SELECTOR = YES;
438 | 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
439 | 				GCC_WARN_UNUSED_FUNCTION = YES;
440 | 				GCC_WARN_UNUSED_VARIABLE = YES;
441 | 				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
442 | 				MTL_ENABLE_DEBUG_INFO = NO;
443 | 				SDKROOT = iphoneos;
444 | 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(INHERITED)";
445 | 				SWIFT_VERSION = 5.0;
446 | 				TARGETED_DEVICE_FAMILY = "1,2";
447 | 				VALIDATE_PRODUCT = YES;
448 | 				VERSIONING_SYSTEM = "apple-generic";
449 | 				VERSION_INFO_PREFIX = "";
450 | 			};
451 | 			name = Release;
452 | 		};
453 | 		6D86A9C71BEBDC7D00E3DD5A /* Debug */ = {
454 | 			isa = XCBuildConfiguration;
455 | 			baseConfigurationReference = D08FF2891DC3AD2900F28088 /* PermissionFlags.xcconfig */;
456 | 			buildSettings = {
457 | 				CLANG_ENABLE_MODULES = YES;
458 | 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
459 | 				DEFINES_MODULE = YES;
460 | 				DYLIB_COMPATIBILITY_VERSION = 1;
461 | 				DYLIB_CURRENT_VERSION = 1;
462 | 				DYLIB_INSTALL_NAME_BASE = "@rpath";
463 | 				GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
464 | 				INFOPLIST_FILE = "Source/Supporting Files/Info.plist";
465 | 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
466 | 				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
467 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
468 | 				PRODUCT_BUNDLE_IDENTIFIER = io.delba.Permission;
469 | 				PRODUCT_NAME = Permission;
470 | 				SKIP_INSTALL = YES;
471 | 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
472 | 				SWIFT_VERSION = 5.0;
473 | 			};
474 | 			name = Debug;
475 | 		};
476 | 		6D86A9C81BEBDC7D00E3DD5A /* Release */ = {
477 | 			isa = XCBuildConfiguration;
478 | 			baseConfigurationReference = D08FF2891DC3AD2900F28088 /* PermissionFlags.xcconfig */;
479 | 			buildSettings = {
480 | 				CLANG_ENABLE_MODULES = YES;
481 | 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
482 | 				DEFINES_MODULE = YES;
483 | 				DYLIB_COMPATIBILITY_VERSION = 1;
484 | 				DYLIB_CURRENT_VERSION = 1;
485 | 				DYLIB_INSTALL_NAME_BASE = "@rpath";
486 | 				INFOPLIST_FILE = "Source/Supporting Files/Info.plist";
487 | 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
488 | 				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
489 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
490 | 				PRODUCT_BUNDLE_IDENTIFIER = io.delba.Permission;
491 | 				PRODUCT_NAME = Permission;
492 | 				SKIP_INSTALL = YES;
493 | 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
494 | 				SWIFT_VERSION = 5.0;
495 | 			};
496 | 			name = Release;
497 | 		};
498 | 		6D86A9CA1BEBDC7D00E3DD5A /* Debug */ = {
499 | 			isa = XCBuildConfiguration;
500 | 			baseConfigurationReference = D08FF2891DC3AD2900F28088 /* PermissionFlags.xcconfig */;
501 | 			buildSettings = {
502 | 				INFOPLIST_FILE = "Tests/Supporting Files/Info.plist";
503 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
504 | 				PRODUCT_BUNDLE_IDENTIFIER = io.delba.PermissionTests;
505 | 				PRODUCT_NAME = Permission;
506 | 				SWIFT_VERSION = 5.0;
507 | 			};
508 | 			name = Debug;
509 | 		};
510 | 		6D86A9CB1BEBDC7D00E3DD5A /* Release */ = {
511 | 			isa = XCBuildConfiguration;
512 | 			baseConfigurationReference = D08FF2891DC3AD2900F28088 /* PermissionFlags.xcconfig */;
513 | 			buildSettings = {
514 | 				INFOPLIST_FILE = "Tests/Supporting Files/Info.plist";
515 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
516 | 				PRODUCT_BUNDLE_IDENTIFIER = io.delba.PermissionTests;
517 | 				PRODUCT_NAME = Permission;
518 | 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
519 | 				SWIFT_VERSION = 5.0;
520 | 			};
521 | 			name = Release;
522 | 		};
523 | /* End XCBuildConfiguration section */
524 | 
525 | /* Begin XCConfigurationList section */
526 | 		6D86A9AC1BEBDC7C00E3DD5A /* Build configuration list for PBXProject "Permission" */ = {
527 | 			isa = XCConfigurationList;
528 | 			buildConfigurations = (
529 | 				6D86A9C41BEBDC7D00E3DD5A /* Debug */,
530 | 				6D86A9C51BEBDC7D00E3DD5A /* Release */,
531 | 			);
532 | 			defaultConfigurationIsVisible = 0;
533 | 			defaultConfigurationName = Release;
534 | 		};
535 | 		6D86A9C61BEBDC7D00E3DD5A /* Build configuration list for PBXNativeTarget "Permission" */ = {
536 | 			isa = XCConfigurationList;
537 | 			buildConfigurations = (
538 | 				6D86A9C71BEBDC7D00E3DD5A /* Debug */,
539 | 				6D86A9C81BEBDC7D00E3DD5A /* Release */,
540 | 			);
541 | 			defaultConfigurationIsVisible = 0;
542 | 			defaultConfigurationName = Release;
543 | 		};
544 | 		6D86A9C91BEBDC7D00E3DD5A /* Build configuration list for PBXNativeTarget "PermissionTests" */ = {
545 | 			isa = XCConfigurationList;
546 | 			buildConfigurations = (
547 | 				6D86A9CA1BEBDC7D00E3DD5A /* Debug */,
548 | 				6D86A9CB1BEBDC7D00E3DD5A /* Release */,
549 | 			);
550 | 			defaultConfigurationIsVisible = 0;
551 | 			defaultConfigurationName = Release;
552 | 		};
553 | /* End XCConfigurationList section */
554 | 	};
555 | 	rootObject = 6D86A9A91BEBDC7C00E3DD5A /* Project object */;
556 | }
557 | 


--------------------------------------------------------------------------------
/Permission.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <Workspace
3 |    version = "1.0">
4 |    <FileRef
5 |       location = "self:/Users/damien/Code/Sorry/Permission.xcodeproj">
6 |    </FileRef>
7 | </Workspace>
8 | 


--------------------------------------------------------------------------------
/Permission.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 | <plist version="1.0">
4 | <dict>
5 | 	<key>IDEDidComputeMac32BitWarning</key>
6 | 	<true/>
7 | </dict>
8 | </plist>
9 | 


--------------------------------------------------------------------------------
/Permission.xcodeproj/xcshareddata/xcschemes/Permission.xcscheme:
--------------------------------------------------------------------------------
  1 | <?xml version="1.0" encoding="UTF-8"?>
  2 | <Scheme
  3 |    LastUpgradeVersion = "1030"
  4 |    version = "1.3">
  5 |    <BuildAction
  6 |       parallelizeBuildables = "YES"
  7 |       buildImplicitDependencies = "YES">
  8 |       <BuildActionEntries>
  9 |          <BuildActionEntry
 10 |             buildForTesting = "YES"
 11 |             buildForRunning = "YES"
 12 |             buildForProfiling = "YES"
 13 |             buildForArchiving = "YES"
 14 |             buildForAnalyzing = "YES">
 15 |             <BuildableReference
 16 |                BuildableIdentifier = "primary"
 17 |                BlueprintIdentifier = "6D86A9B11BEBDC7C00E3DD5A"
 18 |                BuildableName = "Permission.framework"
 19 |                BlueprintName = "Permission"
 20 |                ReferencedContainer = "container:Permission.xcodeproj">
 21 |             </BuildableReference>
 22 |          </BuildActionEntry>
 23 |       </BuildActionEntries>
 24 |    </BuildAction>
 25 |    <TestAction
 26 |       buildConfiguration = "Debug"
 27 |       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
 28 |       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
 29 |       shouldUseLaunchSchemeArgsEnv = "YES">
 30 |       <Testables>
 31 |          <TestableReference
 32 |             skipped = "NO">
 33 |             <BuildableReference
 34 |                BuildableIdentifier = "primary"
 35 |                BlueprintIdentifier = "6D86A9BB1BEBDC7D00E3DD5A"
 36 |                BuildableName = "Permission.xctest"
 37 |                BlueprintName = "PermissionTests"
 38 |                ReferencedContainer = "container:Permission.xcodeproj">
 39 |             </BuildableReference>
 40 |          </TestableReference>
 41 |       </Testables>
 42 |       <MacroExpansion>
 43 |          <BuildableReference
 44 |             BuildableIdentifier = "primary"
 45 |             BlueprintIdentifier = "6D86A9B11BEBDC7C00E3DD5A"
 46 |             BuildableName = "Permission.framework"
 47 |             BlueprintName = "Permission"
 48 |             ReferencedContainer = "container:Permission.xcodeproj">
 49 |          </BuildableReference>
 50 |       </MacroExpansion>
 51 |       <AdditionalOptions>
 52 |       </AdditionalOptions>
 53 |    </TestAction>
 54 |    <LaunchAction
 55 |       buildConfiguration = "Debug"
 56 |       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
 57 |       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
 58 |       launchStyle = "0"
 59 |       useCustomWorkingDirectory = "NO"
 60 |       ignoresPersistentStateOnLaunch = "NO"
 61 |       debugDocumentVersioning = "YES"
 62 |       debugServiceExtension = "internal"
 63 |       allowLocationSimulation = "YES">
 64 |       <MacroExpansion>
 65 |          <BuildableReference
 66 |             BuildableIdentifier = "primary"
 67 |             BlueprintIdentifier = "6D86A9B11BEBDC7C00E3DD5A"
 68 |             BuildableName = "Permission.framework"
 69 |             BlueprintName = "Permission"
 70 |             ReferencedContainer = "container:Permission.xcodeproj">
 71 |          </BuildableReference>
 72 |       </MacroExpansion>
 73 |       <AdditionalOptions>
 74 |       </AdditionalOptions>
 75 |    </LaunchAction>
 76 |    <ProfileAction
 77 |       buildConfiguration = "Release"
 78 |       shouldUseLaunchSchemeArgsEnv = "YES"
 79 |       savedToolIdentifier = ""
 80 |       useCustomWorkingDirectory = "NO"
 81 |       debugDocumentVersioning = "YES">
 82 |       <MacroExpansion>
 83 |          <BuildableReference
 84 |             BuildableIdentifier = "primary"
 85 |             BlueprintIdentifier = "6D86A9B11BEBDC7C00E3DD5A"
 86 |             BuildableName = "Permission.framework"
 87 |             BlueprintName = "Permission"
 88 |             ReferencedContainer = "container:Permission.xcodeproj">
 89 |          </BuildableReference>
 90 |       </MacroExpansion>
 91 |    </ProfileAction>
 92 |    <AnalyzeAction
 93 |       buildConfiguration = "Debug">
 94 |    </AnalyzeAction>
 95 |    <ArchiveAction
 96 |       buildConfiguration = "Release"
 97 |       revealArchiveInOrganizer = "YES">
 98 |    </ArchiveAction>
 99 | </Scheme>
100 | 


--------------------------------------------------------------------------------
/Permission.xcodeproj/xcshareddata/xcschemes/PermissionTests.xcscheme:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <Scheme
 3 |    LastUpgradeVersion = "1030"
 4 |    version = "1.3">
 5 |    <BuildAction
 6 |       parallelizeBuildables = "YES"
 7 |       buildImplicitDependencies = "YES">
 8 |    </BuildAction>
 9 |    <TestAction
10 |       buildConfiguration = "Debug"
11 |       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
12 |       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
13 |       shouldUseLaunchSchemeArgsEnv = "YES">
14 |       <Testables>
15 |          <TestableReference
16 |             skipped = "NO">
17 |             <BuildableReference
18 |                BuildableIdentifier = "primary"
19 |                BlueprintIdentifier = "6D86A9BB1BEBDC7D00E3DD5A"
20 |                BuildableName = "Permission.xctest"
21 |                BlueprintName = "PermissionTests"
22 |                ReferencedContainer = "container:Permission.xcodeproj">
23 |             </BuildableReference>
24 |          </TestableReference>
25 |       </Testables>
26 |       <AdditionalOptions>
27 |       </AdditionalOptions>
28 |    </TestAction>
29 |    <LaunchAction
30 |       buildConfiguration = "Debug"
31 |       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
32 |       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
33 |       launchStyle = "0"
34 |       useCustomWorkingDirectory = "NO"
35 |       ignoresPersistentStateOnLaunch = "NO"
36 |       debugDocumentVersioning = "YES"
37 |       debugServiceExtension = "internal"
38 |       allowLocationSimulation = "YES">
39 |       <AdditionalOptions>
40 |       </AdditionalOptions>
41 |    </LaunchAction>
42 |    <ProfileAction
43 |       buildConfiguration = "Release"
44 |       shouldUseLaunchSchemeArgsEnv = "YES"
45 |       savedToolIdentifier = ""
46 |       useCustomWorkingDirectory = "NO"
47 |       debugDocumentVersioning = "YES">
48 |    </ProfileAction>
49 |    <AnalyzeAction
50 |       buildConfiguration = "Debug">
51 |    </AnalyzeAction>
52 |    <ArchiveAction
53 |       buildConfiguration = "Release"
54 |       revealArchiveInOrganizer = "YES">
55 |    </ArchiveAction>
56 | </Scheme>
57 | 


--------------------------------------------------------------------------------
/PermissionConfiguration.xcconfig:
--------------------------------------------------------------------------------
 1 | //
 2 | // PermissionConfiguration.swift
 3 | //
 4 | // Copyright (c) 2015-2016 Justin Makaila (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | PERMISSION_ADDRESS_BOOK      = PERMISSION_ADDRESS_BOOK
26 | PERMISSION_BLUETOOTH         = PERMISSION_BLUETOOTH
27 | PERMISSION_CAMERA            = PERMISSION_CAMERA
28 | PERMISSION_CONTACTS          = PERMISSION_CONTACTS
29 | PERMISSION_EVENTS            = PERMISSION_EVENTS
30 | PERMISSION_LOCATION          = PERMISSION_LOCATION
31 | PERMISSION_MICROPHONE        = PERMISSION_MICROPHONE
32 | PERMISSION_MOTION            = PERMISSION_MOTION
33 | PERMISSION_NOTIFICATIONS     = PERMISSION_NOTIFICATIONS
34 | PERMISSION_PHOTOS            = PERMISSION_PHOTOS
35 | PERMISSION_REMINDERS         = PERMISSION_REMINDERS
36 | PERMISSION_SPEECH_RECOGNIZER = PERMISSION_SPEECH_RECOGNIZER
37 | PERMISSION_MEDIA_LIBRARY     = PERMISSION_MEDIA_LIBRARY
38 | PERMISSION_SIRI              = PERMISSION_SIRI
39 | 
40 | // Do not modify this line. Instead, remove comments above as needed to enable the categories your app uses.
41 | PERMISSION_FLAGS = $(PERMISSION_ADDRESS_BOOK) $(PERMISSION_BLUETOOTH) $(PERMISSION_CAMERA) $(PERMISSION_CONTACTS) $(PERMISSION_EVENTS) $(PERMISSION_LOCATION) $(PERMISSION_MICROPHONE) $(PERMISSION_MOTION) $(PERMISSION_NOTIFICATIONS) $(PERMISSION_PHOTOS) $(PERMISSION_REMINDERS) $(PERMISSION_SPEECH_RECOGNIZER) $(PERMISSION_MEDIA_LIBRARY) $(PERMISSION_SIRI)
42 | 
43 | SWIFT_ACTIVE_COMPILATION_CONDITIONS= $(inherited) $(PERMISSION_FLAGS)
44 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | <p align="center">
  2 |   <img src="https://github.com/delba/Permission/raw/assets/Permission@2x.png">
  3 | </p>
  4 | 
  5 | <p align="center">
  6 |   <a href="https://travis-ci.org/delba/Permission"><img alt="Travis Status" src="https://img.shields.io/travis/delba/Permission.svg"/></a>
  7 |   <a href="https://img.shields.io/cocoapods/v/Permission.svg"><img alt="CocoaPods compatible" src="https://img.shields.io/cocoapods/v/Permission.svg"/></a>
  8 |   <a href="https://github.com/Carthage/Carthage"><img alt="Carthage compatible" src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat"/></a>
  9 | </p>
 10 | 
 11 | **Permission** exposes a unified API to request permissions on iOS.
 12 | 
 13 | <p align="center">
 14 |     <a href="#usage">Usage</a> • <a href="#example">Example</a> • <a href="#installation">Installation</a> • <a href="#license">License</a>
 15 | </p>
 16 | 
 17 | ## Usage
 18 | 
 19 | #### Permission
 20 | 
 21 | > [`Permission.swift`](https://github.com/delba/Permission/blob/master/Source/Permission.swift)
 22 | > [`PermissionStatus.swift`](https://github.com/delba/Permission/blob/master/Source/PermissionStatus.swift)
 23 | 
 24 | ```swift
 25 | let permission: Permission = .contacts
 26 | 
 27 | print(permission.status) // .notDetermined
 28 | 
 29 | permission.request { status in
 30 |     switch status {
 31 |     case .authorized:    print("authorized")
 32 |     case .denied:        print("denied")
 33 |     case .disabled:      print("disabled")
 34 |     case .notDetermined: print("not determined")
 35 |     }
 36 | }
 37 | ```
 38 | 
 39 | ##### Supported Permissions
 40 | 
 41 | > [`PermissionType.swift`](https://github.com/delba/Permission/blob/master/Source/PermissionType.swift)
 42 | > [`Types/`](https://github.com/delba/Permission/tree/master/Source/Types)
 43 | 
 44 | - [`Bluetooth`](https://github.com/delba/Permission/blob/master/Source/Types/Bluetooth.swift)
 45 | - [`Camera`](https://github.com/delba/Permission/blob/master/Source/Types/Camera.swift)
 46 | - [`Contacts`](https://github.com/delba/Permission/blob/master/Source/Types/Contacts.swift)
 47 | - [`Events`](https://github.com/delba/Permission/blob/master/Source/Types/Events.swift)
 48 | - [`Motion`](https://github.com/delba/Permission/blob/master/Source/Types/Motion.swift)
 49 | - [`Microphone`](https://github.com/delba/Permission/blob/master/Source/Types/Microphone.swift)
 50 | - [`Notifications`](https://github.com/delba/Permission/blob/master/Source/Types/Notifications.swift)
 51 | - [`Photos`](https://github.com/delba/Permission/blob/master/Source/Types/Photos.swift)
 52 | - [`Reminders`](https://github.com/delba/Permission/blob/master/Source/Types/Reminders.swift)
 53 | - [`LocationAlways`](https://github.com/delba/Permission/blob/master/Source/Types/LocationAlways.swift)
 54 | - [`LocationWhenInUse`](https://github.com/delba/Permission/blob/master/Source/Types/LocationWhenInUse.swift)
 55 | - [`MediaLibrary`](https://github.com/delba/Permission/blob/master/Source/Types/MediaLibrary.swift)
 56 | - [`SpeechRecognizer`](https://github.com/delba/Permission/blob/master/Source/Types/SpeechRecognizer.swift)
 57 | - [`Siri`](https://github.com/delba/Permission/blob/master/Source/Types/Siri.swift)
 58 | 
 59 | #### PermissionAlert
 60 | 
 61 | > [`PermissionAlert.swift`](https://github.com/delba/Permission/blob/master/Source/PermissionAlert.swift)
 62 | 
 63 | ##### Denied and disabled alerts
 64 | 
 65 | When you first request a permission, a system alert is presented to the user.
 66 | If you request a permission that was denied/disabled, a `PermissionAlert` will be presented.
 67 | You might want to change the default `title`, `message`, `cancel` and `settings` text:
 68 | 
 69 | ```swift
 70 | let alert = permission.deniedAlert // or permission.disabledAlert
 71 | 
 72 | alert.title    = "Please allow access to your contacts"
 73 | alert.message  = nil
 74 | alert.cancel   = "Cancel"
 75 | alert.settings = "Settings"
 76 | ```
 77 | 
 78 | Set `permission.presentDeniedAlert = false` or `permission.presentDisabledAlert = false` if you don't want to present these alerts.
 79 | 
 80 | ##### Pre-permission alerts
 81 | 
 82 | In order not to burn your only chance of displaying the system alert, you can present a **pre-permission alert**. See this [article](http://techcrunch.com/2014/04/04/the-right-way-to-ask-users-for-ios-permissions/) for more informations.
 83 | 
 84 | ```swift
 85 | permission.presentPrePermissionAlert = true
 86 | 
 87 | let alert = permission.prePermissionAlert
 88 | 
 89 | alert.title   = "Let Foo Access Photos?"
 90 | alert.message = "This lets you choose which photos you want to add to your Foo profile"
 91 | alert.cancel  = "Not now"
 92 | alert.confirm = "Give Access"
 93 | ```
 94 | 
 95 | The system alert will only be presented if the user taps "Give Access".
 96 | 
 97 | #### PermissionSet
 98 | 
 99 | > [`PermissionSet.swift`](https://github.com/delba/Permission/blob/master/Source/PermissionSet.swift)
100 | 
101 | Use a `PermissionSet` to check the status of a group of `Permission` and to react when a permission is requested.
102 | 
103 | ```swift
104 | let permissionSet = PermissionSet(.contacts, .camera, .microphone, .photos)
105 | permissionSet.delegate = self
106 | 
107 | print(permissionSet.status) // .notDetermined
108 | 
109 | // ...
110 | 
111 | func permissionSet(permissionSet: PermissionSet, willRequestPermission permission: Permission) {
112 |     print("Will request \(permission)")
113 | }
114 | 
115 | func permissionSet(permissionSet: PermissionSet, didRequestPermission permission: Permission) {
116 |     switch permissionSet.status {
117 |     case .authorized:    print("all the permissions are granted")
118 |     case .denied:        print("at least one permission is denied")
119 |     case .disabled:      print("at least one permission is disabled")
120 |     case .notDetermined: print("at least one permission is not determined")
121 |     }
122 | }
123 | ```
124 | 
125 | #### PermissionButton
126 | 
127 | > [`PermissionButton`](https://github.com/delba/Permission/blob/master/Source/PermissionButton.swift)
128 | 
129 | A `PermissionButton` requests the permission when tapped and updates itself when its underlying permission status changes.
130 | 
131 | ```swift
132 | let button = PermissionButton(.photos)
133 | ```
134 | 
135 | `PermissionButton` is a subclass of `UIButton`. All the getters and setters of `UIButton` have their equivalent in `PermissionButton`.
136 | 
137 | ```swift
138 | button.setTitles([
139 |     .authorized:    "Authorized",
140 |     .denied:        "Denied",
141 |     .disabled:      "Disabled",
142 |     .notDetermined: "Not determined"
143 | ])
144 | 
145 | // button.setAttributedTitles
146 | // button.setTitleColors
147 | // button.setTitleShadowColors
148 | // button.setImages
149 | // button.setBackgroundImages
150 | // etc.
151 | ```
152 | 
153 | #### Third-party libraries:
154 | 
155 | - [sunshinejr/**RxPermission**](https://github.com/sunshinejr/RxPermission) RxSwift bindings for Permissions API in iOS.
156 | 
157 | ## Example
158 | 
159 | ```swift
160 | class PermissionsViewController: UIViewController, PermissionSetDelegate {
161 | 
162 |     override func viewDidLoad() {
163 |         super.viewDidLoad()
164 | 
165 |         let label = UILabel()
166 | 
167 |         let contacts   = PermissionButton(.contacts)
168 |         let camera     = PermissionButton(.camera)
169 |         let microphone = PermissionButton(.microphone)
170 |         let photos     = PermissionButton(.photos)
171 | 
172 |         contacts.setTitles([
173 |             .notDetermined: "Contacts - NotDetermined"
174 |             .authorized:    "Contacts - Authorized",
175 |             .denied:        "Contacts - Denied"
176 |         ])
177 | 
178 |         contacts.setTitleColors([
179 |             .notDetermined: .black,
180 |             .authorized:    .green,
181 |             .denied:        .red
182 |         ])
183 | 
184 |         // ...
185 | 
186 |         let permissionSet = PermissionSet(contacts, camera, microphone, photos)
187 | 
188 |         permissionSet.delegate = self
189 | 
190 |         label.text = String(describing: permissionSet.status)
191 | 
192 |         for subview in [label, contacts, camera, microphone, photos] {
193 |             view.addSubview(subview)
194 |         }
195 |     }
196 | 
197 |     func permissionSet(permissionSet: PermissionSet, didRequestPermission permission: Permission) {
198 |         label.text = String(permissionSet.status)
199 |     }
200 | }
201 | ```
202 | 
203 | <img align="center" src="https://raw.githubusercontent.com/delba/Permission/assets/permission.gif" />
204 | 
205 | ## Installation
206 | 
207 | ### Carthage
208 | 
209 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application.
210 | 
211 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
212 | 
213 | ```bash
214 | $ brew update
215 | $ brew install carthage
216 | ```
217 | 
218 | To integrate Permission into your Xcode project using Carthage, specify it in your `Cartfile`:
219 | 
220 | ```ogdl
221 | github "delba/Permission"
222 | ```
223 | 
224 | ##### Configuration
225 | 
226 | Due to Apple's new policy regarding permission access, binaries may be rejected due to a perceived attempt
227 | to access privacy-sensitive data without a usage key, and then further rejected for not actually requesting
228 | permissions.
229 | 
230 | As a workaround, you can provide custom build flags _before_ building the dynamic framework to only compile
231 | with permissions you request. This is done by adding a configuration file named `PermissionConfiguration.xcconfig`
232 | to the root of your project. For convenience, you can use
233 | `PermissionConfiguration.xcconfig` in the `Permission/` repo directory. Just comment out the permissions
234 | you want to use, and compile the framework.
235 | 
236 | To compile with only notifications and photos permissions:
237 | ```
238 | PERMISSION_BLUETOOTH         = // PERMISSION_BLUETOOTH
239 | PERMISSION_CAMERA            = PERMISSION_CAMERA
240 | PERMISSION_CONTACTS          = // PERMISSION_CONTACTS
241 | PERMISSION_EVENTS            = // PERMISSION_EVENTS
242 | PERMISSION_LOCATION          = // PERMISSION_LOCATION
243 | PERMISSION_MICROPHONE        = // PERMISSION_MICROPHONE
244 | PERMISSION_MOTION            = // PERMISSION_MOTION
245 | PERMISSION_NOTIFICATIONS     = PERMISSION_NOTIFICATIONS
246 | PERMISSION_PHOTOS            = // PERMISSION_PHOTOS
247 | PERMISSION_REMINDERS         = // PERMISSION_REMINDERS
248 | PERMISSION_SPEECH_RECOGNIZER = // PERMISSION_SPEECH_RECOGNIZER
249 | PERMISSION_MEDIA_LIBRARY     = // PERMISSION_MEDIA_LIBRARY
250 | 
251 | // Do not modify this line. Instead, remove comments above as needed to enable the categories your app uses.
252 | PERMISSION_FLAGS= $(PERMISSION_BLUETOOTH) $(PERMISSION_CAMERA) $(PERMISSION_CONTACTS) $(PERMISSION_EVENTS) $(PERMISSION_LOCATION) $(PERMISSION_MICROPHONE) $(PERMISSION_MOTION) $(PERMISSION_NOTIFICATIONS) $(PERMISSION_PHOTOS) $(PERMISSION_REMINDERS) $(PERMISSION_SPEECH_RECOGNIZER) $(PERMISSION_MEDIA_LIBRARY)
253 | 
254 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(PERMISSION_FLAGS)
255 | ```
256 | 
257 | ### Cocoapods
258 | 
259 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects.
260 | 
261 | You can install it with the following command:
262 | 
263 | ```bash
264 | $ gem install cocoapods
265 | ```
266 | 
267 | To integrate Permission into your Xcode project using CocoaPods, specify it in your `Podfile`. Due to Apple's new policy regarding permission access you need to specifically define what kind of permissions you want to access using subspecs. For example if you want to access the Camera and the Notifications you define the following:
268 | 
269 | ```ruby
270 | use_frameworks!
271 | 
272 | pod 'Permission/Camera'
273 | pod 'Permission/Notifications'
274 | ```
275 | 
276 | Please see `Permission.podspec` for more information about which subspecs are available.
277 | 
278 | ## License
279 | 
280 | Copyright (c) 2015-2019 Damien (http://delba.io)
281 | 
282 | Permission is hereby granted, free of charge, to any person obtaining a copy
283 | of this software and associated documentation files (the "Software"), to deal
284 | in the Software without restriction, including without limitation the rights
285 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
286 | copies of the Software, and to permit persons to whom the Software is
287 | furnished to do so, subject to the following conditions:
288 | 
289 | The above copyright notice and this permission notice shall be included in all
290 | copies or substantial portions of the Software.
291 | 
292 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
293 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
294 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
295 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
296 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
297 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
298 | SOFTWARE.
299 | 


--------------------------------------------------------------------------------
/Source/Permission.swift:
--------------------------------------------------------------------------------
  1 | //
  2 | // Permission.swift
  3 | //
  4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
  5 | //
  6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
  7 | // of this software and associated documentation files (the "Software"), to deal
  8 | // in the Software without restriction, including without limitation the rights
  9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 | // copies of the Software, and to permit persons to whom the Software is
 11 | // furnished to do so, subject to the following conditions:
 12 | //
 13 | // The above copyright notice and this permission notice shall be included in all
 14 | // copies or substantial portions of the Software.
 15 | //
 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22 | // SOFTWARE.
 23 | //
 24 | 
 25 | open class Permission: NSObject {
 26 |     public typealias Callback = (PermissionStatus) -> Void
 27 | 
 28 |     #if PERMISSION_CONTACTS
 29 |     /// The permission to access the user's contacts.
 30 |     public static let contacts = Permission(type: .contacts)
 31 |     #endif
 32 | 
 33 |     #if PERMISSION_LOCATION
 34 |     /// The permission to access the user's location when the app is in background.
 35 |     public static let locationAlways = Permission(type: .locationAlways)
 36 | 
 37 |     /// The permission to access the user's location when the app is in use.
 38 |     public static let locationWhenInUse = Permission(type: .locationWhenInUse)
 39 |     #endif
 40 | 
 41 |     #if PERMISSION_MICROPHONE
 42 |     /// The permission to access the microphone.
 43 |     public static let microphone = Permission(type: .microphone)
 44 |     #endif
 45 | 
 46 |     #if PERMISSION_CAMERA
 47 |     /// The permission to access the camera.
 48 |     public static let camera = Permission(type: .camera)
 49 |     #endif
 50 | 
 51 |     #if PERMISSION_PHOTOS
 52 |     /// The permission to access the user's photos.
 53 |     public static let photos = Permission(type: .photos)
 54 |     #endif
 55 | 
 56 |     #if PERMISSION_REMINDERS
 57 |     /// The permission to access the user's reminders.
 58 |     public static let reminders = Permission(type: .reminders)
 59 |     #endif
 60 | 
 61 |     #if PERMISSION_EVENTS
 62 |     /// The permission to access the user's events.
 63 |     public static let events = Permission(type: .events)
 64 |     #endif
 65 | 
 66 |     #if PERMISSION_BLUETOOTH
 67 |     /// The permission to access the user's bluetooth.
 68 |     public static let bluetooth = Permission(type: .bluetooth)
 69 |     #endif
 70 | 
 71 |     #if PERMISSION_MOTION
 72 |     /// The permission to access the user's motion.
 73 |     public static let motion = Permission(type: .motion)
 74 |     #endif
 75 | 
 76 |     #if PERMISSION_SPEECH_RECOGNIZER
 77 |     /// The permission to access the user's SpeechRecognizer.
 78 |     @available(iOS 10.0, *)
 79 |     public static let speechRecognizer = Permission(type: .speechRecognizer)
 80 |     #endif
 81 | 
 82 |     #if PERMISSION_MEDIA_LIBRARY
 83 |     /// The permission to access the user's MediaLibrary.
 84 |     @available(iOS 9.3, *)
 85 |     public static let mediaLibrary = Permission(type: .mediaLibrary)
 86 |     #endif
 87 | 
 88 |     #if PERMISSION_SIRI
 89 |     /// The permission to access the user's Siri.
 90 |     @available(iOS 10.0, *)
 91 |     public static let siri = Permission(type: .siri)
 92 |     #endif
 93 | 
 94 |     #if PERMISSION_NOTIFICATIONS
 95 |     /// The permission to send notifications.
 96 |     public static let notifications: Permission = {
 97 |         let options: UNAuthorizationOptions = [.badge, .sound, .alert]
 98 |         return Permission(type: .notifications(options))
 99 |     }()
100 | 
101 |     /// Variable used to retain the notifications permission.
102 |     private static var _notifications: Permission?
103 | 
104 |     /// The permission to send notifications.
105 |     public static func notifications(options: UNAuthorizationOptions) -> Permission {
106 |         let permission = Permission(type: .notifications(options))
107 |         _notifications = permission
108 |         return permission
109 |     }
110 |     #endif
111 | 
112 |     /// The permission domain.
113 |     public let type: PermissionType
114 | 
115 |     /// The permission status.
116 |     open var status: PermissionStatus {
117 |         switch type {
118 |         #if PERMISSION_CONTACTS
119 |         case .contacts: return statusContacts
120 |         #endif
121 | 
122 |         #if PERMISSION_LOCATION
123 |         case .locationAlways: return statusLocationAlways
124 |         case .locationWhenInUse: return statusLocationWhenInUse
125 |         #endif
126 | 
127 |         #if PERMISSION_NOTIFICATIONS
128 |         case .notifications: return statusNotifications
129 |         #endif
130 | 
131 |         #if PERMISSION_MICROPHONE
132 |         case .microphone: return statusMicrophone
133 |         #endif
134 | 
135 |         #if PERMISSION_CAMERA
136 |         case .camera: return statusCamera
137 |         #endif
138 | 
139 |         #if PERMISSION_PHOTOS
140 |         case .photos: return statusPhotos
141 |         #endif
142 | 
143 |         #if PERMISSION_REMINDERS
144 |         case .reminders: return statusReminders
145 |         #endif
146 | 
147 |         #if PERMISSION_EVENTS
148 |         case .events: return statusEvents
149 |         #endif
150 | 
151 |         #if PERMISSION_BLUETOOTH
152 |         case .bluetooth: return statusBluetooth
153 |         #endif
154 | 
155 |         #if PERMISSION_MOTION
156 |         case .motion: return statusMotion
157 |         #endif
158 | 
159 |         #if PERMISSION_SPEECH_RECOGNIZER
160 |         case .speechRecognizer: return statusSpeechRecognizer
161 |         #endif
162 | 
163 |         #if PERMISSION_MEDIA_LIBRARY
164 |         case .mediaLibrary: return statusMediaLibrary
165 |         #endif
166 | 
167 |         #if PERMISSION_SIRI
168 |         case .siri: return statusSiri
169 |         #endif
170 | 
171 |         case .never: fatalError()
172 |         }
173 |     }
174 | 
175 |     /// Determines whether to present the pre-permission alert.
176 |     open var presentPrePermissionAlert = false
177 | 
178 |     /// The pre-permission alert.
179 |     open lazy var prePermissionAlert: PermissionAlert = {
180 |         return PrePermissionAlert(permission: self)
181 |     }()
182 | 
183 |     /// Determines whether to present the denied alert.
184 |     open var presentDeniedAlert = true
185 | 
186 |     /// The alert when the permission was denied.
187 |     open lazy var deniedAlert: PermissionAlert = {
188 |         return DeniedAlert(permission: self)
189 |     }()
190 | 
191 |     /// Determines whether to present the disabled alert.
192 |     open var presentDisabledAlert = true
193 | 
194 |     /// The alert when the permission is disabled.
195 |     open lazy var disabledAlert: PermissionAlert = {
196 |         return DisabledAlert(permission: self)
197 |     }()
198 | 
199 |     var callback: Callback?
200 | 
201 |     var permissionSets: [PermissionSet] = []
202 | 
203 |     /**
204 |      Creates and return a new permission for the specified type.
205 |      
206 |      - parameter type: The permission type.
207 |      
208 |      - returns: A newly created permission.
209 |      */
210 |     private init(type: PermissionType) {
211 |         self.type = type
212 |     }
213 | 
214 |     /**
215 |      Requests the permission.
216 |      
217 |      - parameter callback: The function to be triggered after the user responded to the request.
218 |      */
219 |     open func request(_ callback: @escaping Callback) {
220 |         self.callback = callback
221 | 
222 |         DispatchQueue.main.async {
223 |             self.permissionSets.forEach { $0.willRequestPermission(self) }
224 |         }
225 | 
226 |         let status = self.status
227 | 
228 |         switch status {
229 |         case .authorized:    callbacks(status)
230 |         case .notDetermined: presentPrePermissionAlert ? prePermissionAlert.present() : requestAuthorization(callbacks)
231 |         case .denied:        presentDeniedAlert ? deniedAlert.present() : callbacks(status)
232 |         case .disabled:      presentDisabledAlert ? disabledAlert.present() : callbacks(status)
233 |         }
234 |     }
235 | 
236 |     func requestAuthorization(_ callback: @escaping Callback) {
237 |         switch type {
238 |         #if PERMISSION_CONTACTS
239 |         case .contacts: requestContacts(callback)
240 |         #endif
241 | 
242 |         #if PERMISSION_LOCATION
243 |         case .locationAlways: requestLocationAlways(callback)
244 |         case .locationWhenInUse: requestLocationWhenInUse(callback)
245 |         #endif
246 | 
247 |         #if PERMISSION_NOTIFICATIONS
248 |         case .notifications: requestNotifications(callback)
249 |         #endif
250 | 
251 |         #if PERMISSION_MICROPHONE
252 |         case .microphone: requestMicrophone(callback)
253 |         #endif
254 | 
255 |         #if PERMISSION_CAMERA
256 |         case .camera: requestCamera(callback)
257 |         #endif
258 | 
259 |         #if PERMISSION_PHOTOS
260 |         case .photos: requestPhotos(callback)
261 |         #endif
262 | 
263 |         #if PERMISSION_REMINDERS
264 |         case .reminders: requestReminders(callback)
265 |         #endif
266 | 
267 |         #if PERMISSION_EVENTS
268 |         case .events: requestEvents(callback)
269 |         #endif
270 | 
271 |         #if PERMISSION_BLUETOOTH
272 |         case .bluetooth: requestBluetooth(self.callback)
273 |         #endif
274 | 
275 |         #if PERMISSION_MOTION
276 |         case .motion: requestMotion(self.callback)
277 |         #endif
278 | 
279 |         #if PERMISSION_SPEECH_RECOGNIZER
280 |         case .speechRecognizer: requestSpeechRecognizer(callback)
281 |         #endif
282 | 
283 |         #if PERMISSION_MEDIA_LIBRARY
284 |         case .mediaLibrary: requestMediaLibrary(callback)
285 |         #endif
286 | 
287 |         #if PERMISSION_SIRI
288 |         case .siri: requestSiri(callback)
289 |         #endif
290 | 
291 |         case .never: fatalError()
292 |         }
293 |     }
294 | 
295 |     func callbacks(_ with: PermissionStatus) {
296 |         DispatchQueue.main.async {
297 |             self.callback?(self.status)
298 |             self.permissionSets.forEach { $0.didRequestPermission(self) }
299 |         }
300 |     }
301 | }
302 | 
303 | extension Permission {
304 |     /// The textual representation of self.
305 |     override open var description: String {
306 |         return type.description
307 |     }
308 | 
309 |     /// A textual representation of this instance, suitable for debugging.
310 |     override open var debugDescription: String {
311 |         return "\(type): \(status)"
312 |     }
313 | 
314 | }
315 | 


--------------------------------------------------------------------------------
/Source/PermissionAlert.swift:
--------------------------------------------------------------------------------
  1 | //
  2 | // PermissionAlert.swift
  3 | //
  4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
  5 | //
  6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
  7 | // of this software and associated documentation files (the "Software"), to deal
  8 | // in the Software without restriction, including without limitation the rights
  9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 | // copies of the Software, and to permit persons to whom the Software is
 11 | // furnished to do so, subject to the following conditions:
 12 | //
 13 | // The above copyright notice and this permission notice shall be included in all
 14 | // copies or substantial portions of the Software.
 15 | //
 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22 | // SOFTWARE.
 23 | //
 24 | 
 25 | open class PermissionAlert {
 26 |     /// The permission.
 27 |     fileprivate let permission: Permission
 28 | 
 29 |     /// The status of the permission.
 30 |     fileprivate var status: PermissionStatus { return permission.status }
 31 | 
 32 |     /// The type of the permission.
 33 |     private var type: PermissionType { return permission.type }
 34 | 
 35 |     fileprivate var callbacks: Permission.Callback { return permission.callbacks }
 36 | 
 37 |     /// The title of the alert.
 38 |     open var title: String?
 39 | 
 40 |     /// Descriptive text that provides more details about the reason for the alert.
 41 |     open var message: String?
 42 | 
 43 |     /// The title of the cancel action.
 44 |     open var cancel: String? {
 45 |         get { return cancelActionTitle }
 46 |         set { cancelActionTitle = newValue }
 47 |     }
 48 | 
 49 |     /// The title of the settings action.
 50 |     open var settings: String? {
 51 |         get { return defaultActionTitle }
 52 |         set { defaultActionTitle = newValue }
 53 |     }
 54 | 
 55 |     /// The title of the confirm action.
 56 |     open var confirm: String? {
 57 |         get { return defaultActionTitle }
 58 |         set { defaultActionTitle = newValue }
 59 |     }
 60 | 
 61 |     fileprivate var cancelActionTitle: String?
 62 |     fileprivate var defaultActionTitle: String?
 63 | 
 64 |     var controller: UIAlertController {
 65 |         let controller = UIAlertController(title: title, message: message, preferredStyle: .alert)
 66 | 
 67 |         let action = UIAlertAction(title: cancelActionTitle, style: .cancel, handler: cancelHandler)
 68 |         controller.addAction(action)
 69 | 
 70 |         return controller
 71 |     }
 72 | 
 73 |     init(permission: Permission) {
 74 |         self.permission = permission
 75 |     }
 76 | 
 77 |     func present() {
 78 |         DispatchQueue.main.async {
 79 |             UIApplication.shared.present(self.controller)
 80 |         }
 81 |     }
 82 | 
 83 |     private func cancelHandler(_ action: UIAlertAction) {
 84 |         callbacks(status)
 85 |     }
 86 | }
 87 | 
 88 | class DisabledAlert: PermissionAlert {
 89 |     override init(permission: Permission) {
 90 |         super.init(permission: permission)
 91 | 
 92 |         title   = "\(permission) is currently disabled"
 93 |         message = "Please enable access to \(permission) in the Settings app."
 94 |         cancel  = "OK"
 95 |     }
 96 | }
 97 | 
 98 | class DeniedAlert: PermissionAlert {
 99 |     override var controller: UIAlertController {
100 |         let controller = super.controller
101 | 
102 |         let action = UIAlertAction(title: defaultActionTitle, style: .default, handler: settingsHandler)
103 |         controller.addAction(action)
104 | 
105 |         if #available(iOS 9.0, *) {
106 |             controller.preferredAction = action
107 |         }
108 | 
109 |         return controller
110 |     }
111 | 
112 |     override init(permission: Permission) {
113 |         super.init(permission: permission)
114 | 
115 |         title    = "Permission for \(permission) was denied"
116 |         message  = "Please enable access to \(permission) in the Settings app."
117 |         cancel   = "Cancel"
118 |         settings = "Settings"
119 |     }
120 | 
121 |     @objc func settingsHandler() {
122 |         NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification)
123 |         callbacks(status)
124 |     }
125 | 
126 |     private func settingsHandler(_ action: UIAlertAction) {
127 |         NotificationCenter.default.addObserver(self, selector: .settingsHandler, name: UIApplication.didBecomeActiveNotification)
128 | 
129 |         if let URL = URL(string: UIApplication.openSettingsURLString) {
130 |             UIApplication.shared.open(URL)
131 |         }
132 |     }
133 | }
134 | 
135 | class PrePermissionAlert: PermissionAlert {
136 |     override var controller: UIAlertController {
137 |         let controller = super.controller
138 | 
139 |         let action = UIAlertAction(title: defaultActionTitle, style: .default, handler: confirmHandler)
140 |         controller.addAction(action)
141 | 
142 |         if #available(iOS 9.0, *) {
143 |             controller.preferredAction = action
144 |         }
145 | 
146 |         return controller
147 |     }
148 | 
149 |     override init(permission: Permission) {
150 |         super.init(permission: permission)
151 | 
152 |         title   = "\(Bundle.main.name) would like to access your \(permission)"
153 |         message = "Please enable access to \(permission)."
154 |         cancel  = "Cancel"
155 |         confirm = "Confirm"
156 |     }
157 | 
158 |     private func confirmHandler(_ action: UIAlertAction) {
159 |         permission.requestAuthorization(callbacks)
160 |     }
161 | }
162 | 


--------------------------------------------------------------------------------
/Source/PermissionButton.swift:
--------------------------------------------------------------------------------
  1 | //
  2 | // PermissionButton.swift
  3 | //
  4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
  5 | //
  6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
  7 | // of this software and associated documentation files (the "Software"), to deal
  8 | // in the Software without restriction, including without limitation the rights
  9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 | // copies of the Software, and to permit persons to whom the Software is
 11 | // furnished to do so, subject to the following conditions:
 12 | //
 13 | // The above copyright notice and this permission notice shall be included in all
 14 | // copies or substantial portions of the Software.
 15 | //
 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22 | // SOFTWARE.
 23 | //
 24 | 
 25 | open class PermissionButton: UIButton {
 26 | 
 27 |     /// The permission of the button.
 28 |     public let permission: Permission
 29 | 
 30 |     /// The permission type of the button.
 31 |     open var type: PermissionType { return permission.type }
 32 | 
 33 |     /// The permission status of the button.
 34 |     open var status: PermissionStatus { return permission.status }
 35 | 
 36 |     private var titles: [UIControl.State: [PermissionStatus: String]] = [:]
 37 |     private var attributedTitles: [UIControl.State: [PermissionStatus: NSAttributedString]] = [:]
 38 |     private var titleColors: [UIControl.State: [PermissionStatus: UIColor]] = [:]
 39 |     private var titleShadowColors: [UIControl.State: [PermissionStatus: UIColor]] = [:]
 40 |     private var images: [UIControl.State: [PermissionStatus: UIImage]] = [:]
 41 |     private var backgroundImages: [UIControl.State: [PermissionStatus: UIImage]] = [:]
 42 | 
 43 |     /// The alert when the permission was denied.
 44 |     open var deniedAlert: PermissionAlert {
 45 |         return permission.deniedAlert
 46 |     }
 47 | 
 48 |     /// The alert when the permission is disabled.
 49 |     open var disabledAlert: PermissionAlert {
 50 |         return permission.disabledAlert
 51 |     }
 52 | 
 53 |     /// The textual representation of self.
 54 |     open override var description: String {
 55 |         return permission.description
 56 |     }
 57 | 
 58 |     // MARK: - Initialization
 59 | 
 60 |     /**
 61 |     Creates and returns a new button for the specified permission.
 62 |     
 63 |     - parameter permission: The permission.
 64 |     
 65 |     - returns: A newly created button.
 66 |     */
 67 |     public init(_ permission: Permission) {
 68 |         self.permission = permission
 69 | 
 70 |         super.init(frame: .zero)
 71 | 
 72 |         addTarget(self, action: .tapped, for: .touchUpInside)
 73 |         addTarget(self, action: .highlight, for: .touchDown)
 74 |     }
 75 | 
 76 |     /**
 77 |      Returns an object initialized from data in a given unarchiver.
 78 |      
 79 |      - parameter aDecoder: An unarchiver object.
 80 |      
 81 |      - returns: self, initialized using the data in decoder.
 82 |      */
 83 |     public required init?(coder aDecoder: NSCoder) {
 84 |         fatalError("init(coder:) has not been implemented")
 85 |     }
 86 | 
 87 |     // MARK: - Titles
 88 | 
 89 |     /**
 90 |     Returns the title associated with the specified permission status and state.
 91 |     
 92 |     - parameter status: The permission status that uses the title.
 93 |     - parameter state:  The state that uses the title.
 94 |     
 95 |     - returns: The title for the specified permission status and state.
 96 |     */
 97 |     open func titleForStatus(_ status: PermissionStatus, andState state: UIControl.State = .normal) -> String? {
 98 |         return titles[state]?[status]
 99 |     }
100 | 
101 |     /**
102 |     Sets the title to use for the specified state.
103 |     
104 |     - parameter title: The title to use for the specified state.
105 |     - parameter state: The state that uses the specified title.
106 |     */
107 |     open override func setTitle(_ title: String?, for state: UIControl.State) {
108 |         titles[state] = nil
109 |         super.setTitle(title, for: state)
110 |     }
111 | 
112 |     /**
113 |      Sets the title to use for the specified permission status and state.
114 |      
115 |      - parameter title:  The title to use for the specified state.
116 |      - parameter status: The permission status that uses the specified title.
117 |      - parameter state:  The state that uses the specified title.
118 |      */
119 |     open func setTitle(_ title: String?, forStatus status: PermissionStatus, andState state: UIControl.State = .normal) {
120 |         guard [.normal, .highlighted].contains(state) else { return }
121 | 
122 |         if titles[state] == nil {
123 |             titles[state] = [:]
124 |         }
125 | 
126 |         titles[state]?[status] = title
127 |     }
128 | 
129 |     /**
130 |      Sets the titles to use for the specified permission statuses and state.
131 |      
132 |      - parameter titles: The titles to use for the specified statuses.
133 |      - parameter state:  The state that uses the specifed titles.
134 |      */
135 |     open func setTitles(_ titles: [PermissionStatus: String?], forState state: UIControl.State = .normal) {
136 |         guard [.normal, .highlighted].contains(state) else { return }
137 | 
138 |         if self.titles[state] == nil {
139 |             self.titles[state] = [:]
140 |         }
141 | 
142 |         for (status, title) in titles {
143 |             self.titles[state]?[status] = title
144 |         }
145 |     }
146 | 
147 |     // MARK: - Attributed titles
148 | 
149 |     /**
150 |     Returns the styled title associated with the specified permission status and state.
151 |     
152 |     - parameter status: The permission status that uses the styled title.
153 |     - parameter state:  The state that uses the styled title.
154 |     
155 |     - returns: The title for the specified permission status and state.
156 |     */
157 |     open func attributedTitleForStatus(_ status: PermissionStatus, andState state: UIControl.State = .normal) -> NSAttributedString? {
158 |         return attributedTitles[state]?[status]
159 |     }
160 | 
161 |     /**
162 |     Sets the styled title to use for the specified state.
163 |     
164 |     - parameter title: The styled text string to use for the title.
165 |     - parameter state: The state that uses the specified title.
166 |     */
167 |     open override func setAttributedTitle(_ title: NSAttributedString?, for state: UIControl.State) {
168 |         attributedTitles[state] = nil
169 |         super.setAttributedTitle(title, for: state)
170 |     }
171 | 
172 |     /**
173 |      Sets the styled title to use for the specifed permission status and state.
174 |      
175 |      - parameter title:  The styled text string to use for the title.
176 |      - parameter status: The permission status that uses the specified title.
177 |      - parameter state:  The state that uses the specified title.
178 |      */
179 |     open func setAttributedTitle(_ title: NSAttributedString?, forStatus status: PermissionStatus, andState state: UIControl.State = .normal) {
180 |         guard [.normal, .highlighted].contains(state) else { return }
181 | 
182 |         if attributedTitles[state] == nil {
183 |             attributedTitles[state] = [:]
184 |         }
185 | 
186 |         attributedTitles[state]?[status] = title
187 |     }
188 | 
189 |     /**
190 |      Sets the styled titles to use for the specified permission statuses and state.
191 |      
192 |      - parameter titles: The titles to use for the specified statuses.
193 |      - parameter state:  The state that uses the specified titles.
194 |      */
195 |     open func setAttributedTitles(_ titles: [PermissionStatus: NSAttributedString?], forState state: UIControl.State = .normal) {
196 |         guard [.normal, .highlighted].contains(state) else { return }
197 | 
198 |         if attributedTitles[state] == nil {
199 |             attributedTitles[state] = [:]
200 |         }
201 | 
202 |         for (status, title) in titles {
203 |             attributedTitles[state]?[status] = title
204 |         }
205 |     }
206 | 
207 |     // MARK: - Title colors
208 | 
209 |     /**
210 |     Returns the title color used for a permission status and state.
211 |     
212 |     - parameter status: The permission status that uses the title color.
213 |     - parameter state:  The state that uses the title color.
214 |     
215 |     - returns: The color of the title for the specified permission status and state.
216 |     */
217 |     open func titleColorForStatus(_ status: PermissionStatus, andState state: UIControl.State = .normal) -> UIColor? {
218 |         return titleColors[state]?[status]
219 |     }
220 | 
221 |     /**
222 |     Sets the color of the title to use for the specified state.
223 |     
224 |     - parameter color: The color of the title to use for the specified state.
225 |     - parameter state: The state that uses the specified color.
226 |     */
227 |     open override func setTitleColor(_ color: UIColor?, for state: UIControl.State) {
228 |         titleColors[state] = nil
229 |         super.setTitleColor(color, for: state)
230 |     }
231 | 
232 |     /**
233 |      Sets the color of the title to use for the specified permission status and state.
234 |      
235 |      - parameter color:  The color of the title to use for the specified permission status and state.
236 |      - parameter status: The permission status that uses the specified color.
237 |      - parameter state:  The state that uses the specified color.
238 |      */
239 |     open func setTitleColor(_ color: UIColor?, forStatus status: PermissionStatus, andState state: UIControl.State = .normal) {
240 |         guard [.normal, .highlighted].contains(state) else { return }
241 | 
242 |         if titleColors[state] == nil {
243 |             titleColors[state] = [:]
244 |         }
245 | 
246 |         titleColors[state]?[status] = color
247 |     }
248 | 
249 |     /**
250 |      Sets the colors of the title to use for the specified permission statuses and state.
251 |      
252 |      - parameter colors: The colors to use for the specified permission statuses.
253 |      - parameter state:  The state that uses the specified colors.
254 |      */
255 |     open func setTitleColors(_ colors: [PermissionStatus: UIColor?], forState state: UIControl.State = .normal) {
256 |         guard [.normal, .highlighted].contains(state) else { return }
257 | 
258 |         if titleColors[state] == nil {
259 |             titleColors[state] = [:]
260 |         }
261 | 
262 |         for (status, color) in colors {
263 |             titleColors[state]?[status] = color
264 |         }
265 |     }
266 | 
267 |     // MARK: - Title shadow colors
268 | 
269 |     /**
270 |     Returns the shadow color of the title used for a permission status and state.
271 |     
272 |     - parameter status: The permission status that uses the title shadow color.
273 |     - parameter state:  The state that uses the title shadow color.
274 |     
275 |     - returns: The color of the title's shadow for the specified permission status and state.
276 |     */
277 |     open func titleShadowColorForStatus(_ status: PermissionStatus, andState state: UIControl.State = .normal) -> UIColor? {
278 |         return titleShadowColors[state]?[status]
279 |     }
280 | 
281 |     /**
282 |      Sets the color of the title shadow to use for the specified state.
283 |      
284 |      - parameter color: The color of the title shadow to use for the specified state.
285 |      - parameter state: The state that uses the specified color.
286 |      */
287 |     open override func setTitleShadowColor(_ color: UIColor?, for state: UIControl.State) {
288 |         titleShadowColors[state] = nil
289 |         super.setTitleShadowColor(color, for: state)
290 |     }
291 | 
292 |     /**
293 |      Sets the color of the title shadow to use for the specified permission status and state.
294 |      
295 |      - parameter color:  The color of the title shadow to use for the specified permission status and state.
296 |      - parameter status: The permission status that uses the specified color.
297 |      - parameter state:  The state that uses the specified color.
298 |      */
299 |     open func setTitleShadowColor(_ color: UIColor?, forStatus status: PermissionStatus, andState state: UIControl.State = .normal) {
300 |         guard [.normal, .highlighted].contains(state) else { return }
301 | 
302 |         if titleShadowColors[state] == nil {
303 |             titleShadowColors[state] = [:]
304 |         }
305 | 
306 |         titleShadowColors[state]?[status] = color
307 |     }
308 | 
309 |     /**
310 |     Sets the colors of the title shadow to use for the specified permission statuses and state.
311 |     
312 |     - parameter colors: The colors to use for the specified permission statuses.
313 |     - parameter state:  The state that uses the specified colors.
314 |     */
315 |     open func setTitleShadowColors(_ colors: [PermissionStatus: UIColor?], forState state: UIControl.State = .normal) {
316 |         guard [.normal, .highlighted].contains(state) else { return }
317 | 
318 |         if titleShadowColors[state] == nil {
319 |             titleShadowColors[state] = [:]
320 |         }
321 | 
322 |         for (status, color) in colors {
323 |             titleShadowColors[state]?[status] = color
324 |         }
325 |     }
326 | 
327 |     // MARK: - Images
328 | 
329 |     /**
330 |     Returns the image used for a permission status and state
331 |     
332 |     - parameter status: The permission status that uses the image.
333 |     - parameter state:  The state that uses the image.
334 |     
335 |     - returns: The image used for the specified permission status and state.
336 |     */
337 |     open func imageForStatus(_ status: PermissionStatus, andState state: UIControl.State = .normal) -> UIImage? {
338 |         return images[state]?[status]
339 |     }
340 | 
341 |     /**
342 |      Sets the image to use for the specified state.
343 |      
344 |      - parameter image: The image to use for the specified state.
345 |      - parameter state: The state that uses the specified image.
346 |      */
347 |     open override func setImage(_ image: UIImage?, for state: UIControl.State) {
348 |         images[state] = nil
349 |         super.setImage(image, for: state)
350 |     }
351 | 
352 |     /**
353 |      Sets the image to use for the specified permission status and state.
354 |      
355 |      - parameter image:  The image to use for the specified permission status and state.
356 |      - parameter status: The permission status that uses the specified image.
357 |      - parameter state:  The state that uses the specified image.
358 |      */
359 |     open func setImage(_ image: UIImage?, forStatus status: PermissionStatus, andState state: UIControl.State = .normal) {
360 |         guard [.normal, .highlighted].contains(state) else { return }
361 | 
362 |         if images[state] == nil {
363 |             images[state] = [:]
364 |         }
365 | 
366 |         images[state]?[status] = image
367 |     }
368 | 
369 |     /**
370 |      Sets the images for the specified permission statuses and state.
371 |      
372 |      - parameter images: The images to use for the specified permission statuses.
373 |      - parameter state:  The state that uses the specified images.
374 |      */
375 |     open func setImages(_ images: [PermissionStatus: UIImage], forState state: UIControl.State = .normal) {
376 |         guard [.normal, .highlighted].contains(state) else { return }
377 | 
378 |         if self.images[state] == nil {
379 |             self.images[state] = [:]
380 |         }
381 | 
382 |         for (status, image) in images {
383 |             self.images[state]?[status] = image
384 |         }
385 |     }
386 | 
387 |     // MARK: - Background images
388 | 
389 |     /**
390 |     Returns the background image used for a permission status and a button state.
391 |     
392 |     - parameter status: The permission status that uses the background image.
393 |     - parameter state:  The state that uses the background image.
394 |     
395 |     - returns: The background image used for the specified permission status and state.
396 |     */
397 |     open func backgroundImageForStatus(_ status: PermissionStatus, andState state: UIControl.State = .normal) -> UIImage? {
398 |         return backgroundImages[state]?[status]
399 |     }
400 | 
401 |     /**
402 |      Sets the background image to use for the specified button state.
403 |      
404 |      - parameter image: The background image to use for the specified state.
405 |      - parameter state: The state that uses the specified image.
406 |      */
407 |     open override func setBackgroundImage(_ image: UIImage?, for state: UIControl.State) {
408 |         backgroundImages[state] = nil
409 |         super.setBackgroundImage(image, for: state)
410 |     }
411 | 
412 |     /**
413 |      Sets the background image to use for the specified permission status and button state.
414 |      
415 |      - parameter image:  The background image to use for the specified permission status and button state.
416 |      - parameter status: The permission status that uses the specified image.
417 |      - parameter state:  The state that uses the specified image.
418 |      */
419 |     open func setBackgroundImage(_ image: UIImage?, forStatus status: PermissionStatus, andState state: UIControl.State = .normal) {
420 |         guard [.normal, .highlighted].contains(state) else { return }
421 | 
422 |         if backgroundImages[state] == nil {
423 |             backgroundImages[state] = [:]
424 |         }
425 | 
426 |         backgroundImages[state]?[status] = image
427 |     }
428 | 
429 |     /**
430 |      Set the background images to use for the specified permission statuses and button state.
431 |      
432 |      - parameter images: The background images to use for the specified permission statuses.
433 |      - parameter state:  The state that uses the specified images.
434 |      */
435 |     open func setBackgroundImages(_ images: [PermissionStatus: UIImage], forState state: UIControl.State = .normal) {
436 |         guard [.normal, .highlighted].contains(state) else { return }
437 | 
438 |         if backgroundImages[state] == nil {
439 |             backgroundImages[state] = [:]
440 |         }
441 | 
442 |         for (status, image) in images {
443 |             backgroundImages[state]?[status] = image
444 |         }
445 |     }
446 | 
447 |     // MARK: - UIView
448 | 
449 |     /**
450 |     Tells the view that its superview changed.
451 |     */
452 |     open override func didMoveToSuperview() {
453 |         render(.normal)
454 |     }
455 | }
456 | 
457 | extension PermissionButton {
458 |     @objc func highlight(_ button: PermissionButton) {
459 |         render(.highlighted)
460 |     }
461 | 
462 |     @objc func tapped(_ button: PermissionButton) {
463 |         permission.request { [weak self] _ in
464 |             self?.render()
465 |         }
466 |     }
467 | }
468 | 
469 | private extension PermissionButton {
470 |     func render(_ state: UIControl.State = .normal) {
471 |         if let title = titleForStatus(status, andState: state) {
472 |             super.setTitle(title, for: state)
473 |         }
474 | 
475 |         if let title = attributedTitleForStatus(status, andState: state) {
476 |             super.setAttributedTitle(title, for: state)
477 |         }
478 | 
479 |         if let color = titleColorForStatus(status, andState: state) {
480 |             super.setTitleColor(color, for: state)
481 |         }
482 | 
483 |         if let color = titleShadowColorForStatus(status, andState: state) {
484 |             super.setTitleShadowColor(color, for: state)
485 |         }
486 | 
487 |         if let image = imageForStatus(status, andState: state) {
488 |             super.setImage(image, for: state)
489 |         }
490 | 
491 |         if let image = backgroundImageForStatus(status, andState: state) {
492 |             super.setBackgroundImage(image, for: state)
493 |         }
494 |     }
495 | }
496 | 


--------------------------------------------------------------------------------
/Source/PermissionSet.swift:
--------------------------------------------------------------------------------
  1 | //
  2 | // PermissionSet.swift
  3 | //
  4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
  5 | //
  6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
  7 | // of this software and associated documentation files (the "Software"), to deal
  8 | // in the Software without restriction, including without limitation the rights
  9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 | // copies of the Software, and to permit persons to whom the Software is
 11 | // furnished to do so, subject to the following conditions:
 12 | //
 13 | // The above copyright notice and this permission notice shall be included in all
 14 | // copies or substantial portions of the Software.
 15 | //
 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22 | // SOFTWARE.
 23 | //
 24 | 
 25 | open class PermissionSet {
 26 | 
 27 |     /// The permissions in the set.
 28 |     public let permissions: Swift.Set<Permission>
 29 | 
 30 |     /// The delegate of the permission set.
 31 |     open weak var delegate: PermissionSetDelegate?
 32 | 
 33 |     /// The permission set status
 34 |     open var status: PermissionStatus {
 35 |         let statuses = permissions.map({ $0.status })
 36 | 
 37 |         for status in statuses where status == .denied {
 38 |             return .denied
 39 |         }
 40 | 
 41 |         for status in statuses where status == .disabled {
 42 |             return .disabled
 43 |         }
 44 | 
 45 |         for status in statuses where status == .notDetermined {
 46 |             return .notDetermined
 47 |         }
 48 | 
 49 |         return .authorized
 50 |     }
 51 | 
 52 |     /**
 53 |      Creates and returns a new permission set containing the specified buttons.
 54 |      
 55 |      - parameter buttons: The buttons contained by the set.
 56 |      
 57 |      - returns: A newly created set.
 58 |      */
 59 |     public convenience init(_ buttons: PermissionButton...) {
 60 |         self.init(buttons: buttons)
 61 |     }
 62 | 
 63 |     /**
 64 |      Creates and returns a new permission set containing the specified buttons.
 65 |      
 66 |      - parameter buttons: The buttons contained by the set.
 67 |      
 68 |      - returns: A newly created set.
 69 |      */
 70 |     public convenience init(_ buttons: [PermissionButton]) {
 71 |         self.init(buttons: buttons)
 72 |     }
 73 | 
 74 |     /**
 75 |      Creates and returns a new permission set containing the specified buttons.
 76 |      
 77 |      - parameter permissions: The permissions contained by the set.
 78 |      
 79 |      - returns: A newly created set.
 80 |      */
 81 |     public convenience init(_ permissions: Permission...) {
 82 |         self.init(permissions: permissions)
 83 |     }
 84 | 
 85 |     /**
 86 |      Creates and returns a new permission set containing the specified buttons.
 87 |      
 88 |      - parameter permissions: The permissions contained by the set.
 89 |      
 90 |      - returns: A newly created set.
 91 |      */
 92 |     public convenience init(_ permissions: [Permission]) {
 93 |         self.init(permissions: permissions)
 94 |     }
 95 | 
 96 |     private convenience init(buttons: [PermissionButton]) {
 97 |         let permissions = buttons.map({ $0.permission })
 98 | 
 99 |         self.init(permissions: permissions)
100 |     }
101 | 
102 |     private init(permissions: [Permission]) {
103 |         self.permissions = Swift.Set(permissions)
104 |         self.permissions.forEach { $0.permissionSets.append(self) }
105 |     }
106 | 
107 |     func willRequestPermission(_ permission: Permission) {
108 |         delegate?.permissionSet(self, willRequestPermission: permission)
109 |     }
110 | 
111 |     func didRequestPermission(_ permission: Permission) {
112 |         delegate?.permissionSet(self, didRequestPermission: permission)
113 |     }
114 | }
115 | 
116 | extension PermissionSet: CustomStringConvertible {
117 |     /// The textual representation of self.
118 |     public var description: String {
119 |         return [
120 |             "\(status): [",
121 |             permissions.map { "\t\($0)" }.joined(separator: ",\n"),
122 |             "]"
123 |         ].joined(separator: "\n")
124 |     }
125 | }
126 | 
127 | public protocol PermissionSetDelegate: class {
128 |     /**
129 |      Tells the delegate that the specified permission has been requested.
130 |      
131 |      - parameter permissionSet: The permission set containing the requested permission.
132 |      - parameter permission:    The requested permission.
133 |      */
134 |     func permissionSet(_ permissionSet: PermissionSet, didRequestPermission permission: Permission)
135 | 
136 |     /**
137 |      Tells the delegate that the specified permission will be requested.
138 |      
139 |      - parameter permissionSet: The permission set containing the requested permission.
140 |      - parameter permission:    The requested permission.
141 |      */
142 |     func permissionSet(_ permissionSet: PermissionSet, willRequestPermission permission: Permission)
143 | }
144 | 
145 | public extension PermissionSetDelegate {
146 |     /**
147 |      Tells the delegate that the specified permission has been requested.
148 |      
149 |      - parameter permissionSet: The permission set containing the requested permission.
150 |      - parameter permission:    The requested permission.
151 |      */
152 |     func permissionSet(_ permissionSet: PermissionSet, didRequestPermission permission: Permission) {}
153 | 
154 |     /**
155 |      Tells the delegate that the specified permission will be requested.
156 |      
157 |      - parameter permissionSet: The permission set containing the requested permission.
158 |      - parameter permission:    The requested permission.
159 |      */
160 |     func permissionSet(_ permissionSet: PermissionSet, willRequestPermission permission: Permission) {}
161 | }
162 | 


--------------------------------------------------------------------------------
/Source/PermissionStatus.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // PermissionStatus.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | public enum PermissionStatus: String {
26 |     case authorized    = "Authorized"
27 |     case denied        = "Denied"
28 |     case disabled      = "Disabled"
29 |     case notDetermined = "Not Determined"
30 | 
31 |     init?(string: String?) {
32 |         guard let string = string else { return nil }
33 |         self.init(rawValue: string)
34 |     }
35 | }
36 | 
37 | extension PermissionStatus: CustomStringConvertible {
38 |     /// The textual representation of self.
39 |     public var description: String {
40 |         return rawValue
41 |     }
42 | }
43 | 


--------------------------------------------------------------------------------
/Source/PermissionType.swift:
--------------------------------------------------------------------------------
  1 | //
  2 | // PermissionType.swift
  3 | //
  4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
  5 | //
  6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
  7 | // of this software and associated documentation files (the "Software"), to deal
  8 | // in the Software without restriction, including without limitation the rights
  9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 | // copies of the Software, and to permit persons to whom the Software is
 11 | // furnished to do so, subject to the following conditions:
 12 | //
 13 | // The above copyright notice and this permission notice shall be included in all
 14 | // copies or substantial portions of the Software.
 15 | //
 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22 | // SOFTWARE.
 23 | //
 24 | 
 25 | public enum PermissionType {
 26 |     #if PERMISSION_CONTACTS
 27 |     case contacts
 28 |     #endif
 29 | 
 30 |     #if PERMISSION_LOCATION
 31 |     case locationAlways
 32 |     case locationWhenInUse
 33 |     #endif
 34 | 
 35 |     #if PERMISSION_NOTIFICATIONS
 36 |     case notifications(UNAuthorizationOptions)
 37 |     #endif
 38 | 
 39 |     #if PERMISSION_MICROPHONE
 40 |     case microphone
 41 |     #endif
 42 | 
 43 |     #if PERMISSION_CAMERA
 44 |     case camera
 45 |     #endif
 46 | 
 47 |     #if PERMISSION_PHOTOS
 48 |     case photos
 49 |     #endif
 50 | 
 51 |     #if PERMISSION_REMINDERS
 52 |     case reminders
 53 |     #endif
 54 | 
 55 |     #if PERMISSION_EVENTS
 56 |     case events
 57 |     #endif
 58 | 
 59 |     #if PERMISSION_BLUETOOTH
 60 |     case bluetooth
 61 |     #endif
 62 | 
 63 |     #if PERMISSION_MOTION
 64 |     case motion
 65 |     #endif
 66 | 
 67 |     #if PERMISSION_SPEECH_RECOGNIZER
 68 |     @available(iOS 10.0, *) case speechRecognizer
 69 |     #endif
 70 | 
 71 |     #if PERMISSION_MEDIA_LIBRARY
 72 |     @available(iOS 9.3, *) case mediaLibrary
 73 |     #endif
 74 | 
 75 |     #if PERMISSION_SIRI
 76 |     @available(iOS 10.0, *) case siri
 77 |     #endif
 78 | 
 79 |     case never
 80 | }
 81 | 
 82 | extension PermissionType: CustomStringConvertible {
 83 |     public var description: String {
 84 |         switch self {
 85 |         #if PERMISSION_CONTACTS
 86 |         case .contacts: return "Contacts"
 87 |         #endif
 88 | 
 89 |         #if PERMISSION_LOCATION
 90 |         case .locationAlways: return "Location"
 91 |         case .locationWhenInUse: return "Location"
 92 |         #endif
 93 | 
 94 |         #if PERMISSION_NOTIFICATIONS
 95 |         case .notifications: return "Notifications"
 96 |         #endif
 97 | 
 98 |         #if PERMISSION_MICROPHONE
 99 |         case .microphone: return "Microphone"
100 |         #endif
101 | 
102 |         #if PERMISSION_CAMERA
103 |         case .camera: return "Camera"
104 |         #endif
105 | 
106 |         #if PERMISSION_PHOTOS
107 |         case .photos: return "Photos"
108 |         #endif
109 | 
110 |         #if PERMISSION_REMINDERS
111 |         case .reminders: return "Reminders"
112 |         #endif
113 | 
114 |         #if PERMISSION_EVENTS
115 |         case .events: return "Events"
116 |         #endif
117 | 
118 |         #if PERMISSION_BLUETOOTH
119 |         case .bluetooth: return "Bluetooth"
120 |         #endif
121 | 
122 |         #if PERMISSION_MOTION
123 |         case .motion: return "Motion"
124 |         #endif
125 | 
126 |         #if PERMISSION_SPEECH_RECOGNIZER
127 |         case .speechRecognizer: return "Speech Recognizer"
128 |         #endif
129 | 
130 |         #if PERMISSION_SIRI
131 |         case .siri: return "SiriKit"
132 |         #endif
133 | 
134 |         #if PERMISSION_MEDIA_LIBRARY
135 |         case .mediaLibrary: return "Media Library"
136 |         #endif
137 | 
138 |         case .never: fatalError()
139 |         }
140 |     }
141 | }
142 | 


--------------------------------------------------------------------------------
/Source/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 3 | <plist version="1.0">
 4 | <dict>
 5 | 	<key>CFBundleDevelopmentRegion</key>
 6 | 	<string>en</string>
 7 | 	<key>CFBundleExecutable</key>
 8 | 	<string>$(EXECUTABLE_NAME)</string>
 9 | 	<key>CFBundleIdentifier</key>
10 | 	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11 | 	<key>CFBundleInfoDictionaryVersion</key>
12 | 	<string>6.0</string>
13 | 	<key>CFBundleName</key>
14 | 	<string>$(PRODUCT_NAME)</string>
15 | 	<key>CFBundlePackageType</key>
16 | 	<string>FMWK</string>
17 | 	<key>CFBundleShortVersionString</key>
18 | 	<string>1.0</string>
19 | 	<key>CFBundleSignature</key>
20 | 	<string>????</string>
21 | 	<key>CFBundleVersion</key>
22 | 	<string>$(CURRENT_PROJECT_VERSION)</string>
23 | 	<key>NSPrincipalClass</key>
24 | 	<string></string>
25 | </dict>
26 | </plist>
27 | 


--------------------------------------------------------------------------------
/Source/Supporting Files/Permission.h:
--------------------------------------------------------------------------------
 1 | //
 2 | // Permission.h
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #import <UIKit/UIKit.h>
26 | 
27 | FOUNDATION_EXPORT double PermissionVersionNumber;
28 | FOUNDATION_EXPORT const unsigned char PermissionVersionString[];
29 | 


--------------------------------------------------------------------------------
/Source/Supporting Files/PermissionFlags.xcconfig:
--------------------------------------------------------------------------------
1 | // This hook allows users of the framework to provide build flags without modifying any files within the Permission repository. See README.md for instructions.
2 | #include? "../../PermissionConfiguration.xcconfig"
3 | 
4 | // This secondary hook allows to provide a configuration two levels further up the directory hierarchy. It exists primarily for Carthage support to allow for config files outside of auto-generated directories that may not be under version control.
5 | // Carthage directories are typically structured:
6 | //    "Carthage/Build/iOS/Permission"
7 | #include? "../../../../../PermissionConfiguration.xcconfig"
8 | 


--------------------------------------------------------------------------------
/Source/Supporting Files/Utilities.swift:
--------------------------------------------------------------------------------
  1 | //
  2 | // Utilities.swift
  3 | //
  4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
  5 | //
  6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
  7 | // of this software and associated documentation files (the "Software"), to deal
  8 | // in the Software without restriction, including without limitation the rights
  9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 | // copies of the Software, and to permit persons to whom the Software is
 11 | // furnished to do so, subject to the following conditions:
 12 | //
 13 | // The above copyright notice and this permission notice shall be included in all
 14 | // copies or substantial portions of the Software.
 15 | //
 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22 | // SOFTWARE.
 23 | //
 24 | 
 25 | extension UIApplication {
 26 |     private var topViewController: UIViewController? {
 27 |         var vc = keyWindow?.rootViewController
 28 | 
 29 |         while let presentedVC = vc?.presentedViewController {
 30 |             vc = presentedVC
 31 |         }
 32 | 
 33 |         return vc
 34 |     }
 35 | 
 36 |     func present(_ viewController: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil) {
 37 |         topViewController?.present(viewController, animated: animated, completion: completion)
 38 |     }
 39 | }
 40 | 
 41 | extension Bundle {
 42 |     var name: String {
 43 |         return object(forInfoDictionaryKey: "CFBundleName") as? String ?? ""
 44 |     }
 45 | }
 46 | 
 47 | extension UIControl.State: Hashable {
 48 |     public var hashValue: Int { return Int(rawValue) }
 49 | }
 50 | 
 51 | @propertyWrapper
 52 | struct UserDefault<T> {
 53 |     let key: String
 54 |     let defaultValue: T
 55 | 
 56 |     init(_ key: String, defaultValue: T) {
 57 |         self.key = key
 58 |         self.defaultValue = defaultValue
 59 |     }
 60 | 
 61 |     var wrappedValue: T {
 62 |         get {
 63 |             return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
 64 |         }
 65 |         set {
 66 |             UserDefaults.standard.set(newValue, forKey: key)
 67 |         }
 68 |     }
 69 | }
 70 | 
 71 | struct Defaults {
 72 |     @UserDefault("permission.requestedNotifications", defaultValue: false)
 73 |     static var requestedNotifications: Bool
 74 | 
 75 |     @UserDefault("permission.requestedLocationAlwaysWithWhenInUse", defaultValue: false)
 76 |     static var requestedLocationAlwaysWithWhenInUse: Bool
 77 | 
 78 |     @UserDefault("permission.requestedMotion", defaultValue: false)
 79 |     static var requestedMotion: Bool
 80 | 
 81 |     @UserDefault("permission.requestedBluetooth", defaultValue: false)
 82 |     static var requestedBluetooth: Bool
 83 | 
 84 |     @UserDefault("permission.statusBluetooth", defaultValue: nil)
 85 |     static var statusBluetooth: PermissionStatus?
 86 | 
 87 |     @UserDefault("permission.stateBluetoothManagerDetermined", defaultValue: false)
 88 |     static var stateBluetoothManagerDetermined: Bool
 89 | }
 90 | 
 91 | extension String {
 92 |     static let locationWhenInUseUsageDescription = "NSLocationWhenInUseUsageDescription"
 93 |     static let locationAlwaysUsageDescription    = "NSLocationAlwaysUsageDescription"
 94 |     static let microphoneUsageDescription        = "NSMicrophoneUsageDescription"
 95 |     static let speechRecognitionUsageDescription = "NSSpeechRecognitionUsageDescription"
 96 |     static let photoLibraryUsageDescription      = "NSPhotoLibraryUsageDescription"
 97 |     static let cameraUsageDescription            = "NSCameraUsageDescription"
 98 |     static let mediaLibraryUsageDescription      = "NSAppleMusicUsageDescription"
 99 |     static let siriUsageDescription              = "NSSiriUsageDescription"
100 | }
101 | 
102 | extension Selector {
103 |     static let tapped = #selector(PermissionButton.tapped(_:))
104 |     static let highlight = #selector(PermissionButton.highlight(_:))
105 |     static let settingsHandler = #selector(DeniedAlert.settingsHandler)
106 | }
107 | 
108 | extension OperationQueue {
109 |     convenience init(_ qualityOfService: QualityOfService) {
110 |         self.init()
111 |         self.qualityOfService = qualityOfService
112 |     }
113 | }
114 | 
115 | extension NotificationCenter {
116 |     func addObserver(_ observer: AnyObject, selector: Selector, name: NSNotification.Name?) {
117 |         addObserver(observer, selector: selector, name: name!, object: nil)
118 |     }
119 | 
120 |     func removeObserver(_ observer: AnyObject, name: NSNotification.Name?) {
121 |         removeObserver(observer, name: name, object: nil)
122 |     }
123 | }
124 | 


--------------------------------------------------------------------------------
/Source/Types/Bluetooth.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Bluetooth.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_BLUETOOTH
26 | import CoreBluetooth
27 | 
28 | let BluetoothManager = CBPeripheralManager(
29 |     delegate: Permission.bluetooth,
30 |     queue: nil,
31 |     options: [CBPeripheralManagerOptionShowPowerAlertKey: false]
32 | )
33 | 
34 | extension Permission {
35 |     var statusBluetooth: PermissionStatus {
36 |         switch CBPeripheralManager.authorizationStatus() {
37 |         case .restricted:                 return .disabled
38 |         case .denied:                     return .denied
39 |         case .notDetermined, .authorized: break
40 |         @unknown default:                 return .notDetermined
41 |         }
42 | 
43 |         guard Defaults.stateBluetoothManagerDetermined else { return .notDetermined }
44 | 
45 |         switch BluetoothManager.state {
46 |         case .unsupported, .poweredOff: return .disabled
47 |         case .unauthorized: return .denied
48 |         case .poweredOn: return .authorized
49 |         case .resetting, .unknown:
50 |             return Defaults.statusBluetooth ?? .notDetermined
51 |         @unknown default: return .notDetermined
52 |         }
53 |     }
54 | 
55 |     func requestBluetooth(_ callback: Callback?) {
56 |         Defaults.requestedBluetooth = true
57 | 
58 |         BluetoothManager.request(self)
59 |     }
60 | }
61 | 
62 | extension Permission: CBPeripheralManagerDelegate {
63 |     public func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
64 |         Defaults.stateBluetoothManagerDetermined = true
65 |         Defaults.statusBluetooth = statusBluetooth
66 | 
67 |         guard Defaults.requestedBluetooth else { return }
68 | 
69 |         callback?(statusBluetooth)
70 | 
71 |         Defaults.requestedBluetooth = false
72 |     }
73 | }
74 | 
75 | extension CBPeripheralManager {
76 |     func request(_ permission: Permission) {
77 |         guard case .poweredOn = state else { return }
78 | 
79 |         startAdvertising(nil)
80 |         stopAdvertising()
81 |     }
82 | }
83 | #endif
84 | 


--------------------------------------------------------------------------------
/Source/Types/Camera.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Camera.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_CAMERA
26 | import AVFoundation
27 | 
28 | extension Permission {
29 |     var statusCamera: PermissionStatus {
30 |         let status = AVCaptureDevice.authorizationStatus(for: .video)
31 | 
32 |         switch status {
33 |         case .authorized:          return .authorized
34 |         case .restricted, .denied: return .denied
35 |         case .notDetermined:       return .notDetermined
36 |         @unknown default:          return .notDetermined
37 |         }
38 |     }
39 | 
40 |     func requestCamera(_ callback: @escaping Callback) {
41 |         guard let _ = Bundle.main.object(forInfoDictionaryKey: .cameraUsageDescription) else {
42 |             print("WARNING: \(String.cameraUsageDescription) not found in Info.plist")
43 |             return
44 |         }
45 | 
46 |         AVCaptureDevice.requestAccess(for: .video) { _ in
47 |             callback(self.statusCamera)
48 |         }
49 |     }
50 | }
51 | #endif
52 | 


--------------------------------------------------------------------------------
/Source/Types/Contacts.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Contacts.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_CONTACTS
26 | import Contacts
27 | 
28 | extension Permission {
29 |     var statusContacts: PermissionStatus {
30 |         guard #available(iOS 9.0, *) else { fatalError() }
31 | 
32 |         let status = CNContactStore.authorizationStatus(for: .contacts)
33 | 
34 |         switch status {
35 |         case .authorized:          return .authorized
36 |         case .restricted, .denied: return .denied
37 |         case .notDetermined:       return .notDetermined
38 |         @unknown default:          return .notDetermined
39 |         }
40 |     }
41 | 
42 |     func requestContacts(_ callback: @escaping Callback) {
43 |         guard #available(iOS 9.0, *) else { fatalError() }
44 | 
45 |         CNContactStore().requestAccess(for: .contacts) { _, _ in
46 |             callback(self.statusContacts)
47 |         }
48 |     }
49 | }
50 | #endif
51 | 


--------------------------------------------------------------------------------
/Source/Types/Events.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Events.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_EVENTS
26 | import EventKit
27 | 
28 | extension Permission {
29 |     var statusEvents: PermissionStatus {
30 |         let status = EKEventStore.authorizationStatus(for: .event)
31 | 
32 |         switch status {
33 |         case .authorized:          return .authorized
34 |         case .restricted, .denied: return .denied
35 |         case .notDetermined:       return .notDetermined
36 |         @unknown default:          return .notDetermined
37 |         }
38 |     }
39 | 
40 |     func requestEvents(_ callback: @escaping Callback) {
41 |         EKEventStore().requestAccess(to: .event) { _, _ in
42 |             callback(self.statusEvents)
43 |         }
44 |     }
45 | }
46 | #endif
47 | 


--------------------------------------------------------------------------------
/Source/Types/Location.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Location.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_LOCATION
26 | import CoreLocation
27 | 
28 | let LocationManager = CLLocationManager()
29 | 
30 | private var requestedLocation = false
31 | private var triggerCallbacks  = false
32 | 
33 | extension Permission: CLLocationManagerDelegate {
34 |     public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
35 |         switch (requestedLocation, triggerCallbacks) {
36 |         case (true, false):
37 |             triggerCallbacks = true
38 |         case (true, true):
39 |             requestedLocation = false
40 |             triggerCallbacks  = false
41 |             callbacks(self.status)
42 |         default:
43 |             break
44 |         }
45 |     }
46 | }
47 | 
48 | extension CLLocationManager {
49 |     func request(_ permission: Permission) {
50 |         delegate = permission
51 | 
52 |         requestedLocation = true
53 | 
54 |         switch permission.type {
55 |         case .locationAlways: requestAlwaysAuthorization()
56 |         case .locationWhenInUse: requestWhenInUseAuthorization()
57 |         default: break
58 |         }
59 |     }
60 | }
61 | #endif
62 | 


--------------------------------------------------------------------------------
/Source/Types/LocationAlways.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // LocationAlways.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_LOCATION
26 | import CoreLocation
27 | 
28 | extension Permission {
29 |     var statusLocationAlways: PermissionStatus {
30 |         guard CLLocationManager.locationServicesEnabled() else { return .disabled }
31 | 
32 |         let status = CLLocationManager.authorizationStatus()
33 | 
34 |         switch status {
35 |         case .authorizedAlways: return .authorized
36 |         case .authorizedWhenInUse:
37 |             return Defaults.requestedLocationAlwaysWithWhenInUse ? .denied : .notDetermined
38 |         case .notDetermined: return .notDetermined
39 |         case .restricted, .denied: return .denied
40 |         @unknown default: return .notDetermined
41 |         }
42 |     }
43 | 
44 |     func requestLocationAlways(_ callback: Callback) {
45 |         guard let _ = Foundation.Bundle.main.object(forInfoDictionaryKey: .locationAlwaysUsageDescription) else {
46 |             print("WARNING: \(String.locationAlwaysUsageDescription) not found in Info.plist")
47 |             return
48 |         }
49 | 
50 |         if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
51 |             Defaults.requestedLocationAlwaysWithWhenInUse = true
52 |         }
53 | 
54 |         LocationManager.request(self)
55 |     }
56 | }
57 | #endif
58 | 


--------------------------------------------------------------------------------
/Source/Types/LocationWhenInUse.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // LocationWhenInUse.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_LOCATION
26 | import CoreLocation
27 | 
28 | extension Permission {
29 |     var statusLocationWhenInUse: PermissionStatus {
30 |         guard CLLocationManager.locationServicesEnabled() else { return .disabled }
31 | 
32 |         let status = CLLocationManager.authorizationStatus()
33 | 
34 |         switch status {
35 |         case .authorizedWhenInUse, .authorizedAlways: return .authorized
36 |         case .restricted, .denied:                    return .denied
37 |         case .notDetermined:                          return .notDetermined
38 |         @unknown default:                             return .notDetermined
39 |         }
40 |     }
41 | 
42 |     func requestLocationWhenInUse(_ callback: Callback) {
43 |         guard let _ = Foundation.Bundle.main.object(forInfoDictionaryKey: .locationWhenInUseUsageDescription) else {
44 |             print("WARNING: \(String.locationWhenInUseUsageDescription) not found in Info.plist")
45 |             return
46 |         }
47 | 
48 |         LocationManager.request(self)
49 |     }
50 | }
51 | #endif
52 | 


--------------------------------------------------------------------------------
/Source/Types/MediaLibrary.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // MediaLibrary.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_MEDIA_LIBRARY
26 | import MediaPlayer
27 | 
28 | extension Permission {
29 |     var statusMediaLibrary: PermissionStatus {
30 |         guard #available(iOS 9.3, *) else { fatalError() }
31 | 
32 |         let status = MPMediaLibrary.authorizationStatus()
33 | 
34 |         switch status {
35 |         case .authorized:          return .authorized
36 |         case .restricted, .denied: return .denied
37 |         case .notDetermined:       return .notDetermined
38 |         @unknown default:          return .notDetermined
39 |         }
40 |     }
41 | 
42 |     func requestMediaLibrary(_ callback: @escaping Callback) {
43 |         guard #available(iOS 9.3, *) else { fatalError() }
44 | 
45 |         guard let _ = Bundle.main.object(forInfoDictionaryKey: .mediaLibraryUsageDescription) else {
46 |             print("WARNING: \(String.mediaLibraryUsageDescription) not found in Info.plist")
47 |             return
48 |         }
49 | 
50 |         MPMediaLibrary.requestAuthorization { _ in
51 |             callback(self.statusMediaLibrary)
52 |         }
53 |     }
54 | }
55 | #endif
56 | 


--------------------------------------------------------------------------------
/Source/Types/Microphone.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Microphone.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_MICROPHONE
26 | import AVFoundation
27 | 
28 | extension Permission {
29 |     var statusMicrophone: PermissionStatus {
30 |         let status = AVAudioSession.sharedInstance().recordPermission
31 | 
32 |         switch status {
33 |         case .denied:  return .denied
34 |         case .granted: return .authorized
35 |         default:       return .notDetermined
36 |         }
37 |     }
38 | 
39 |     func requestMicrophone(_ callback: @escaping Callback) {
40 |         AVAudioSession.sharedInstance().requestRecordPermission { _ in
41 |             callback(self.statusMicrophone)
42 |         }
43 |     }
44 | }
45 | #endif
46 | 


--------------------------------------------------------------------------------
/Source/Types/Motion.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Motion.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_MOTION
26 | import CoreMotion
27 | 
28 | private let MotionManager = CMMotionActivityManager()
29 | 
30 | extension Permission {
31 |     var statusMotion: PermissionStatus {
32 |         if Defaults.requestedMotion {
33 |             return synchronousStatusMotion
34 |         }
35 | 
36 |         return .notDetermined
37 |     }
38 | 
39 |     func requestMotion(_ callback: Callback?) {
40 |         Defaults.requestedMotion = true
41 | 
42 |         let now = Date()
43 | 
44 |         MotionManager.queryActivityStarting(from: now, to: now, to: .main) { _, error in
45 |             let status: PermissionStatus
46 | 
47 |             if  let error = error, error._code == Int(CMErrorMotionActivityNotAuthorized.rawValue) {
48 |                 status = .denied
49 |             } else {
50 |                 status = .authorized
51 |             }
52 | 
53 |             MotionManager.stopActivityUpdates()
54 | 
55 |             callback?(status)
56 |         }
57 |     }
58 | 
59 |     private var synchronousStatusMotion: PermissionStatus {
60 |         let semaphore = DispatchSemaphore(value: 0)
61 | 
62 |         var status: PermissionStatus = .notDetermined
63 | 
64 |         let now = Date()
65 | 
66 |         MotionManager.queryActivityStarting(from: now, to: now, to: OperationQueue(.background)) { _, error in
67 |             if  let error = error, error._code == Int(CMErrorMotionActivityNotAuthorized.rawValue) {
68 |                 status = .denied
69 |             } else {
70 |                 status = .authorized
71 |             }
72 | 
73 |             MotionManager.stopActivityUpdates()
74 | 
75 |             semaphore.signal()
76 |         }
77 | 
78 |         _ = semaphore.wait(timeout: .distantFuture)
79 | 
80 |         return status
81 |     }
82 | }
83 | #endif
84 | 


--------------------------------------------------------------------------------
/Source/Types/Notifications.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Notifications.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_NOTIFICATIONS
26 | import UserNotifications
27 | 
28 | extension Permission {
29 |     var statusNotifications: PermissionStatus {
30 |         if Defaults.requestedNotifications {
31 |             return synchronousStatusNotifications
32 |         }
33 | 
34 |         return .notDetermined
35 |     }
36 | 
37 |     func requestNotifications(_ callback: Callback) {
38 |         guard case .notifications(let options) = type else { fatalError() }
39 | 
40 |         UNUserNotificationCenter.current().requestAuthorization(options: options) { granted, error in
41 |             Defaults.requestedNotifications = true
42 | 
43 |             DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
44 |                 self.callbacks(self.statusNotifications)
45 |             }
46 |         }
47 |     }
48 | 
49 |     private var synchronousStatusNotifications: PermissionStatus {
50 |         let semaphore = DispatchSemaphore(value: 0)
51 | 
52 |         var status: PermissionStatus = .notDetermined
53 | 
54 |         UNUserNotificationCenter.current().getNotificationSettings { settings in
55 |             switch settings.authorizationStatus {
56 |             case .authorized: status = .authorized
57 |             case .denied: status = .denied
58 |             case .notDetermined: status = .notDetermined
59 |             case .provisional: status = .authorized
60 |             @unknown default: status = .notDetermined
61 |             }
62 | 
63 |             semaphore.signal()
64 |         }
65 | 
66 |         _ = semaphore.wait(timeout: .distantFuture)
67 | 
68 |         return status
69 |     }
70 | }
71 | #endif
72 | 


--------------------------------------------------------------------------------
/Source/Types/Photos.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Photos.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_PHOTOS
26 | import Photos
27 | 
28 | extension Permission {
29 |     var statusPhotos: PermissionStatus {
30 |         let status = PHPhotoLibrary.authorizationStatus()
31 | 
32 |         switch status {
33 |         case .authorized:          return .authorized
34 |         case .denied, .restricted: return .denied
35 |         case .notDetermined:       return .notDetermined
36 |         @unknown default:          return .notDetermined
37 |         }
38 |     }
39 | 
40 |     func requestPhotos(_ callback: @escaping Callback) {
41 |         guard let _ = Bundle.main.object(forInfoDictionaryKey: .photoLibraryUsageDescription) else {
42 |             print("WARNING: \(String.photoLibraryUsageDescription) not found in Info.plist")
43 |             return
44 |         }
45 | 
46 |         PHPhotoLibrary.requestAuthorization { _ in
47 |             callback(self.statusPhotos)
48 |         }
49 |     }
50 | }
51 | #endif
52 | 


--------------------------------------------------------------------------------
/Source/Types/Reminders.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Reminders.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_REMINDERS
26 | import EventKit
27 | 
28 | extension Permission {
29 |     var statusReminders: PermissionStatus {
30 |         let status = EKEventStore.authorizationStatus(for: .reminder)
31 | 
32 |         switch status {
33 |         case .authorized:          return .authorized
34 |         case .restricted, .denied: return .denied
35 |         case .notDetermined:       return .notDetermined
36 |         @unknown default:          return .notDetermined
37 |         }
38 |     }
39 | 
40 |     func requestReminders(_ callback: @escaping Callback) {
41 |         EKEventStore().requestAccess(to: .reminder) { _, _ in
42 |             callback(self.statusReminders)
43 |         }
44 |     }
45 | }
46 | #endif
47 | 


--------------------------------------------------------------------------------
/Source/Types/Siri.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // Siri.swift
 3 | //
 4 | // Copyright (c) 2015-2017 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_SIRI
26 | import Intents
27 | 
28 | extension Permission {
29 |     var statusSiri: PermissionStatus {
30 |         guard #available(iOS 10.0, *) else { fatalError() }
31 | 
32 |         let status = INPreferences.siriAuthorizationStatus()
33 | 
34 |         switch status {
35 |         case .authorized:          return .authorized
36 |         case .restricted, .denied: return .denied
37 |         case .notDetermined:       return .notDetermined
38 |         @unknown default:          return .notDetermined
39 |         }
40 |     }
41 | 
42 |     func requestSiri(_ callback: @escaping Callback) {
43 |         guard #available(iOS 10.0, *) else { fatalError() }
44 | 
45 |         guard let _ = Bundle.main.object(forInfoDictionaryKey: .siriUsageDescription) else {
46 |             print("WARNING: \(String.siriUsageDescription) not found in Info.plist")
47 |             return
48 |         }
49 | 
50 |         INPreferences.requestSiriAuthorization { _ in
51 |             callback(self.statusSiri)
52 |         }
53 |     }
54 | }
55 | #endif
56 | 


--------------------------------------------------------------------------------
/Source/Types/SpeechRecognizer.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // SpeechRecognizer.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | #if PERMISSION_SPEECH_RECOGNIZER
26 | import Speech
27 | 
28 | extension Permission {
29 |     var statusSpeechRecognizer: PermissionStatus {
30 |         guard #available(iOS 10.0, *) else { fatalError() }
31 | 
32 |         let status = SFSpeechRecognizer.authorizationStatus()
33 | 
34 |         switch status {
35 |         case .authorized:          return .authorized
36 |         case .restricted, .denied: return .denied
37 |         case .notDetermined:       return .notDetermined
38 |         @unknown default:          return .notDetermined
39 |         }
40 |     }
41 | 
42 |     func requestSpeechRecognizer(_ callback: @escaping Callback) {
43 |         guard #available(iOS 10.0, *) else { fatalError() }
44 | 
45 |         guard let _ = Bundle.main.object(forInfoDictionaryKey: .microphoneUsageDescription) else {
46 |             print("WARNING: \(String.microphoneUsageDescription) not found in Info.plist")
47 |             return
48 |         }
49 | 
50 |         guard let _ = Bundle.main.object(forInfoDictionaryKey: .speechRecognitionUsageDescription) else {
51 |             print("WARNING: \(String.speechRecognitionUsageDescription) not found in Info.plist")
52 |             return
53 |         }
54 | 
55 |         SFSpeechRecognizer.requestAuthorization { _ in
56 |             callback(self.statusSpeechRecognizer)
57 |         }
58 |     }
59 | }
60 | #endif
61 | 


--------------------------------------------------------------------------------
/Tests/PermissionTests.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | // PermissionTests.swift
 3 | //
 4 | // Copyright (c) 2015-2019 Damien (http://delba.io)
 5 | //
 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | // of this software and associated documentation files (the "Software"), to deal
 8 | // in the Software without restriction, including without limitation the rights
 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | 
25 | import XCTest
26 | 
27 | class PermissionTests: XCTestCase {
28 | 
29 | }
30 | 


--------------------------------------------------------------------------------
/Tests/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 3 | <plist version="1.0">
 4 | <dict>
 5 | 	<key>CFBundleDevelopmentRegion</key>
 6 | 	<string>en</string>
 7 | 	<key>CFBundleExecutable</key>
 8 | 	<string>$(EXECUTABLE_NAME)</string>
 9 | 	<key>CFBundleIdentifier</key>
10 | 	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11 | 	<key>CFBundleInfoDictionaryVersion</key>
12 | 	<string>6.0</string>
13 | 	<key>CFBundleName</key>
14 | 	<string>$(PRODUCT_NAME)</string>
15 | 	<key>CFBundlePackageType</key>
16 | 	<string>BNDL</string>
17 | 	<key>CFBundleShortVersionString</key>
18 | 	<string>1.0</string>
19 | 	<key>CFBundleSignature</key>
20 | 	<string>????</string>
21 | 	<key>CFBundleVersion</key>
22 | 	<string>1</string>
23 | </dict>
24 | </plist>
25 | 


--------------------------------------------------------------------------------