I'm trying to get photo from gallery for sending it to api. When I call AttachNewFileClick()
, black screen appears.
@IBAction func AttachNewFileClick(_ sender: Any) {
if checkPhotoAccess()==true {
let imagePicker = UIImagePickerController(rootViewController: self)
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
imagePicker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
present(imagePicker, animated: true, completion: nil)
}
}
func checkPhotoAccess() -> Bool {
let status:PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus();
switch status {
case PHAuthorizationStatus.notDetermined:
PHPhotoLibrary.requestAuthorization({status in
return
})
return checkPhotoAccess()
case .denied:
return false
case .restricted:
let permissionRequestAlert:UIAlertView = UIAlertView(title: "Доступ к галлерее заблокирован", message: "Приложение не может получить доступ к вашей галлерее для прикрепления фото. Вероятно ранее вы заблокировали эту возможность", delegate: self, cancelButtonTitle: "Отмена", otherButtonTitles: "Открыть настройки")
permissionRequestAlert.tag = PERMISSION_REQUEST_ALERT_TAG
permissionRequestAlert.show()
return false
case .authorized:
return true
}
}
Then I added breakpoint to all exception and got this piece of code highlighted before exception being thrown
libobjc.A.dylib`objc_exception_throw: -> 0x1159b0f11 <+0>: pushq %rbp
and exception in log:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSDictionaryM setObject:forKey:]: key cannot be nil'
I run app in simulator iPhone SE (11.2 Xcode 9.2). Project min iOS version is 8.2. UIImagePickerController
is called in NavigationViewController
that was showed modally.
How this problem can be solved?
The reason you are crashing is this line:
let imagePicker = UIImagePickerController(rootViewController: self)
That is not how you create a UIImagePickerController. Just create it! It already has a root view controller of its own; do not attempt to change it. Just say:
let imagePicker = UIImagePickerController()
Also, your checkPhotoAccess
code is fatally flawed:
func checkPhotoAccess() -> Bool {
let status:PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus();
switch status {
case PHAuthorizationStatus.notDetermined:
PHPhotoLibrary.requestAuthorization({status in
return
})
return checkPhotoAccess()
case .denied:
return false
case .restricted:
let permissionRequestAlert:UIAlertView = UIAlertView(title: "Доступ к галлерее заблокирован", message: "Приложение не может получить доступ к вашей галлерее для прикрепления фото. Вероятно ранее вы заблокировали эту возможность", delegate: self, cancelButtonTitle: "Отмена", otherButtonTitles: "Открыть настройки")
permissionRequestAlert.tag = PERMISSION_REQUEST_ALERT_TAG
permissionRequestAlert.show()
return false
case .authorized:
return true
}
}
To see why, let's eliminate everything except the notDetermined
case:
func checkPhotoAccess() -> Bool {
PHPhotoLibrary.requestAuthorization({status in
return
})
return checkPhotoAccess()
}
Now think about the order in which those lines will execute. requestAuthorization
is asynchronous. Therefore the order of execution is like this:
func checkPhotoAccess() -> Bool {
PHPhotoLibrary.requestAuthorization({status in
return // 2
})
return checkPhotoAccess() // 1
}
Okay, but then 2
will never execute, because 1
returns first. So we are left with this:
func checkPhotoAccess() -> Bool {
return checkPhotoAccess()
}
But that is clearly insane. We have an infinite recursion.
The takeaway is that you cannot return a value from checkPhotoAccess
, because you need to call requestAuthorization
which is asynchronous. The correct way to implement this sort of thing is either to return false
in the .notDetermined
case, or else (better) to write a checkPhotoAccess
method that receives a completion function and that can then call that function if it discovers that we have authorization.
Also you need to stop using UIAlertView immediately and convert to UIAlertController. UIAlertView was deprecated long ago. The compiler is warning you about this, so listen to what it's telling you!