Library for working with iOS permissions, from idea to release (part 1)

Hello! In this mini-series of articles, you will learn:





  • How to inherit a Swift class not entirely, but only what you need in it?





  • How do you let the user of your CocoaPods or Carthage library compile only those parts of it that they actually use?





  • How to rip apart iOS resources to get specific system icons and localized strings from there?





  • How to support completion blocks even where it is not provided by the default system permission API?





, , iOS — . , !





— . ? . ? .





Apple, Stack Overflow. , , - .





GitHub , , . , — , - , .





. . , , . , PermissionWizard...





  • iOS 14 macOS 11 Big Sur





  • Mac Catalyst









  • «Info.plist» , -





  • , API





  • , -   , , , DispatchQueue.main







  • Swift





  • API ,





  • UI





  • , ,





- ...





, , ?

PermissionWizard, , :





  • usageDescriptionPlistKey







  • checkStatus



    requestAccess







, , , .





, , , Swift Xcode , —    .





, :





  • (, ) , , checkStatus



    . — , .





  • requestAccess(completion:)



    , , , . requestAccess(whenInUseOnly:completion:)



    , - , .





  • plist- — (NSPhotoLibraryUsageDescription) , (NSPhotoLibraryAddUsageDescription). , - usageDescriptionPlistKey



    — .





. . , 18 , , .





-. , . , — .





class SupportedType {
    func requestAccess(completion: (Status) -> Void) { }
}

final class Bluetooth: SupportedType { ... }

final class Location: SupportedType {
    @available(*, unavailable)
    override func requestAccess(completion: (Status) -> Void) { }
    
    func requestAccess(whenInUseOnly: Bool, completion: (Status) -> Void) { ... }
}
      
      



, @available(*, unavailable)



, , , Xcode, .





, , , .





CocoaPods- Carthage- , ?

PermissionWizard 18 — Siri iOS 14 . , AVKit, CoreBluetooth, CoreLocation, CoreMotion, EventKit, HealthKit, HomeKit .





, , - , Apple App Store, , API . , — . - .





CocoaPods

. , , . , .





pod 'PermissionWizard/Assets' # Icons and localized strings
pod 'PermissionWizard/Bluetooth'
pod 'PermissionWizard/Calendars'
pod 'PermissionWizard/Camera'
pod 'PermissionWizard/Contacts'
pod 'PermissionWizard/FaceID'
pod 'PermissionWizard/Health'
pod 'PermissionWizard/Home'
pod 'PermissionWizard/LocalNetwork'
pod 'PermissionWizard/Location'
pod 'PermissionWizard/Microphone'
pod 'PermissionWizard/Motion'
pod 'PermissionWizard/Music'
pod 'PermissionWizard/Notifications'
pod 'PermissionWizard/Photos'
pod 'PermissionWizard/Reminders'
pod 'PermissionWizard/Siri'
pod 'PermissionWizard/SpeechRecognition'
pod 'PermissionWizard/Tracking'
      
      



, «Podspec» (, CocoaPods) :





Pod::Spec.new do |spec|
  
  ...
  
  spec.subspec 'Core' do |core|
    core.source_files = 'Source/Permission.swift', 'Source/Framework'
  end
  
  spec.subspec 'Assets' do |assets|
    assets.dependency 'PermissionWizard/Core'
    assets.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS' => 'ASSETS' }
    
    assets.resource_bundles = {
      'Icons' => 'Source/Icons.xcassets',
      'Localizations' => 'Source/Localizations/*.lproj'
    }
  end
  
  spec.subspec 'Bluetooth' do |bluetooth|
    bluetooth.dependency 'PermissionWizard/Core'
    bluetooth.pod_target_xcconfig = { 'SWIFT_ACTIVE_COMPILATION_CONDITIONS' => 'BLUETOOTH' }
    bluetooth.source_files = 'Source/Supported Types/Bluetooth*.swift'
  end
  
  ...
  
  spec.default_subspec = 'Assets', 'Bluetooth', 'Calendars', 'Camera', 'Contacts', 'FaceID', 'Health', 'Home', 'LocalNetwork', 'Location', 'Microphone', 'Motion', 'Music', 'Notifications', 'Photos', 'Reminders', 'Siri', 'SpeechRecognition', 'Tracking'
  
end
      
      



, , .





#if BLUETOOTH
    final class Bluetooth { ... }
#endif
      
      



Carthage

. , - — , . , - .





«Settings.xcconfig» :





#include? "../../../../PermissionWizard.xcconfig"
      
      



Carthage «Carthage/Build/iOS», «PermissionWizard.xcconfig», .





:





ENABLED_FEATURES = ASSETS BLUETOOTH ...
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(ENABLED_FEATURES) CUSTOM_SETTINGS
      
      



, , «Settings.xcconfig» . , , «project.pbxproj» . , , .





A53DFF50255AAB8200995A85 /* Settings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Settings.xcconfig; sourceTree = "<group>"; };
      
      



«XCBuildConfiguration» ( 3):





B6DAF0412528D771002483A6 /* Release */ = {
		isa = XCBuildConfiguration;
		baseConfigurationReference = A53DFF50255AAB8200995A85 /* Settings.xcconfig */;
		buildSettings = {
				...
		};
		name = Release;
};
      
      



, CUSTOM_SETTINGS



. — , , «PermissionWizard.xcconfig» , .





#if BLUETOOTH || !CUSTOM_SETTINGS
    final class Bluetooth { ... }
#endif
      
      



That's all for now

In the next part, we'll talk about how I found the localized strings I needed among the 5 gigabytes of iOS 14 and how I got the icons of all system permissions. And I'll also tell you how I managed to file it requestAccess(completion:)



even where the default system permission API does not support callbacks.





Thanks for attention!








All Articles