We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
iOS Camera take_photo
### Describe the bug

When using the documented:

```python3
if await app.camera.request_permission():
    photo = await app.camera.take_photo()
```

### Steps to reproduce

1. request permission to use camera
2. await the take_photo function.


### Expected behavior

Return a toga.Image or None as documented here https://toga.readthedocs.io/en/stable/reference/api/hardware/camera.html#toga.hardware.camera.Camera.take_photo

### Screenshots

Successfully get permission to use camera: <img width="378" alt="Screenshot 2024-02-08 at 22 14 03" src="https://github.com/beeware/toga/assets/25908768/5867a18e-b5c5-4141-bd72-a4b737c0fbd1">

No camera screen comes up.


### Environment

- Operating System: iOS 16.4 & 17.2 (built and development from Macbook Air M1 macOS 14.3.1)
- Python version: 3.10.2
- Software versions:
  - Briefcase: 0.3.17
  - Toga: 4.0.2


### Logs

Simulator Log
```
Error in async handler: 'NoneType' object has no attribute 'result'
Traceback (most recent call last):
  File "/Users/j/Library/Developer/CoreSimulator/Devices/6EAED83E-F2F7-4497-8F74-52F3DC368DA2/data/Containers/Bundle/Application/E864BC86-AFF3-410A-A4CF-D594ADB4AFCA/Health App.app/app_packages/toga/handlers.py", line 38, in handler_with_cleanup
    result = await handler(interface, *args, **kwargs)
  File "/Users/j/Library/Developer/CoreSimulator/Devices/6EAED83E-F2F7-4497-8F74-52F3DC368DA2/data/Containers/Bundle/Application/E864BC86-AFF3-410A-A4CF-D594ADB4AFCA/Health App.app/app/healthapp/app.py", line 71, in analyse_gait_handler
    photo = await self.app.camera.take_photo()
  File "/Users/j/Library/Developer/CoreSimulator/Devices/6EAED83E-F2F7-4497-8F74-52F3DC368DA2/data/Containers/Bundle/Application/E864BC86-AFF3-410A-A4CF-D594ADB4AFCA/Health App.app/app_packages/toga/hardware/camera.py", line 122, in take_photo
    self._impl.take_photo(photo, device=device, flash=flash)
  File "/Users/j/Library/Developer/CoreSimulator/Devices/6EAED83E-F2F7-4497-8F74-52F3DC368DA2/data/Containers/Bundle/Application/E864BC86-AFF3-410A-A4CF-D594ADB4AFCA/Health App.app/app_packages/toga_iOS/hardware/camera.py", line 159, in take_photo
    self.native.delegate.result = result
AttributeError: 'NoneType' object has no attribute 'result'
```


Physical iOS device testing log:
```
2024-02-08 22:27:03.097595+0000 Health App[2216:513956] [TraitCollection] Class CKBrowserSwitcherViewController overrides the -traitCollection getter, which is not supported. If you're trying to override traits, you must use the appropriate API.
2024-02-08 22:27:03.179852+0000 Health App[2216:513956] Error in async handler: 'NoneType' object has no attribute 'result'

2024-02-08 22:27:03.183351+0000 Health App[2216:513956] Traceback (most recent call last):

2024-02-08 22:27:03.183705+0000 Health App[2216:513956]   File "/private/var/containers/Bundle/Application/22F0E7F3-012A-46ED-93DE-C8EC8125EE19/Health App.app/app_packages/toga/handlers.py", line 38, in handler_with_cleanup

2024-02-08 22:27:03.183927+0000 Health App[2216:513956]     result = await handler(interface, *args, **kwargs)

2024-02-08 22:27:03.184161+0000 Health App[2216:513956]   File "/private/var/containers/Bundle/Application/22F0E7F3-012A-46ED-93DE-C8EC8125EE19/Health App.app/app/healthapp/app.py", line 71, in analyse_gait_handler

2024-02-08 22:27:03.184361+0000 Health App[2216:513956]     photo = await self.app.camera.take_photo()

2024-02-08 22:27:03.184604+0000 Health App[2216:513956]   File "/private/var/containers/Bundle/Application/22F0E7F3-012A-46ED-93DE-C8EC8125EE19/Health App.app/app_packages/toga/hardware/camera.py", line 122, in take_photo

2024-02-08 22:27:03.184827+0000 Health App[2216:513956]     self._impl.take_photo(photo, device=device, flash=flash)

2024-02-08 22:27:03.185124+0000 Health App[2216:513956]   File "/private/var/containers/Bundle/Application/22F0E7F3-012A-46ED-93DE-C8EC8125EE19/Health App.app/app_packages/toga_iOS/hardware/camera.py", line 159, in take_photo

2024-02-08 22:27:03.185537+0000 Health App[2216:513956]     self.native.delegate.result = result

2024-02-08 22:27:03.185785+0000 Health App[2216:513956] AttributeError: 'NoneType' object has no attribute 'result'
```

### Additional context

Fully works on android, same problem on macOS I believe.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `iOS/src/toga_iOS/hardware/camera.py`
Content:
```
1 import warnings
2 
3 from rubicon.objc import Block, NSObject, objc_method
4 
5 import toga
6 from toga.constants import FlashMode
7 
8 # for classes that need to be monkeypatched for testing
9 from toga_iOS import libs as iOS
10 from toga_iOS.libs import (
11     AVAuthorizationStatus,
12     AVMediaTypeVideo,
13     NSBundle,
14     UIImagePickerControllerCameraCaptureMode,
15     UIImagePickerControllerCameraDevice,
16     UIImagePickerControllerCameraFlashMode,
17     UIImagePickerControllerSourceTypeCamera,
18 )
19 
20 
21 class CameraDevice:
22     def __init__(self, id, name, native):
23         self._id = id
24         self._name = name
25         self.native = native
26 
27     def id(self):
28         return self._id
29 
30     def name(self):
31         return self._name
32 
33     def has_flash(self):
34         return iOS.UIImagePickerController.isFlashAvailableForCameraDevice(self.native)
35 
36 
37 def native_flash_mode(flash):
38     return {
39         FlashMode.ON: UIImagePickerControllerCameraFlashMode.On,
40         FlashMode.OFF: UIImagePickerControllerCameraFlashMode.Off,
41     }.get(flash, UIImagePickerControllerCameraFlashMode.Auto)
42 
43 
44 # def native_video_quality(quality):
45 #     return {
46 #         VideoQuality.HIGH: UIImagePickerControllerQualityType.High,
47 #         VideoQuality.LOW: UIImagePickerControllerQualityType.Low,
48 #     }.get(quality, UIImagePickerControllerQualityType.Medium)
49 
50 
51 class TogaImagePickerDelegate(NSObject):
52     @objc_method
53     def imagePickerController_didFinishPickingMediaWithInfo_(
54         self, picker, info
55     ) -> None:
56         picker.dismissViewControllerAnimated(True, completion=None)
57 
58         image = toga.Image(info["UIImagePickerControllerOriginalImage"])
59         self.result.set_result(image)
60 
61     @objc_method
62     def imagePickerControllerDidCancel_(self, picker) -> None:
63         picker.dismissViewControllerAnimated(True, completion=None)
64         self.result.set_result(None)
65 
66 
67 class Camera:
68     def __init__(self, interface):
69         self.interface = interface
70 
71         if NSBundle.mainBundle.objectForInfoDictionaryKey("NSCameraUsageDescription"):
72             if iOS.UIImagePickerController.isSourceTypeAvailable(
73                 UIImagePickerControllerSourceTypeCamera
74             ):
75                 self.native = iOS.UIImagePickerController.new()
76                 self.native.sourceType = UIImagePickerControllerSourceTypeCamera
77                 self.native.delegate = TogaImagePickerDelegate.new()
78             else:
79                 self.native = None
80         else:  # pragma: no cover
81             # The app doesn't have the NSCameraUsageDescription key (e.g., via
82             # `permission.camera` in Briefcase). No-cover because we can't manufacture
83             # this condition in testing.
84             raise RuntimeError(
85                 "Application metadata does not declare that the app will use the camera."
86             )
87 
88     def has_permission(self, allow_unknown=False):
89         if allow_unknown:
90             valid_values = {
91                 AVAuthorizationStatus.Authorized.value,
92                 AVAuthorizationStatus.NotDetermined.value,
93             }
94         else:
95             valid_values = {AVAuthorizationStatus.Authorized.value}
96 
97         return (
98             iOS.AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
99             in valid_values
100         )
101 
102     def request_permission(self, future):
103         # This block is invoked when the permission is granted; however, permission is
104         # granted from a different (inaccessible) thread, so it isn't picked up by
105         # coverage.
106         def permission_complete(result) -> None:
107             future.set_result(result)
108 
109         iOS.AVCaptureDevice.requestAccessForMediaType(
110             AVMediaTypeVideo, completionHandler=Block(permission_complete, None, bool)
111         )
112 
113     def get_devices(self):
114         return (
115             [
116                 CameraDevice(
117                     id="Rear",
118                     name="Rear",
119                     native=UIImagePickerControllerCameraDevice.Rear,
120                 )
121             ]
122             if iOS.UIImagePickerController.isCameraDeviceAvailable(
123                 UIImagePickerControllerCameraDevice.Rear
124             )
125             else []
126         ) + (
127             [
128                 CameraDevice(
129                     id="Front",
130                     name="Front",
131                     native=UIImagePickerControllerCameraDevice.Front,
132                 )
133             ]
134             if iOS.UIImagePickerController.isCameraDeviceAvailable(
135                 UIImagePickerControllerCameraDevice.Front
136             )
137             else []
138         )
139 
140     def take_photo(self, result, device, flash):
141         if self.native is None:
142             warnings.warn("No camera is available")
143             result.set_result(None)
144         elif self.has_permission(allow_unknown=True):
145             # Configure the controller to take a photo
146             self.native.cameraCaptureMode = (
147                 UIImagePickerControllerCameraCaptureMode.Photo
148             )
149 
150             self.native.showsCameraControls = True
151             self.native.cameraDevice = (
152                 device._impl.native
153                 if device
154                 else UIImagePickerControllerCameraDevice.Rear
155             )
156             self.native.cameraFlashMode = native_flash_mode(flash)
157 
158             # Attach the result to the delegate
159             self.native.delegate.result = result
160 
161             # Show the pane
162             toga.App.app.current_window._impl.native.rootViewController.presentViewController(
163                 self.native, animated=True, completion=None
164             )
165         else:
166             raise PermissionError("App does not have permission to take photos")
167
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/iOS/src/toga_iOS/hardware/camera.py b/iOS/src/toga_iOS/hardware/camera.py
--- a/iOS/src/toga_iOS/hardware/camera.py
+++ b/iOS/src/toga_iOS/hardware/camera.py
@@ -74,7 +74,8 @@
             ):
                 self.native = iOS.UIImagePickerController.new()
                 self.native.sourceType = UIImagePickerControllerSourceTypeCamera
-                self.native.delegate = TogaImagePickerDelegate.new()
+                self.delegate_link = TogaImagePickerDelegate.new()
+                self.native.delegate = self.delegate_link
             else:
                 self.native = None
         else:  # pragma: no cover

{"golden_diff": "diff --git a/iOS/src/toga_iOS/hardware/camera.py b/iOS/src/toga_iOS/hardware/camera.py\n--- a/iOS/src/toga_iOS/hardware/camera.py\n+++ b/iOS/src/toga_iOS/hardware/camera.py\n@@ -74,7 +74,8 @@\n             ):\n                 self.native = iOS.UIImagePickerController.new()\n                 self.native.sourceType = UIImagePickerControllerSourceTypeCamera\n-                self.native.delegate = TogaImagePickerDelegate.new()\n+                self.delegate_link = TogaImagePickerDelegate.new()\n+                self.native.delegate = self.delegate_link\n             else:\n                 self.native = None\n         else:  # pragma: no cover\n", "issue": "iOS Camera take_photo\n### Describe the bug\r\n\r\nWhen using the documented:\r\n\r\n```python3\r\nif await app.camera.request_permission():\r\n    photo = await app.camera.take_photo()\r\n```\r\n\r\n### Steps to reproduce\r\n\r\n1. request permission to use camera\r\n2. await the take_photo function.\r\n\r\n\r\n### Expected behavior\r\n\r\nReturn a toga.Image or None as documented here https://toga.readthedocs.io/en/stable/reference/api/hardware/camera.html#toga.hardware.camera.Camera.take_photo\r\n\r\n### Screenshots\r\n\r\nSuccessfully get permission to use camera: <img width=\"378\" alt=\"Screenshot 2024-02-08 at 22 14 03\" src=\"https://github.com/beeware/toga/assets/25908768/5867a18e-b5c5-4141-bd72-a4b737c0fbd1\">\r\n\r\nNo camera screen comes up.\r\n\r\n\r\n### Environment\r\n\r\n- Operating System: iOS 16.4 & 17.2 (built and development from Macbook Air M1 macOS 14.3.1)\r\n- Python version: 3.10.2\r\n- Software versions:\r\n  - Briefcase: 0.3.17\r\n  - Toga: 4.0.2\r\n\r\n\r\n### Logs\r\n\r\nSimulator Log\r\n```\r\nError in async handler: 'NoneType' object has no attribute 'result'\r\nTraceback (most recent call last):\r\n  File \"/Users/j/Library/Developer/CoreSimulator/Devices/6EAED83E-F2F7-4497-8F74-52F3DC368DA2/data/Containers/Bundle/Application/E864BC86-AFF3-410A-A4CF-D594ADB4AFCA/Health App.app/app_packages/toga/handlers.py\", line 38, in handler_with_cleanup\r\n    result = await handler(interface, *args, **kwargs)\r\n  File \"/Users/j/Library/Developer/CoreSimulator/Devices/6EAED83E-F2F7-4497-8F74-52F3DC368DA2/data/Containers/Bundle/Application/E864BC86-AFF3-410A-A4CF-D594ADB4AFCA/Health App.app/app/healthapp/app.py\", line 71, in analyse_gait_handler\r\n    photo = await self.app.camera.take_photo()\r\n  File \"/Users/j/Library/Developer/CoreSimulator/Devices/6EAED83E-F2F7-4497-8F74-52F3DC368DA2/data/Containers/Bundle/Application/E864BC86-AFF3-410A-A4CF-D594ADB4AFCA/Health App.app/app_packages/toga/hardware/camera.py\", line 122, in take_photo\r\n    self._impl.take_photo(photo, device=device, flash=flash)\r\n  File \"/Users/j/Library/Developer/CoreSimulator/Devices/6EAED83E-F2F7-4497-8F74-52F3DC368DA2/data/Containers/Bundle/Application/E864BC86-AFF3-410A-A4CF-D594ADB4AFCA/Health App.app/app_packages/toga_iOS/hardware/camera.py\", line 159, in take_photo\r\n    self.native.delegate.result = result\r\nAttributeError: 'NoneType' object has no attribute 'result'\r\n```\r\n\r\n\r\nPhysical iOS device testing log:\r\n```\r\n2024-02-08 22:27:03.097595+0000 Health App[2216:513956] [TraitCollection] Class CKBrowserSwitcherViewController overrides the -traitCollection getter, which is not supported. If you're trying to override traits, you must use the appropriate API.\r\n2024-02-08 22:27:03.179852+0000 Health App[2216:513956] Error in async handler: 'NoneType' object has no attribute 'result'\r\n\r\n2024-02-08 22:27:03.183351+0000 Health App[2216:513956] Traceback (most recent call last):\r\n\r\n2024-02-08 22:27:03.183705+0000 Health App[2216:513956]   File \"/private/var/containers/Bundle/Application/22F0E7F3-012A-46ED-93DE-C8EC8125EE19/Health App.app/app_packages/toga/handlers.py\", line 38, in handler_with_cleanup\r\n\r\n2024-02-08 22:27:03.183927+0000 Health App[2216:513956]     result = await handler(interface, *args, **kwargs)\r\n\r\n2024-02-08 22:27:03.184161+0000 Health App[2216:513956]   File \"/private/var/containers/Bundle/Application/22F0E7F3-012A-46ED-93DE-C8EC8125EE19/Health App.app/app/healthapp/app.py\", line 71, in analyse_gait_handler\r\n\r\n2024-02-08 22:27:03.184361+0000 Health App[2216:513956]     photo = await self.app.camera.take_photo()\r\n\r\n2024-02-08 22:27:03.184604+0000 Health App[2216:513956]   File \"/private/var/containers/Bundle/Application/22F0E7F3-012A-46ED-93DE-C8EC8125EE19/Health App.app/app_packages/toga/hardware/camera.py\", line 122, in take_photo\r\n\r\n2024-02-08 22:27:03.184827+0000 Health App[2216:513956]     self._impl.take_photo(photo, device=device, flash=flash)\r\n\r\n2024-02-08 22:27:03.185124+0000 Health App[2216:513956]   File \"/private/var/containers/Bundle/Application/22F0E7F3-012A-46ED-93DE-C8EC8125EE19/Health App.app/app_packages/toga_iOS/hardware/camera.py\", line 159, in take_photo\r\n\r\n2024-02-08 22:27:03.185537+0000 Health App[2216:513956]     self.native.delegate.result = result\r\n\r\n2024-02-08 22:27:03.185785+0000 Health App[2216:513956] AttributeError: 'NoneType' object has no attribute 'result'\r\n```\r\n\r\n### Additional context\r\n\r\nFully works on android, same problem on macOS I believe.\n", "before_files": [{"content": "import warnings\n\nfrom rubicon.objc import Block, NSObject, objc_method\n\nimport toga\nfrom toga.constants import FlashMode\n\n# for classes that need to be monkeypatched for testing\nfrom toga_iOS import libs as iOS\nfrom toga_iOS.libs import (\n    AVAuthorizationStatus,\n    AVMediaTypeVideo,\n    NSBundle,\n    UIImagePickerControllerCameraCaptureMode,\n    UIImagePickerControllerCameraDevice,\n    UIImagePickerControllerCameraFlashMode,\n    UIImagePickerControllerSourceTypeCamera,\n)\n\n\nclass CameraDevice:\n    def __init__(self, id, name, native):\n        self._id = id\n        self._name = name\n        self.native = native\n\n    def id(self):\n        return self._id\n\n    def name(self):\n        return self._name\n\n    def has_flash(self):\n        return iOS.UIImagePickerController.isFlashAvailableForCameraDevice(self.native)\n\n\ndef native_flash_mode(flash):\n    return {\n        FlashMode.ON: UIImagePickerControllerCameraFlashMode.On,\n        FlashMode.OFF: UIImagePickerControllerCameraFlashMode.Off,\n    }.get(flash, UIImagePickerControllerCameraFlashMode.Auto)\n\n\n# def native_video_quality(quality):\n#     return {\n#         VideoQuality.HIGH: UIImagePickerControllerQualityType.High,\n#         VideoQuality.LOW: UIImagePickerControllerQualityType.Low,\n#     }.get(quality, UIImagePickerControllerQualityType.Medium)\n\n\nclass TogaImagePickerDelegate(NSObject):\n    @objc_method\n    def imagePickerController_didFinishPickingMediaWithInfo_(\n        self, picker, info\n    ) -> None:\n        picker.dismissViewControllerAnimated(True, completion=None)\n\n        image = toga.Image(info[\"UIImagePickerControllerOriginalImage\"])\n        self.result.set_result(image)\n\n    @objc_method\n    def imagePickerControllerDidCancel_(self, picker) -> None:\n        picker.dismissViewControllerAnimated(True, completion=None)\n        self.result.set_result(None)\n\n\nclass Camera:\n    def __init__(self, interface):\n        self.interface = interface\n\n        if NSBundle.mainBundle.objectForInfoDictionaryKey(\"NSCameraUsageDescription\"):\n            if iOS.UIImagePickerController.isSourceTypeAvailable(\n                UIImagePickerControllerSourceTypeCamera\n            ):\n                self.native = iOS.UIImagePickerController.new()\n                self.native.sourceType = UIImagePickerControllerSourceTypeCamera\n                self.native.delegate = TogaImagePickerDelegate.new()\n            else:\n                self.native = None\n        else:  # pragma: no cover\n            # The app doesn't have the NSCameraUsageDescription key (e.g., via\n            # `permission.camera` in Briefcase). No-cover because we can't manufacture\n            # this condition in testing.\n            raise RuntimeError(\n                \"Application metadata does not declare that the app will use the camera.\"\n            )\n\n    def has_permission(self, allow_unknown=False):\n        if allow_unknown:\n            valid_values = {\n                AVAuthorizationStatus.Authorized.value,\n                AVAuthorizationStatus.NotDetermined.value,\n            }\n        else:\n            valid_values = {AVAuthorizationStatus.Authorized.value}\n\n        return (\n            iOS.AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)\n            in valid_values\n        )\n\n    def request_permission(self, future):\n        # This block is invoked when the permission is granted; however, permission is\n        # granted from a different (inaccessible) thread, so it isn't picked up by\n        # coverage.\n        def permission_complete(result) -> None:\n            future.set_result(result)\n\n        iOS.AVCaptureDevice.requestAccessForMediaType(\n            AVMediaTypeVideo, completionHandler=Block(permission_complete, None, bool)\n        )\n\n    def get_devices(self):\n        return (\n            [\n                CameraDevice(\n                    id=\"Rear\",\n                    name=\"Rear\",\n                    native=UIImagePickerControllerCameraDevice.Rear,\n                )\n            ]\n            if iOS.UIImagePickerController.isCameraDeviceAvailable(\n                UIImagePickerControllerCameraDevice.Rear\n            )\n            else []\n        ) + (\n            [\n                CameraDevice(\n                    id=\"Front\",\n                    name=\"Front\",\n                    native=UIImagePickerControllerCameraDevice.Front,\n                )\n            ]\n            if iOS.UIImagePickerController.isCameraDeviceAvailable(\n                UIImagePickerControllerCameraDevice.Front\n            )\n            else []\n        )\n\n    def take_photo(self, result, device, flash):\n        if self.native is None:\n            warnings.warn(\"No camera is available\")\n            result.set_result(None)\n        elif self.has_permission(allow_unknown=True):\n            # Configure the controller to take a photo\n            self.native.cameraCaptureMode = (\n                UIImagePickerControllerCameraCaptureMode.Photo\n            )\n\n            self.native.showsCameraControls = True\n            self.native.cameraDevice = (\n                device._impl.native\n                if device\n                else UIImagePickerControllerCameraDevice.Rear\n            )\n            self.native.cameraFlashMode = native_flash_mode(flash)\n\n            # Attach the result to the delegate\n            self.native.delegate.result = result\n\n            # Show the pane\n            toga.App.app.current_window._impl.native.rootViewController.presentViewController(\n                self.native, animated=True, completion=None\n            )\n        else:\n            raise PermissionError(\"App does not have permission to take photos\")\n", "path": "iOS/src/toga_iOS/hardware/camera.py"}], "after_files": [{"content": "import warnings\n\nfrom rubicon.objc import Block, NSObject, objc_method\n\nimport toga\nfrom toga.constants import FlashMode\n\n# for classes that need to be monkeypatched for testing\nfrom toga_iOS import libs as iOS\nfrom toga_iOS.libs import (\n    AVAuthorizationStatus,\n    AVMediaTypeVideo,\n    NSBundle,\n    UIImagePickerControllerCameraCaptureMode,\n    UIImagePickerControllerCameraDevice,\n    UIImagePickerControllerCameraFlashMode,\n    UIImagePickerControllerSourceTypeCamera,\n)\n\n\nclass CameraDevice:\n    def __init__(self, id, name, native):\n        self._id = id\n        self._name = name\n        self.native = native\n\n    def id(self):\n        return self._id\n\n    def name(self):\n        return self._name\n\n    def has_flash(self):\n        return iOS.UIImagePickerController.isFlashAvailableForCameraDevice(self.native)\n\n\ndef native_flash_mode(flash):\n    return {\n        FlashMode.ON: UIImagePickerControllerCameraFlashMode.On,\n        FlashMode.OFF: UIImagePickerControllerCameraFlashMode.Off,\n    }.get(flash, UIImagePickerControllerCameraFlashMode.Auto)\n\n\n# def native_video_quality(quality):\n#     return {\n#         VideoQuality.HIGH: UIImagePickerControllerQualityType.High,\n#         VideoQuality.LOW: UIImagePickerControllerQualityType.Low,\n#     }.get(quality, UIImagePickerControllerQualityType.Medium)\n\n\nclass TogaImagePickerDelegate(NSObject):\n    @objc_method\n    def imagePickerController_didFinishPickingMediaWithInfo_(\n        self, picker, info\n    ) -> None:\n        picker.dismissViewControllerAnimated(True, completion=None)\n\n        image = toga.Image(info[\"UIImagePickerControllerOriginalImage\"])\n        self.result.set_result(image)\n\n    @objc_method\n    def imagePickerControllerDidCancel_(self, picker) -> None:\n        picker.dismissViewControllerAnimated(True, completion=None)\n        self.result.set_result(None)\n\n\nclass Camera:\n    def __init__(self, interface):\n        self.interface = interface\n\n        if NSBundle.mainBundle.objectForInfoDictionaryKey(\"NSCameraUsageDescription\"):\n            if iOS.UIImagePickerController.isSourceTypeAvailable(\n                UIImagePickerControllerSourceTypeCamera\n            ):\n                self.native = iOS.UIImagePickerController.new()\n                self.native.sourceType = UIImagePickerControllerSourceTypeCamera\n                self.delegate_link = TogaImagePickerDelegate.new()\n                self.native.delegate = self.delegate_link\n            else:\n                self.native = None\n        else:  # pragma: no cover\n            # The app doesn't have the NSCameraUsageDescription key (e.g., via\n            # `permission.camera` in Briefcase). No-cover because we can't manufacture\n            # this condition in testing.\n            raise RuntimeError(\n                \"Application metadata does not declare that the app will use the camera.\"\n            )\n\n    def has_permission(self, allow_unknown=False):\n        if allow_unknown:\n            valid_values = {\n                AVAuthorizationStatus.Authorized.value,\n                AVAuthorizationStatus.NotDetermined.value,\n            }\n        else:\n            valid_values = {AVAuthorizationStatus.Authorized.value}\n\n        return (\n            iOS.AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)\n            in valid_values\n        )\n\n    def request_permission(self, future):\n        # This block is invoked when the permission is granted; however, permission is\n        # granted from a different (inaccessible) thread, so it isn't picked up by\n        # coverage.\n        def permission_complete(result) -> None:\n            future.set_result(result)\n\n        iOS.AVCaptureDevice.requestAccessForMediaType(\n            AVMediaTypeVideo, completionHandler=Block(permission_complete, None, bool)\n        )\n\n    def get_devices(self):\n        return (\n            [\n                CameraDevice(\n                    id=\"Rear\",\n                    name=\"Rear\",\n                    native=UIImagePickerControllerCameraDevice.Rear,\n                )\n            ]\n            if iOS.UIImagePickerController.isCameraDeviceAvailable(\n                UIImagePickerControllerCameraDevice.Rear\n            )\n            else []\n        ) + (\n            [\n                CameraDevice(\n                    id=\"Front\",\n                    name=\"Front\",\n                    native=UIImagePickerControllerCameraDevice.Front,\n                )\n            ]\n            if iOS.UIImagePickerController.isCameraDeviceAvailable(\n                UIImagePickerControllerCameraDevice.Front\n            )\n            else []\n        )\n\n    def take_photo(self, result, device, flash):\n        if self.native is None:\n            warnings.warn(\"No camera is available\")\n            result.set_result(None)\n        elif self.has_permission(allow_unknown=True):\n            # Configure the controller to take a photo\n            self.native.cameraCaptureMode = (\n                UIImagePickerControllerCameraCaptureMode.Photo\n            )\n\n            self.native.showsCameraControls = True\n            self.native.cameraDevice = (\n                device._impl.native\n                if device\n                else UIImagePickerControllerCameraDevice.Rear\n            )\n            self.native.cameraFlashMode = native_flash_mode(flash)\n\n            # Attach the result to the delegate\n            self.native.delegate.result = result\n\n            # Show the pane\n            toga.App.app.current_window._impl.native.rootViewController.presentViewController(\n                self.native, animated=True, completion=None\n            )\n        else:\n            raise PermissionError(\"App does not have permission to take photos\")\n", "path": "iOS/src/toga_iOS/hardware/camera.py"}]}