(Q) How to use button action in SwiftUI - swift

I am currently creating a FaceID-aware app using Swift UI.
What I am trying to do is try FaceID authentication when the user presses the button. When I call class in button action, I get the error message "Result of 'DashboardView' initializer is unused". The related sources are as below. Any help would be appreciated!
struct FaceLoginView: View {
#State private var isUnlocked = false
var body: some View {
VStack {
Button(action: {
print("face id tapped!")
if self.isUnlocked {
// print("unlocked")
DashboardView()
} else {
// Text("Locked")
LoginView()
}
}) {
HStack {
Image(systemName: "faceid")
.font(.title)
Text("faceid")
.fontWeight(.semibold)
.font(.title)
}
.frame(minWidth: 0, maxWidth: .infinity)
.padding()
.foregroundColor(.white)
.background(LinearGradient(gradient: Gradient(colors: [Color("DarkGreen"), Color("LightGreen")]), startPoint: .leading, endPoint: .trailing))
.cornerRadius(40)
}
}
.onAppear(perform: authenticate)
}
func authenticate() {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Log in to your account by unlocking FaceID."
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
DispatchQueue.main.async {
if success {
// authenticated successfully
self.isUnlocked = true
} else {
// there was a problem
self.isUnlocked = false
print(error?.localizedDescription ?? "Failed to authenticate")
}
}
}
} else {
// no biometrics
print(error?.localizedDescription ?? "Can't evaluate policy")
}
}
}

check this out:
i had to comment out some things and change some things because your example wasn't compilable at all but things should be clear to you if you see it ;)
struct DashboardView : View {
var body: some View {
Text("Dashboardview")
}
}
struct LoginView : View {
var body: some View {
Text("LoginView")
}
}
struct FaceLoginView: View {
private var isUnlocked : Bool {
get {
return Bool.random()
}
set {
self.isUnlocked = newValue
}
}
#State var navigateToDashboard = false
#State var navigateToLogin = false
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DashboardView(), isActive: $navigateToDashboard) { EmptyView().hidden()
}.hidden().frame(height:0)
NavigationLink(destination: LoginView(), isActive: $navigateToLogin) { EmptyView().hidden()
}.hidden().frame(height:0)
Button(action: {
print("face id tapped!")
if self.isUnlocked {
// print("unlocked")
self.navigateToDashboard.toggle()
} else {
// Text("Locked")
self.navigateToLogin.toggle()
}
}) {
HStack {
Image(systemName: "faceid")
.font(.title)
Text("faceid")
.fontWeight(.semibold)
.font(.title)
}
.frame(minWidth: 0, maxWidth: .infinity)
.padding()
// .foregroundColor(.white)
.background(LinearGradient(gradient: Gradient(colors: [Color("DarkGreen"), Color("LightGreen")]), startPoint: .leading, endPoint: .trailing))
.cornerRadius(40)
}
}
}
.onAppear(perform: authenticate)
}
func authenticate() {
// let context = LAContext()
var error: NSError?
// if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
// let reason = "Log in to your account by unlocking FaceID."
// context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in
// DispatchQueue.main.async {
// if success {
// // authenticated successfully
// self.isUnlocked = true
// } else {
// // there was a problem
// self.isUnlocked = false
// print(error?.localizedDescription ?? "Failed to authenticate")
// }
//
// }
// }
// } else {
// // no biometrics
// print(error?.localizedDescription ?? "Can't evaluate policy")
//
// }
}
}
struct ContentView: View {
var body: some View {
FaceLoginView()
}
}

Related

Why the init() is called when the wifi is turned off and on

When I turn the wifi on and off the mainChatView init is called again but not the landingView init. Why?
So when the wifi is toggled in the console it is printed main chat init again
this is the code of the landingView:
struct LandingView: View {
#State private var selection:Int = 2
#State var connected: Bool = true
#State var supposeToBeConnected: Bool = true
#StateObject var networkReachability = NetworkReachability()
var timer = Timer()
init(){
print("landing page init")
}
var body: some View {
if connected {
content
}else{
connectingView()
}
}
var content: some View {
ZStack{
VStack{
TabView(){
MainChatView()
.tabItem {
Image(systemName: "message")
}
HomeView()
.tabItem {
Image(systemName: "house.fill")
}
yourGroups()
.tabItem {
Image(systemName: "rectangle.3.group.fill")
}
AccontView()
.tabItem {
Image(systemName: "person.fill")
}
}
.padding(.all, -2.0)
.onAppear() {
UITabBar.appearance().barTintColor = .black
Auth.auth().addStateDidChangeListener { auth, user in
if let user = user {
if user != nil {
self.connected = true
self.supposeToBeConnected = true
}else{
self.supposeToBeConnected = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
if !supposeToBeConnected{
print("cont changed")
self.connected = false
}
}
}
}
}
OnligneStatus()
Timer.scheduledTimer(withTimeInterval: 60, repeats: true, block: { _ in
OnligneStatus()
})
}
.statusBar(hidden: true)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
}
.frame(maxWidth: .infinity, maxHeight: .infinity,alignment: .top)
.ignoresSafeArea(.all)
.padding(.all, -1.0)
}
}
func OnligneStatus(){
print("online")
let db = Firestore.firestore()
if Auth.auth().currentUser != nil{
db.collection("users").document(Auth.auth().currentUser!.uid).updateData([
"onLine":Date()])
}
}
}
And this is the init:
init(){
print("main chat init")
}
Thanks for helping me this bug caused image loading errors and text loading bugs

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

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

How to use ObservedObject with Google Sign In

I am trying to observe if user logged in or not by Google sign in SDK using SwiftUI . the sign in is working fine and the print out is showing user is logged in successfully , but the UI is not changing it is like the observe not working or I am sure I miss something there .
App :
#main
struct test_app: App {
#AppStorage("settings") private var settings: Data = Data()
#Environment(\.scenePhase) var scenePhase
#UIApplicationDelegateAdaptor(test_delegate.self) private var appDelegate
var userInfo:Bool = false
#ObservedObject static var gd = GoogleDelegate()
init(){
//Google sign in
GIDSignIn.sharedInstance().delegate = test_app.gd
//Firebase
FirebaseApp.configure()
print("[APP] Init complete.")
}
var body: some Scene {
WindowGroup {
if userInfo {
w_home().onOpenURL(perform: { url in
print("[DEEP LINK] Incoming url: \(url)")
})
}else{
w_splash()
}
}.onChange(of: scenePhase) { (newScenePhase) in
switch newScenePhase {
case .background:
print("[APP] State : Background")
case .inactive:
print("[APP] State : Inactive")
case .active:
print("[APP] State : Active")
#unknown default:
print("[APP] State : Unknown")
}
}
}
}
GoogleDelegate class :
class GoogleDelegate: NSObject, GIDSignInDelegate, ObservableObject {
#Published var signedIn: Bool = false
public func signIn(){
// Maksure that there must be a presenting controller as a container
GIDSignIn.sharedInstance()?.presentingViewController = UIApplication.shared.windows.first?.rootViewController
//Google sign in
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().signIn()
signedIn = false
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let error = error {
if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
print("[GSIN] The user has not signed in before or they have since signed out.")
} else {
print("[GSIN] \(error.localizedDescription)")
}
return
}
// If the previous `error` is null, then the sign-in was succesful
if GIDSignIn.sharedInstance().currentUser != nil {
print("[GSIN] Successful sign-in! \( String(describing: GIDSignIn.sharedInstance().currentUser!.profile.email) )")
}else{
print("[GSIN] Successful sign-in!")
}
self.signedIn = true
}
}
and finally the login :
struct w_splash: View {
let screenBounds:CGRect = UIScreen.main.bounds
var body: some View {
ZStack {
Image("logo").resizable()
.aspectRatio(contentMode: .fit)
.frame(width: screenBounds.width * 0.5, height: screenBounds.height * 0.2)
VStack {
Spacer()
HStack {
Button(action: {
test_app.gd.signIn()
}, label: {
HStack{
Image("btn_google_dark_normal_ios").resizable().frame(width: 50, height: 50, alignment: .center)
Text("Sign in with Google")
.font( Font.custom("Roboto-Medium", size: 14) )
.frame(width: .none,
height: 50,
alignment: .center)
.foregroundColor(Color.white)
.padding(.trailing, 30)
.padding(.leading, 10)
}
})
.isHidden(test_app.gd.signedIn)
.background(Color(hex: "#4285F4"))
.cornerRadius(5.5)
.padding(.bottom , screenBounds.height * 0.1)
.frame(width: screenBounds.width * 0.9,
height: 50,
alignment: .center)
.shadow(color: Color.black.opacity(0.2),
radius: 3,
x: 3,
y: 3)
}
}
}.onAppear{
if (test_app.gd.signedIn ){
print("user logged in test print out")
}
}
}
}
struct w_splash_Previews: PreviewProvider {
static var previews: some View {
w_splash()
}
}
I am still new with swiftUI, any ideas why I can't observe the variable?
It looks like you wanted to make GoogleDelegate a singleton.
I suggest you create a static instance of GoogleDelegate:
class GoogleDelegate: NSObject, GIDSignInDelegate, ObservableObject {
static let shared = GoogleDelegate()
#Published var signedIn: Bool = false
...
}
Then, in your views replace:
#ObservedObject static var gd = GoogleDelegate()
with:
#ObservedObject var gd = GoogleDelegate.shared
Also, note that you don't observe GoogleDelegate in your w_splash view, so you need to add it there as well:
struct w_splash: View {
#ObservedObject var gd = GoogleDelegate.shared
and refer directly to it using gd.signedIn (not test_app.gd.signedIn).
If you want to print something when gd.signedIn changes, you can use onChange:
.onChange(of: gd.signedIn) { signedIn in
print("signedIn: \(String(signedIn))")
}

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
}
}
}