Repository: beeware/toga

Issue:
Inconsistent behaviour with toga.Selection value attribute between Windows and Android
### Describe the bug

On Windows, when a selection widget is created with a list containing an empty string (eg: toga.Selection(items=['1','a','','4']) ), the widget returns '' for the "value" attribute.

On Android, the selection's "value" attribute is set to None if the selection item is an empty string.

I don't have MacOS, iOS, or Linux devices so I don't know what happens there.

### Steps to reproduce

0. Create Beeware Project
1. Go to the app.py file
2. Create a list "list_1" containing an empty string
3. Create the base app class with the startup function and setup main box + window
4. In the startup function, create a selection with the items being that of list_1. Store it as a class attribute for future access.
5. Define a class function that outputs the selection value via print or info_dialog.
6. Create Button to call the output function
7. Add Selector & Button to main box
9. Compile the code for both Windows and Android & launch them.
11. Select the empty item of the selector
12. Press the button
13. Watch as the devices disagree on the value.

### Expected behavior

I would expect the selector value attribute to be an empty string as that was what the item was, and that is what the selector widget understands when values are written to it.

### Screenshots

The source code I used:
```
"""
Testing Crashes
"""
import toga
class TestLab(toga.App):
    def startup(self):
        main_box=toga.Box()
        self.select=toga.Selection(items=['A','B','0','','1','+','-'])
        main_box.add(self.select)
        main_box.add(toga.Button('Get',on_press=self.valuedump))
        self.main_window=toga.MainWindow(title=self.formal_name)
        self.main_window.content=main_box
        self.main_window.show()
    async def valuedump(self,widget):
        q=self.select.value
        print('GET',q,type(q))
        self.main_window.info_dialog('Selection Value',f'Value: "{q}"\n Type: {type(q)}')
def main():return TestLab()
```

On Windows:

![A_val](https://user-images.githubusercontent.com/113656324/209735390-f7e31b6e-c8d7-4cf3-9d51-2f47a69c7a62.png)
![sel_vals](https://user-images.githubusercontent.com/113656324/209735397-60442c7c-28dd-413c-a7c4-bae10003363e.png)
![Null_val](https://user-images.githubusercontent.com/113656324/209735402-9cdf611c-e512-4a52-bc9b-28ea193dae86.png)

On Android:

![Screenshot_20221227-171610](https://user-images.githubusercontent.com/113656324/209735964-7f7ae864-d2ce-4203-99b5-87dddc5f8938.png)
![Screenshot_20221227-171624](https://user-images.githubusercontent.com/113656324/209735970-d459f355-0390-490f-ba56-1adee2c6eef9.png)
![Screenshot_20221227-171635](https://user-images.githubusercontent.com/113656324/209735971-84bca7d5-5590-454f-9f6f-effe244a4126.png)


### Environment


Windows 8.1 / Android 11
- Python version: 3.10.0
  - Briefcase: 3.11
  - Toga: 0.3.0.dev8


### Logs

Windows:
[briefcase.2022_12_27-17_09_47.run.log](https://github.com/beeware/toga/files/10311153/briefcase.2022_12_27-17_09_47.run.log)

Android:
[briefcase.2022_12_27-17_17_14.run.log](https://github.com/beeware/toga/files/10311154/briefcase.2022_12_27-17_17_14.run.log)


### Additional context

_No response_


Discussion:
Thanks for the report; looks like an edge case that we've missed in testing. The fault is almost certainly with the `get_value()` handler and the way it's interpreting the return value from the underlying Android API. 

Resolving patch:
diff --git a/cocoa/src/toga_cocoa/widgets/selection.py b/cocoa/src/toga_cocoa/widgets/selection.py
--- a/cocoa/src/toga_cocoa/widgets/selection.py
+++ b/cocoa/src/toga_cocoa/widgets/selection.py
@@ -43,7 +43,9 @@ def select_item(self, item):
         self.native.selectItemWithTitle(item)
 
     def get_selected_item(self):
-        return str(self.native.titleOfSelectedItem)
+        selected = self.native.titleOfSelectedItem
+        if selected:
+            return str(selected)
 
     def set_on_select(self, handler):
         pass
diff --git a/examples/selection/selection/app.py b/examples/selection/selection/app.py
--- a/examples/selection/selection/app.py
+++ b/examples/selection/selection/app.py
@@ -19,6 +19,9 @@ def startup(self):
 
         # Add the content on the main window
         self.selection = toga.Selection(items=self.OPTIONS)
+        self.empty_selection = toga.Selection()
+
+        self.report_label = toga.Label("", style=label_style)
 
         self.main_window.content = toga.Box(
             children=[
@@ -29,6 +32,22 @@ def startup(self):
                         self.selection,
                     ],
                 ),
+                toga.Box(
+                    style=box_style,
+                    children=[
+                        toga.Label("Empty selection", style=label_style),
+                        self.empty_selection,
+                    ],
+                ),
+                toga.Box(
+                    style=box_style,
+                    children=[
+                        toga.Button(
+                            "Report on selection", on_press=self.report_selection
+                        ),
+                        self.report_label,
+                    ],
+                ),
                 toga.Box(
                     style=box_style,
                     children=[
@@ -66,7 +85,7 @@ def startup(self):
                 toga.Box(
                     style=box_style,
                     children=[
-                        toga.Label("use some style!", style=label_style),
+                        toga.Label("Use some style!", style=label_style),
                         toga.Selection(
                             style=Pack(width=200, padding=24),
                             items=["Curium", "Titanium", "Copernicium"],
@@ -113,6 +132,11 @@ def my_on_select(self, selection):
 
         print(f"The selection widget changed to {selection.value}")
 
+    def report_selection(self, widget):
+        self.report_label.text = (
+            f"Element: {self.selection.value!r}; Empty: {self.empty_selection.value!r}"
+        )
+
 
 def main():
     # App name and namespace
diff --git a/iOS/src/toga_iOS/widgets/selection.py b/iOS/src/toga_iOS/widgets/selection.py
--- a/iOS/src/toga_iOS/widgets/selection.py
+++ b/iOS/src/toga_iOS/widgets/selection.py
@@ -69,7 +69,10 @@ def select_item(self, item):
         self.interface.factory.not_implemented("Selection.select_item()")
 
     def get_selected_item(self):
-        return self.interface.items[self.picker.selectedRowInComponent(0)]
+        try:
+            return self.interface.items[self.picker.selectedRowInComponent(0)]
+        except IndexError:
+            return None
 
     def set_on_select(self, handler):
         # No special handling required


Test patch: