I'm struggling with an elusive issue, where selecting a TextField, which then shows the onscreen keyboard, causes a later application hang, but only on an actual device (not in preview, not in the simulator). I've narrowed my code down to the simplest repro.
If you run the following code, you can try both the repro case and a case that avoids the issue, where the only difference between the two is whether you select a TextField, which displays the onscreen keyboard. Even if you just dismiss the keyboard, the hang still happens.
To repro, select the "Press Here To Show Keyboard To Cause Hang" TextField, then select the "Press Here Before Showing Keyboard To Not Hang" link, then follow through with Create New Group, Add Member, Create New Member. The app will hang when you select Create New Member.
If you start with the "Press Here Before Showing Keyboard to Not Hang", everything works. You can even select the Group Name TextField before selecting Add Member without issue.
I'm looking for any ideas or suggestions, thanks.
Here's the code:
import SwiftUI
class Member : Identifiable {
let id = UUID()
var name = ""
}
class Group : Identifiable {
let id = UUID()
var name = ""
var members = [Member]()
var memberIds = [UUID]()
}
struct MemberView : View {
@Environment(\.dismiss) private var dismiss
@Binding private var groups: [Group]
@Binding private var members: [Member]
@State private var member: Member
init(groups: Binding<[Group]>, members: Binding<[Member]>, member: Member? = nil) {
_groups = groups
_members = members
_member = .init(wrappedValue: member ?? Member())
}
var body: some View {
Form {
Section(header: Text("Member Data")) {
TextField("Member Name", text: $member.name)
}
}
}
}
struct MembersView : View {
@Environment(\.dismiss) private var dismiss
@Binding private var groups: [Group]
@Binding private var members: [Member]
init(groups: Binding<[Group]>, members: Binding<[Member]>) {
_groups = groups
_members = members
}
var body: some View {
if members.isEmpty {
NavigationLink("Create New Member") {
MemberView(groups: $groups, members: $members)
}
}
else {
List(members) { member in
Text(member.name)
}
}
}
}
struct GroupView : View {
@Binding private var groups: [Group]
@Binding private var members: [Member]
@State private var group = Group()
@State private var selectedMemberId: UUID?
init(groups: Binding<[Group]>, members: Binding<[Member]>) {
_groups = groups
_members = members
}
var body: some View {
Form {
Section(header: Text("Group Data")) {
TextField("Group Name", text: $group.name)
}
Section(header: Text("Members")) {
List(members) { member in
if group.memberIds.contains(member.id) {
NavigationLink {
MemberView(groups: $groups, members: $members, member: member)
}
label: {
Text(member.name)
}
}
}
NavigationLink {
MembersView(groups: $groups, members: $members)
}
label: {
Text("Add Member")
}
}
}
}
}
struct GroupsView : View {
@Binding private var groups: [Group]
@Binding private var members: [Member]
init(groups: Binding<[Group]>, members: Binding<[Member]>) {
_groups = groups
_members = members
}
var body: some View {
if groups.isEmpty {
NavigationLink("Create New Group") {
GroupView(groups: $groups, members: $members)
}
}
else {
List(groups) { group in
Text(group.name)
}
}
}
}
struct MainView : View {
@State private var groups: [Group]
@State private var members: [Member]
@State private var settings: [String]
@State private var setting = ""
init() {
_groups = .init(wrappedValue: [])
_members = .init(wrappedValue: [])
_settings = .init(wrappedValue: [])
}
var body: some View {
NavigationStack {
if settings.isEmpty {
VStack {
TextField("Press Here To Show Keyboard To Cause Hang (Whether Or Not You Type Anything)", text: $setting) {
settings.append("Hang")
}
Button("Press Here Before Showing Keyboard To Not Hang") {
settings.append("No Hang")
}
}
}
else {
GroupsView(groups: $groups, members: $members)
}
}
}
}
#Preview {
MainView()
}
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
MainView()
}
}
}