SourceKitService taking up to 200% of CPU - swift

I just updated to Xcode 12.5.1, and now my SourceKitService is taking insanely high amounts of my CPU whenever I edit a specific file. After editing this file to any extent my CPU usage jumps through the roof, and basic services such as code completion stop working. I've already tried most of the solutions online about this issue, and nothing is helping. Does anyone have any ideas for this? Thanks.
I'll just put all of the file's code in here, because I'm not sure where the issue might originate.
//
// ScheduleView.swift
// ClassWidget
//
// Created by Ben K on 6/17/21.
//
import SwiftUI
import CoreData
struct ScheduleView: View {
#Environment(\.managedObjectContext) var moc
#ObservedObject var schedule: Schedule
#State private var showingAddPeriod = false
#State private var showingEditPeriod = false
#State private var editPeriod: Period?
#State private var isEditMode: EditMode = .inactive
#State private var showingSettings = false
#State private var showingPreview = false
#State private var showingWarning = false
#State private var warningPeriod: Period?
var timeFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
return formatter
}
var body: some View {
ZStack {
Text("\(editPeriod?.uName ?? "")")
.hidden()
List {
Section(header: Text("Classes")) {
if !schedule.periods.isEmpty {
ForEach(schedule.periods) { period in
Button(action: { editPeriod = period; isEditMode = .inactive; showingEditPeriod = true }) {
HStack {
VStack {
Text(timeFormatter.string(from: period.uStart))
Text("to")
Text(timeFormatter.string(from: period.uEnd))
}
.font(.caption)
.padding(.trailing, 10)
.padding(6)
Divider()
.frame(height: 35)
.padding(.trailing)
VStack(alignment: .leading) {
Text(period.uName)
if period.uTeacher != "" && period.uRoom != "" {
Text("\(period.uTeacher) • \(period.uRoom)")
.font(.caption)
.foregroundColor(.secondary)
} else if period.uTeacher != "" {
Text("\(period.uTeacher)")
.font(.caption)
.foregroundColor(.secondary)
} else if period.uRoom != "" {
Text("\(period.uRoom)")
.font(.caption)
.foregroundColor(.secondary)
}
}
Spacer()
Image(systemName: "chevron.right")
.renderingMode(.template)
.padding(.trailing, 10)
.opacity(0.5)
}
.foregroundColor(.primary)
}
}
.onDelete(perform: delete)
} else {
VStack(alignment: .leading) {
Text("No classes yet")
.font(.headline)
Text("Start adding classes to create this schedule!")
.font(.caption)
.italic()
}
.padding(8)
}
}
Section {
Button(action: {
showingSettings = true
}) {
HStack {
Text("Settings")
Spacer()
Image(systemName: "chevron.right")
.padding(.trailing, 10)
.opacity(0.5)
}
.foregroundColor(.primary)
}
}
Button("Preview Widget") {
showingPreview = true
}
}
.listStyle(InsetGroupedListStyle())
}
.navigationTitle(schedule.uName)
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing: Button(action: {
showingAddPeriod = true
}) {
Image(systemName: "plus").padding([.vertical, .leading])
})
.sheet(isPresented: $showingAddPeriod) {
AddPeriod(schedule: schedule)
.environment(\.managedObjectContext, self.moc)
}
/*.sheet(isPresented: $showingEditPeriod) {
if let period = editPeriod {
AddPeriod(period: period)
.environment(\.managedObjectContext, self.moc)
}
}*/
.fullScreenCover(isPresented: $showingEditPeriod, onDismiss: dismissedSheet) {
if let period = editPeriod {
AddPeriod(period: period)
.environment(\.managedObjectContext, self.moc)
}
}
.fullScreenCover(isPresented: $showingSettings) {
ScheduleSettingsView(schedule: schedule)
.environment(\.managedObjectContext, self.moc)
}
.sheet(isPresented: $showingPreview) {
PreviewWidget(schedule: schedule)
}
.alert(isPresented: $showingWarning) {
Alert(title: Text("Delete \(warningPeriod?.uName ?? "")"), message: Text("Are you sure?"), primaryButton: .destructive(Text("Delete")) {
try? moc.save()
}, secondaryButton: .cancel() {
if let period = warningPeriod {
readdPeriod(period: period)
}
})
}
.environment(\.editMode, self.$isEditMode)
}
func delete(at offsets: IndexSet) {
for offset in offsets {
warningPeriod = schedule.periods[offset]
moc.delete(schedule.periods[offset])
showingWarning = true
}
}
func readdPeriod(period: Period) {
let newPeriod = Period(period: period, context: moc)
newPeriod.schedule = schedule
try? moc.save()
}
func dismissedSheet() {
schedule.objectWillChange.send()
}
}
struct ScheduleView_Previews: PreviewProvider {
static let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
static var previews: some View {
let schedule = Schedule(name: "Example Schedule", number: 0, context: moc)
NavigationView {
ScheduleView(schedule: schedule)//.preferredColorScheme(.dark)
}
}
}

Looking at your code there seems to be a lot going on but along with the other solutions you will find in SO,
It is helpful to start commenting out portions of your code to try and narrow down a syntax issue that might be causing it.
Per out conversation in the comments the issue was caused by the multiple view modifiers that were attached to your ZStack moving them to the views that activated them has so far resolved it.

This is a compiler type-checking engine failure, aka
compiler is unable to type-check this expression in reasonable time;
try breaking up the expression into distinct sub-expressions
For some reason it fails silently and causes SourceKitService and swift-frontend to leak.
You need to simplify you view by moving logically discrete parts of the view into separate helper vars/funcs or structs.

Related

Refreshable not working or showing any errors

I am trying to use refreshable on my List from this tutorial
struct HomeTab: View {
#StateObject var getMsgs = GetMessages()
#State private var receivedMessages = [Timestamp(event: "nil", imgURL: "nil", read: false, readableDate: "nil", temp: "nil", time: "nil")]
var body: some View {
VStack(spacing: 0) {
greeting.edgesIgnoringSafeArea(.top)
messages
Spacer()
}
.onAppear {
getMsgs.fetchMessages()
}
.refreshable {
do {
let BASE_URL = "url.com"
guard let url = URL(string: BASE_URL) else { return }
let (data, _) = try await URLSession.shared.data(from:url)
receivedMessages = try JSONDecoder().decode([String: Timestamp].self, from: data).values.sorted{ $0.readableDate < $1.readableDate }
} catch {
receivedMessages = []
}
}
}
I am trying to implement it for this list:
private var messages: some View {
List(receivedMessages.filter{$0.read == false}) {msg in
NavigationLink(destination: MsgDetailView(message: msg)) {
HStack {
Spacer()
VStack {
Text(msg.event)
.font(.system(size: 17))
.bold()
.frame(maxWidth: .infinity, alignment: .center)
}
}.padding(.leading)
.padding(.top)
.padding(.trailing)
Divider()
.frame(height: 1)
.background(Color.gray)
.opacity(0.3)
.navigationBarHidden(true)
}
}
My data is being downloaded and decoded fine, it's just that the data doesn't refresh when I pull

Saved Double Values showing as 0.00 instead of what was entered in text field

I am a beginner to swift language and am trying to work on a simple expense tracking project but cannot get the Double values entered in the textfield to show in the list below when saving an item. Any help/assistance and explanation is GREATLY appreciated!
Here are is the code I'm using minus my formatter which is set to .currency
import SwiftUI
import CoreData
struct ContentView: View {
// MARK: - PROPERTIES
#State private var dollarValue: String = ""
#State var dollarAsInt: Double = Double()
#State private var plug: String = ""
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
// MARK: - FUNCTION
private func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
newItem.dollarValue = dollarValue
newItem.dollarAsInt = dollarAsInt
newItem.id = UUID()
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { items[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
// MARK: - BODY
var body: some View {
NavigationView {
VStack {
VStack(spacing: 16){
TextField("Label", text: $dollarValue)
.padding()
.background(Color(UIColor.systemGray6))
.cornerRadius(10)
TextField("Dollar Amount", value: $dollarAsInt, formatter: formatter)
.padding()
.background(Color(UIColor.systemGray6))
.cornerRadius(10)
.keyboardType(.decimalPad)
Button(action: {addItem()}, label: {
Spacer()
Text("Save")
Spacer()
})
.padding()
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(10)
} //: VSTACK
.padding()
List {
ForEach(items) { item in
HStack {
VStack(alignment: .leading) {
Text(item.dollarValue ?? "")
.fontWeight(.semibold)
Text("\(item.timestamp!, formatter: itemFormatter)")
.foregroundColor(.gray)
.font(.footnote)
}
Spacer()
VStack{
Text("\(item.dollarAsInt)")
//Text("\(item.dollarAsInt)")
.fontWeight(.semibold)
Text("Category")
.foregroundColor(.gray)
.font(.footnote)
}
}
}
.onDelete(perform: deleteItems)
}//:List
} //: VSTACK
.navigationBarTitle("Transactions", displayMode: .large)
.toolbar {
#if os(iOS)
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
#endif
ToolbarItem(placement: .navigationBarLeading) {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
} //: TOOLBAR
} //: NAVIGATION
}
// MARK: - PREVIEW
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
}
Preview of Error when trying to add dollar value
your code works well for me on macos 12.beta, xcode 13.beta,
target ios 15 and macCatalyst 12.
This is the test code I used, which should be the same as your code:
import SwiftUI
import CoreData
#main
struct CoreDataTestApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
struct ContentView: View {
#State private var dollarValue: String = ""
#State var dollarAsInt: Double = 0.0
#State private var plug: String = ""
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
private func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
newItem.dollarValue = dollarValue
newItem.dollarAsInt = dollarAsInt
newItem.id = UUID()
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { items[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
var body: some View {
NavigationView {
VStack {
VStack(spacing: 16){
TextField("Label", text: $dollarValue)
.padding()
.background(Color(UIColor.systemGray6))
.cornerRadius(10)
TextField("Dollar Amount", value: $dollarAsInt, formatter: formatter)
.padding()
.background(Color(UIColor.systemGray6))
.cornerRadius(10)
.keyboardType(.decimalPad)
Button(action: {addItem()}, label: {
Spacer()
Text("Save")
Spacer()
})
.padding()
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(10)
} //: VSTACK
.padding()
List {
ForEach(items) { item in
HStack {
VStack(alignment: .leading) {
Text(item.dollarValue ?? "")
.fontWeight(.semibold)
Text("\(item.timestamp!, formatter: itemFormatter)")
.foregroundColor(.gray)
.font(.footnote)
}
Spacer()
VStack{
Text("\(item.dollarAsInt)")
//Text("\(item.dollarAsInt)")
.fontWeight(.semibold)
Text("Category")
.foregroundColor(.gray)
.font(.footnote)
}
}
}
.onDelete(perform: deleteItems)
}//:List
} //: VSTACK
.navigationBarTitle("Transactions", displayMode: .large)
.toolbar {
#if os(iOS)
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
#endif
ToolbarItem(placement: .navigationBarLeading) {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
} //: TOOLBAR
}.navigationViewStyle(.stack)
}
private var formatter: NumberFormatter = {
let f = NumberFormatter()
// allow no currency symbol, extra digits, etc
f.isLenient = true
f.numberStyle = .currency
return f
}()
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()
}

Network monitor crashing in iOS13

So I am trying to keep my current up at iOS 13 + while bring in a few iOS 14 features.
One of the new features is monitoring if a user loses network connection.
However I am getting a strange error when I run the following code.
TaView.swift (default view)
import SwiftUI
struct TaView: View {
#StateObject var monitor = Monitor()
#ObservedObject var location = LocationManager()
#State var errorDetail = false
var lat: String{
return "\(location.lastKnownLocation?.coordinate.latitude ?? 0.0)"
}
var lon: String{
return "\(location.lastKnownLocation?.coordinate.longitude ?? 0.0)"
}
var state: String{
return "\(UserSettings().state)"
}
init() {
// print(self.data.connected)
self.location.startUpdating()
}
#ObservedObject var userSettings = UserSettings()
var body: some View {
if (self.lat == "0.0" && self.lon == "0.0"){
LocationDisabled()
}
else{
if #available(iOS 14.0, *) {
TabView {
ContentView()
.tabItem {
Image(systemName: "dot.radiowaves.left.and.right")
Text("Radio")
}
WinView()
.tabItem {
Image(systemName: "w.circle")
Text("Win")
}
SettingsView()
.tabItem {
Image(systemName: "gear")
Text("Settings")
}
}.accentColor(Color.red)
.onAppear(){
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (timer) in
if monitor.score == 0{
DispatchQueue.main.asyncAfter(deadline: .now()) {
self.errorDetail = true
}
}
//print("this is the score \(monitor.score)")
}
}
.fullScreenCover(isPresented: self.$errorDetail, content: NetworkOutageView.init)
} else {
TabView {
ContentView()
.tabItem {
Image(systemName: "dot.radiowaves.left.and.right")
Text("Radio")
}
WinView()
.tabItem {
Image(systemName: "w.circle")
Text("Win")
}
SettingsView()
.tabItem {
Image(systemName: "gear")
Text("Settings")
}
}.accentColor(Color.red)
.onAppear(){
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (timer) in
if monitor.score == 0{
DispatchQueue.main.asyncAfter(deadline: .now()) {
self.errorDetail = true
}
}
//print("this is the score \(monitor.score)")
}
}
.sheet(isPresented: self.$errorDetail, content: NetworkOutageView.init)
}
}
}
}
struct LocationDisabled: View {
#ObservedObject var location = LocationManager()
init() {
self.location.startUpdating()
}
var body: some View {
GeometryReader { geo in
VStack{
Spacer().frame(maxHeight: 100)
Image(systemName: "location.fill").resizable().scaledToFit().frame(width: 100).foregroundColor(Color.white)
VStack{
Text("Enable Location").font(.system(.largeTitle, design: .rounded)).bold().multilineTextAlignment(.leading).foregroundColor(Color.white)
Text("You'll need to enable your location.\nIn order to use access these features").fontWeight(.light).multilineTextAlignment(.center).fixedSize(horizontal: false, vertical: true).padding(.all, 8).foregroundColor(Color.white)
Text("\u{2022} Win Prizes\n\n\u{2022} Change Stations\n\n\u{2022} Access Podcasts\n\n\u{2022} Request Songs\n\n\u{2022} And More").bold().multilineTextAlignment(.center).fixedSize(horizontal: false, vertical: true).padding(.all, 8).foregroundColor(Color.white)
Spacer()
Button(action: {
self.location.requestLoc()
}) {
Text("ALLOW LOCATION")
.font(.headline)
.bold()
}.buttonStyle(LocationGradientButtonStyle())
.padding(.bottom, 100)
}
// ImageOverlay()
}.frame(maxWidth: .infinity,maxHeight: .infinity).edgesIgnoringSafeArea(.all).background(
Image("TooFarWallPaper").resizable().aspectRatio(contentMode: .fill).frame(maxWidth: .infinity,maxHeight: .infinity).edgesIgnoringSafeArea(.all)
)
}
}
}
struct LocationGradientButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some SwiftUI.View {
configuration.label
.foregroundColor(Color.white)
.padding()
.background(LinearGradient(gradient: Gradient(colors: [Color.blue, Color.purple]), startPoint: .topLeading, endPoint: .bottomTrailing))
.cornerRadius(15.0)
}
}
struct SaveGradientButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some SwiftUI.View {
configuration.label
.foregroundColor(Color.black)
.padding()
.background(LinearGradient(gradient: Gradient(colors: [Color.green, Color.green]), startPoint: .topLeading, endPoint: .bottomTrailing))
.cornerRadius(15.0)
}
}
Monitor.swift
import Network
import SwiftUI
// An enum to handle the network status
enum NetworkStatus: String {
case connected
case disconnected
}
class Monitor: ObservableObject {
private let monitor = NWPathMonitor()
private let queue = DispatchQueue(label: "Monitor")
#Published var score = 0
#Published var status: NetworkStatus = .connected
init() {
monitor.pathUpdateHandler = { [weak self] path in
guard let self = self else { return }
// Monitor runs on a background thread so we need to publish
// on the main thread
DispatchQueue.main.async {
if path.status == .satisfied {
print("We're connected!")
self.status = .connected
self.score = 1
} else {
print("No connection.")
self.score = 0
self.status = .disconnected
}
}
}
monitor.start(queue: queue)
}
}
The error is
Thread 1: signal SIGABRT
I am wondering how do I fix this?
Changing #StateObject to ObservedObject works great.
Note: you don't always need to write the same code again in if and else when checking for #availability (when doing backwards compatibility), you can just create one single variable for a View and reuse it, like in this case we reuse tabViewContent.
struct TaView: View {
#ObservedObject var monitor = Monitor()
#ObservedObject var location = LocationManager()
#State var errorDetail = false
var lat: String{
return "\(location.lastKnownLocation?.coordinate.latitude ?? 0.0)"
}
var lon: String{
return "\(location.lastKnownLocation?.coordinate.longitude ?? 0.0)"
}
var state: String{
return "\(UserSettings().state)"
}
init() {
// print(self.data.connected)
self.location.startUpdating()
}
#ObservedObject var userSettings = UserSettings()
var tabViewContent: some View {
TabView {
ContentView()
.tabItem {
Image(systemName: "dot.radiowaves.left.and.right")
Text("Radio")
}
WinView()
.tabItem {
Image(systemName: "w.circle")
Text("Win")
}
SettingsView()
.tabItem {
Image(systemName: "gear")
Text("Settings")
}
}.accentColor(Color.red)
.onAppear(){
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (timer) in
if monitor.score == 0{
self.errorDetail = true
} else {
self.errorDetail = false
}
//print("this is the score \(monitor.score)")
}
}
}
var body: some View {
if (self.lat == "0.0" && self.lon == "0.0"){
LocationDisabled()
}
else{
if #available(iOS 14.0, *) {
tabViewContent
.fullScreenCover(isPresented: self.$errorDetail, content: NetworkOutageView.init)
} else {
tabViewContent
.sheet(isPresented: self.$errorDetail, content: NetworkOutageView.init)
}
}
}
}
Tested with Xcode 12.4, on iOS13.3.1 and iOS 14.4.2

Dismiss a View in SwiftUI when parent is re-rendered

Using iOS14.4, Swift5.3.2, XCode12.2,
I try to dismiss a SwiftUI's GridView (see below code).
The dismiss function is done by the \.presentationMode property of the #Environment as explained here.
Everything works until the moment where I introduced a #Binding property that mutates the parent-View at the very moment of the dismissal. (see dataStr = titles[idx] in code excerpt below).
I read that dismissal by \.presentationMode only works if the parent-View is not updated during the time the child-View is shown.
But I absolutely need to cause a mutation on the parent-View when the user taps on an element of the GridView at play here.
How can I re-write so that parent-View is updated AND dismissal of Child-View still work ?
struct GridView: View {
#Environment(\.presentationMode) private var presentationMode
#Binding var dataStr: String
#State private var titles = [String]()
let layout = [
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
ScrollView {
LazyVGrid(columns: layout, spacing: 10) {
ForEach(titles.indices, id: \.self) { idx in
VStack {
Text(titles[idx])
Image(titles[idx])
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: (UIScreen.main.bounds.width / 2) - 40)
}
.onTapGesture {
// WITHOUT THIS LINE OF CODE - EVERYTHING WORKS. WHY???????????????
dataStr = titles[idx]
self.presentationMode.wrappedValue.dismiss()
}
}
}
.padding()
}
}
}
As #jnpdx asked, here you can see the parent-View. Please find the GridView(dataStr: self.$dataStr) inside the .sheet() of the ToolBarItem()....
import SwiftUI
struct MainView: View {
#EnvironmentObject var mediaViewModel: MediaViewModel
#EnvironmentObject var commService: CommunicationService
#State private var dataStr = ""
#State private var connectionsLabel = ""
#State private var commumincationRole: THRole = .noMode
#State private var showingInfo = false
#State private var showingGrid = false
init() {
UINavigationBar.appearance().tintColor = UIColor(named: "title")
}
var body: some View {
NavigationView {
if mediaViewModel.mediaList.isEmpty {
LoadingAnimationView()
.navigationBarHidden(true)
.ignoresSafeArea()
} else {
if dataStr.isEmpty {
MainButtonView(dataStr: $dataStr,
commumincationRole: $commumincationRole,
connectionsLabel: $connectionsLabel
)
.navigationBarHidden(false)
.navigationTitle("Trihow Pocket")
.navigationBarColor(backgroundColor: UIColor(named: "btnInactive"), titleColor: UIColor(named: "title"))
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
showingInfo.toggle()
}) {
Image(systemName: "ellipsis")
}
.sheet(isPresented: $showingInfo) {
InfoView()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showingGrid.toggle()
}) {
Image(systemName: "square.grid.3x3")
}
.sheet(isPresented: $showingGrid) {
// GRIDVIEW CALLING THE CHILD-VIEW IS HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
GridView(dataStr: self.$dataStr)
}
}
}
} else {
let str = self.dataStr
#if os(iOS)
PageViewiOS(dataStr: self.$dataStr, commumincationRole: $commumincationRole)
.navigationBarHidden(true)
.onAppear() {
if commumincationRole == .moderatorMode {
commService.send(thCmd: THCmd(key: .tagID, sender: "", content: str))
}
}
.ignoresSafeArea()
#elseif os(macOS)
PageViewMacOS()
.ignoresSafeArea()
#endif
}
}
}
.onTHComm_PeerAction(service: commService) { (peers) in
let idsOrNames = peers.map { (peer) -> String in
if let id = peer.id {
return "\(id)"
} else if let name = peer.name {
return "\(name)"
} else {
return ""
}
}
connectionsLabel = "Connected devices: \n\(idsOrNames.lineFeedString)"
}
.onTHComm_ReceiveCmd(service: commService) { (thCmd) in
if (commumincationRole == .moderatorMode) || (commumincationRole == .discoveryMode) {
switch thCmd.key {
case .tagID:
dataStr = thCmd.content
case .closeID:
dataStr = ""
default:
break
}
}
}
.onTHComm_LastMessageLog(service: commService) { (log) in
print(log)
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView()
.environmentObject(MediaViewModel())
.environmentObject(MultipeerConnectivityService())
}
}
With the help of #jnpdx, I found a workaround.
Wrap the binding-property (i.e. dataStr in my example) into a delayed block that executes after something like 50ms:
.onTapGesture {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(50)) {
dataStr = thumNames[idx]
}
self.presentationMode.wrappedValue.dismiss()
}
Of course, this workaround only works in my case, because I do no longer need to keep the Child-View open. There might be other situations where the Parent-View needs to be updated prior to closing the Child-View (i.e. here the update of dataStr can be done right at the closing moment of the Child-View).
I am still wondering how to deal with dismiss-problems for any case where the Child-View makes the Parent-View update prior to closing. These are situations where SwiftUI's dismiss function no longer work from then on. Any mutation of the Parent-View cause the Child-View to separate somehow and dismisss no longer works.
Any idea what to do in that case ?

How can I use Navigation in alert using SwiftUI

I'm working on a Bluetooth Application.It has onboarding and dashboard.On the Onboarding there is pairing and instructions on how to use the module, and the dashboard controls the peripheral device.So I need to unpair using an alert and navigate it to a different page called Onboarding.How can I navigate to a different view using an alert.
Code Block
import SwiftUI
import BLE
struct Dashboard: View {
#EnvironmentObject var BLE: BLE
#State private var showUnpairAlert: Bool = false
#State private var hasConnected: Bool = false
let defaults = UserDefaults.standard
let defaultDeviceinformation = "01FFFFFFFFFF"
struct Keys {
static let deviceInformation = "deviceInformation"
}
var body: some View {
VStack(alignment: .center, spacing: 0) {
// MARK: - Menu Bar
HStack(alignment: .center, spacing: 10) {
VStack(alignment: .center, spacing: 4) {
Text(self.hasConnected ? "PodId \(checkForDeviceInformation())":"Pod is not connected")
.font(.footnote)
.foregroundColor(.white)
Button(action: {
print("Unpair tapped!")
self.showUnpairAlert = true
}) {
HStack {
Text("Unpair")
.fontWeight(.bold)
.font(.body)
}
.frame(minWidth: 85, minHeight: 35)
.foregroundColor(.white)
.background(Color(red: 0.8784313725490196, green: 0.34509803921568627, blue: 0.36470588235294116))
.cornerRadius(30)
}
}
}
}
.alert(isPresented: $showUnpairAlert) {
Alert(title: Text("Unpair from \(checkForDeviceInformation())"), message: Text("Do you want to unpair the current pod?"), primaryButton: .destructive(Text("Unpair")) {
self.unpairAndSetDefaultDeviceInformation()
}, secondaryButton: .cancel())
}
}
func checkForDeviceInformation() -> String {
let deviceInformation = defaults.value(forKey: Keys.deviceInformation) as? String ?? ""
print("Device Info \(deviceInformation)")
return deviceInformation
}
func unpairAndSetDefaultDeviceInformation() {
defaults.set(defaultDeviceinformation, forKey: Keys.deviceInformation)
print("Pod unpaired and view changed to Onboarding")
}
}
Thank you !!!!
I simplified your code snapshot for demo, but think the idea would be clear
struct TestNavigationFromAlert: View {
#State private var showUnpairAlert: Bool = false
#State private var activateLink: Bool = false
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Your Onboarding page"), isActive: $activateLink,
label: { EmptyView() })
// DEMO BUTTON - REMOVE IT
Button(action: { self.showUnpairAlert = true }) { Text("Alert") }
// YOUR CODE IS HERE
}
.alert(isPresented: $showUnpairAlert) {
Alert(title: Text("Unpair from \(checkForDeviceInformation())"), message: Text("Do you want to unpair the current pod?"), primaryButton: .destructive(Text("Unpair")) {
self.unpairAndSetDefaultDeviceInformation()
}, secondaryButton: .cancel())
}
}
}
func checkForDeviceInformation() -> String {
// YOUR CODE IS HERE
return "Stub information"
}
func unpairAndSetDefaultDeviceInformation() {
// YOUR CODE IS HERE
DispatchQueue.main.async {
self.activateLink = true
}
}
}