diff --git a/Assets/Permissions/faceid.png b/Assets/Permissions/faceid.png new file mode 100644 index 00000000..5d0e6bdb Binary files /dev/null and b/Assets/Permissions/faceid.png differ diff --git a/Assets/Permissions/media.png b/Assets/Permissions/music.png similarity index 100% rename from Assets/Permissions/media.png rename to Assets/Permissions/music.png diff --git a/Assets/Sketch/sppermissions-v6.sketch b/Assets/Sketch/sppermissions-v6.sketch index 438b7145..aad73676 100644 Binary files a/Assets/Sketch/sppermissions-v6.sketch and b/Assets/Sketch/sppermissions-v6.sketch differ diff --git a/Example Apps/Dependencies/Package.swift b/Example Apps/Dependencies/Package.swift index da624f43..cf7741e9 100644 --- a/Example Apps/Dependencies/Package.swift +++ b/Example Apps/Dependencies/Package.swift @@ -39,9 +39,10 @@ let package = Package( .product(name: "SPPermissionsLocationWhenInUse", package: "SPPermissions"), .product(name: "SPPermissionsLocationAlways", package: "SPPermissions"), .product(name: "SPPermissionsMotion", package: "SPPermissions"), - .product(name: "SPPermissionsMediaLibrary", package: "SPPermissions"), + .product(name: "SPPermissionsMusic", package: "SPPermissions"), .product(name: "SPPermissionsBluetooth", package: "SPPermissions"), - .product(name: "SPPermissionsTracking", package: "SPPermissions") + .product(name: "SPPermissionsTracking", package: "SPPermissions"), + .product(name: "SPPermissionsFaceID", package: "SPPermissions") ] ), ] diff --git a/Example Apps/SPPermissions.xcodeproj/project.xcworkspace/xcuserdata/ivanvorobei.xcuserdatad/UserInterfaceState.xcuserstate b/Example Apps/SPPermissions.xcodeproj/project.xcworkspace/xcuserdata/ivanvorobei.xcuserdatad/UserInterfaceState.xcuserstate index b58466e3..d6d0573f 100644 Binary files a/Example Apps/SPPermissions.xcodeproj/project.xcworkspace/xcuserdata/ivanvorobei.xcuserdatad/UserInterfaceState.xcuserstate and b/Example Apps/SPPermissions.xcodeproj/project.xcworkspace/xcuserdata/ivanvorobei.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Example Apps/iOS Example/Scenes/RootController.swift b/Example Apps/iOS Example/Scenes/RootController.swift index 36e60763..7793c28e 100644 --- a/Example Apps/iOS Example/Scenes/RootController.swift +++ b/Example Apps/iOS Example/Scenes/RootController.swift @@ -34,13 +34,14 @@ import SPPermissionsSpeechRecognizer import SPPermissionsLocationWhenInUse import SPPermissionsLocationAlways import SPPermissionsMotion -import SPPermissionsMediaLibrary +import SPPermissionsMusic import SPPermissionsBluetooth import SPPermissionsTracking +import SPPermissionsFaceID class RootController: SPTableViewController { - var availablePermissions: [SPPermissions.Permission] = [.camera, .photoLibrary, .notification, .microphone, .calendar, .contacts, .reminders, .speech, .locationWhenInUse, .locationAlways, .motion, .mediaLibrary, .bluetooth, /*.tracking*/] + var availablePermissions: [SPPermissions.Permission] = [.camera, .photoLibrary, .notification, .microphone, .calendar, .contacts, .reminders, .speech, .locationWhenInUse, .locationAlways, .motion, .mediaLibrary, .bluetooth, /*.tracking,*/ .faceID] var selectedPermissions: [SPPermissions.Permission] = [] diff --git a/Package.swift b/Package.swift index 85ba317f..49e3b13d 100644 --- a/Package.swift +++ b/Package.swift @@ -56,8 +56,8 @@ let package = Package( targets: ["SPPermissionsMotion"] ), .library( - name: "SPPermissionsMediaLibrary", - targets: ["SPPermissionsMediaLibrary"] + name: "SPPermissionsMusic", + targets: ["SPPermissionsMusic"] ), .library( name: "SPPermissionsBluetooth", @@ -67,6 +67,10 @@ let package = Package( name: "SPPermissionsTracking", targets: ["SPPermissionsTracking"] ), + .library( + name: "SPPermissionsFaceID", + targets: ["SPPermissionsFaceID"] + ), ], dependencies: [], targets: [ @@ -168,10 +172,10 @@ let package = Package( ] ), .target( - name: "SPPermissionsMediaLibrary", + name: "SPPermissionsMusic", dependencies: [.target(name: "SPPermissions")], swiftSettings: [ - .define("SPPERMISSIONS_MEDIALIBRARY"), + .define("SPPERMISSIONS_MUSIC"), .define("SPPERMISSIONS_SPM") ] ), @@ -190,6 +194,14 @@ let package = Package( .define("SPPERMISSIONS_TRACKING"), .define("SPPERMISSIONS_SPM") ] + ), + .target( + name: "SPPermissionsFaceID", + dependencies: [.target(name: "SPPermissions")], + swiftSettings: [ + .define("SPPERMISSIONS_FACEID"), + .define("SPPERMISSIONS_SPM") + ] ) ], swiftLanguageVersions: [.v5] diff --git a/README.md b/README.md index c249a28e..16e28f85 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,11 @@ If you like the project, don't forget to `put star ★` and follow me on GitHub: - + +

## Navigate @@ -123,7 +124,7 @@ pod 'SPPermissions/LocationAlways' pod 'SPPermissions/Motion' ``` ```ruby -pod 'SPPermissions/MediaLibrary' +pod 'SPPermissions/Music' ``` ```ruby pod 'SPPermissions/Bluetooth' @@ -131,6 +132,9 @@ pod 'SPPermissions/Bluetooth' ```ruby pod 'SPPermissions/Tracking' ``` +```ruby +pod 'SPPermissions/FaceID' +```

@@ -350,6 +354,7 @@ List of keys: - NSBluetoothAlwaysUsageDescription - NSBluetoothPeripheralUsageDescription (iOS 12 and earlier) - NSUserTrackingUsageDescription +- NSFaceIDUsageDescription Do not use the description as the name of the key. diff --git a/SPPermissions.podspec b/SPPermissions.podspec index 0f095661..05e3b44b 100644 --- a/SPPermissions.podspec +++ b/SPPermissions.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "SPPermissions" - s.version = "6.4.1" + s.version = "6.4.4" s.summary = "Ask permissions on Swift. Available List, Dialog & Native interface. Can check state permission." s.homepage = "https://github.com/ivanvorobei/SPPermissions" s.source = { :git => "https://github.com/ivanvorobei/SPPermissions.git", :tag => s.version } @@ -119,11 +119,11 @@ Pod::Spec.new do |s| } end - s.subspec 'MediaLibrary' do |subspec| + s.subspec 'Music' do |subspec| subspec.dependency 'SPPermissions/Core' - subspec.source_files = "Sources/SPPermissionsMediaLibrary/**/*.swift" + subspec.source_files = "Sources/SPPermissionsMusic/**/*.swift" subspec.pod_target_xcconfig = { - "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "SPPERMISSIONS_MEDIALIBRARY SPPERMISSIONS_COCOAPODS" + "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "SPPERMISSIONS_Music SPPERMISSIONS_COCOAPODS" } end diff --git a/Sources/SPPermissions/Interface/Shared/SPPermissionsDrawIconView.swift b/Sources/SPPermissions/Interface/Shared/SPPermissionsDrawIconView.swift index f5393049..fb0f8187 100644 --- a/Sources/SPPermissions/Interface/Shared/SPPermissionsDrawIconView.swift +++ b/Sources/SPPermissions/Interface/Shared/SPPermissionsDrawIconView.swift @@ -79,6 +79,9 @@ public class SPPermissionsDrawIconView: UIView { Draw.drawBluetooth(frame: rect, resizing: .aspectFit, color: tintColor) case .tracking: Draw.drawTracking(frame: rect, resizing: .aspectFit, color: tintColor) + case .faceID: + // Not implemented old style icons. + break case .none: break } diff --git a/Sources/SPPermissions/Resources/Assets.xcassets/Permissions/FaceID.imageset/Contents.json b/Sources/SPPermissions/Resources/Assets.xcassets/Permissions/FaceID.imageset/Contents.json index 0889b7ac..41ce4b27 100644 --- a/Sources/SPPermissions/Resources/Assets.xcassets/Permissions/FaceID.imageset/Contents.json +++ b/Sources/SPPermissions/Resources/Assets.xcassets/Permissions/FaceID.imageset/Contents.json @@ -1,21 +1,15 @@ { "images" : [ { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "FaceID@3x.png", - "idiom" : "universal", - "scale" : "3x" + "filename" : "FaceID.pdf", + "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/Sources/SPPermissions/Resources/Assets.xcassets/Permissions/FaceID.imageset/FaceID.pdf b/Sources/SPPermissions/Resources/Assets.xcassets/Permissions/FaceID.imageset/FaceID.pdf new file mode 100644 index 00000000..c39fc153 Binary files /dev/null and b/Sources/SPPermissions/Resources/Assets.xcassets/Permissions/FaceID.imageset/FaceID.pdf differ diff --git a/Sources/SPPermissions/Resources/Assets.xcassets/Permissions/FaceID.imageset/FaceID@3x.png b/Sources/SPPermissions/Resources/Assets.xcassets/Permissions/FaceID.imageset/FaceID@3x.png deleted file mode 100644 index 5b8ccf53..00000000 Binary files a/Sources/SPPermissions/Resources/Assets.xcassets/Permissions/FaceID.imageset/FaceID@3x.png and /dev/null differ diff --git a/Sources/SPPermissions/Resources/Localization/en.lproj/Localizable.strings b/Sources/SPPermissions/Resources/Localization/en.lproj/Localizable.strings index 3f4c5d6c..d4e27445 100644 --- a/Sources/SPPermissions/Resources/Localization/en.lproj/Localizable.strings +++ b/Sources/SPPermissions/Resources/Localization/en.lproj/Localizable.strings @@ -66,6 +66,10 @@ "permission tracking description" = "Allow to access app-related data"; +"permission faceid name" = "FaceID"; + +"permission faceid description" = "Allow using Face ID identity."; + "denied alert title" = "Permission denied"; "denied alert description" = "Please, go to Settings and allow permission."; diff --git a/Sources/SPPermissions/SPPermissions.swift b/Sources/SPPermissions/SPPermissions.swift index 11304bd9..3c5e78c0 100644 --- a/Sources/SPPermissions/SPPermissions.swift +++ b/Sources/SPPermissions/SPPermissions.swift @@ -149,6 +149,7 @@ public enum SPPermissions { case authorized case denied case notDetermined + case notSupported } /** @@ -172,6 +173,7 @@ public enum SPPermissions { case mediaLibrary = 12 case bluetooth = 13 case tracking = 14 + case faceID = 15 public var name: String { switch self { @@ -203,6 +205,8 @@ public enum SPPermissions { return "Location When Use" case .tracking: return "Tracking" + case .faceID: + return "FaceID" } } } diff --git a/Sources/SPPermissions/Services/Images.swift b/Sources/SPPermissions/Services/Images.swift index 0e6b69c0..47fcce33 100644 --- a/Sources/SPPermissions/Services/Images.swift +++ b/Sources/SPPermissions/Services/Images.swift @@ -53,6 +53,8 @@ enum Images { return UIImage.init(named: "Location", in: bundle, compatibleWith: nil) ?? UIImage() case .tracking: return UIImage.init(named: "Tracking", in: bundle, compatibleWith: nil) ?? UIImage() + case .faceID: + return UIImage.init(named: "FaceID", in: bundle, compatibleWith: nil) ?? UIImage() } } diff --git a/Sources/SPPermissions/Services/Text.swift b/Sources/SPPermissions/Services/Text.swift index c8fa4542..1c1ee645 100644 --- a/Sources/SPPermissions/Services/Text.swift +++ b/Sources/SPPermissions/Services/Text.swift @@ -53,6 +53,8 @@ enum Texts { return NSLocalizedString("permission location always name", bundle: bundle, comment: "") case .tracking: return NSLocalizedString("permission tracking name", bundle: bundle, comment: "") + case .faceID: + return NSLocalizedString("permission faceid name", bundle: bundle, comment: "") } } @@ -86,6 +88,8 @@ enum Texts { return NSLocalizedString("permission location always description", bundle: bundle, comment: "") case .tracking: return NSLocalizedString("permission tracking description", bundle: bundle, comment: "") + case .faceID: + return NSLocalizedString("permission faceid description", bundle: bundle, comment: "") } } diff --git a/Sources/SPPermissionsFaceID/SPFaceIDPermission.swift b/Sources/SPPermissionsFaceID/SPFaceIDPermission.swift new file mode 100644 index 00000000..fe2be223 --- /dev/null +++ b/Sources/SPPermissionsFaceID/SPFaceIDPermission.swift @@ -0,0 +1,74 @@ +// The MIT License (MIT) +// Copyright © 2020 Ivan Vorobei (hello@ivanvorobei.by) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if SPPERMISSIONS_SPM +import SPPermissions +#endif + +#if os(iOS) && SPPERMISSIONS_FACEID + +import Foundation +import LocalAuthentication + +public extension SPPermissions.Permission { + + static var faceID: SPFaceIDPermission { + return SPFaceIDPermission() + } +} + +public class SPFaceIDPermission: SPPermissions.Permission { + + open override var type: SPPermissions.PermissionType { .faceID } + open var usageDescriptionKey: String? { "NSFaceIDUsageDescription" } + + public override var status: SPPermissions.PermissionStatus { + let context = LAContext() + + var error: NSError? + let isReady = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) + + guard context.biometryType == .faceID else { + return .notSupported + } + + switch error?.code { + case nil where isReady: + return .notDetermined + case LAError.biometryNotAvailable.rawValue: + return .denied + case LAError.biometryNotEnrolled.rawValue: + return .notSupported + default: + return .notSupported + } + } + + public override func request(completion: @escaping () -> Void) { + LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: " ") { _, _ in + DispatchQueue.main.async { + completion() + } + } + } +} + +#endif diff --git a/Sources/SPPermissionsMediaLibrary/SPMediaLibraryPermission.swift b/Sources/SPPermissionsMusic/SPMediaLibraryPermission.swift similarity index 98% rename from Sources/SPPermissionsMediaLibrary/SPMediaLibraryPermission.swift rename to Sources/SPPermissionsMusic/SPMediaLibraryPermission.swift index af2b7f21..7e828d69 100644 --- a/Sources/SPPermissionsMediaLibrary/SPMediaLibraryPermission.swift +++ b/Sources/SPPermissionsMusic/SPMediaLibraryPermission.swift @@ -23,7 +23,7 @@ import SPPermissions #endif -#if os(iOS) && SPPERMISSIONS_MEDIALIBRARY +#if os(iOS) && SPPERMISSIONS_MUSIC import Foundation import MediaPlayer