Checking for Internet connection does not work - swift

I'm trying to check for the internet connection in my app, and currently, I have this code:
private let monitor: NWPathMonitor
monitor.pathUpdateHandler = { [weak self] path in
print(path.status)
self?.isConnected = path.status == .satisfied
}
However, this does not work. Specifically, the print does not print out the value in the debug console.
Could you please tell me what I have done wrong?
Thank you.

here is my (SwiftUI, sorry) test code that works for me.
You can probably recycle some of the code for your purpose.
import SwiftUI
import Foundation
import Network
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
let monitor = NWPathMonitor()
var body: some View {
Text("testing")
.onAppear {
let queue = DispatchQueue(label: "monitoring")
monitor.start(queue: queue)
monitor.pathUpdateHandler = { path in
print(path.status)
if path.status == .satisfied {
print("-----> YES connected")
} else {
print("-----> NOT connected")
}
}
}
}
}
You can also use specific monitor, such as let monitor = NWPathMonitor(requiredInterfaceType: .wifi)

Related

RealmSwift not syncing with MongoDB what is missed in this code?

I'm using realm backend for my swiftui project and it's never been synced so if I add object it's appear locally but not appears in mongodb app service,
so if I try to add object it's appears in local but it doesn't appear in app service and if I fetch the data it fetches the local data only and the items in app service collection doesn't appear
can someone help me what is missed in my code and here's my code
import SwiftUI
import RealmSwift
let app: RealmSwift.App? = RealmSwift.App(id: "swift-app-azpkw")
#main
struct RealAgenApp: SwiftUI.App {
var body: some Scene {
WindowGroup {
if let app = app {
AppContainer(app: app)
} else {
AuthView()
}
}
}
}
import SwiftUI
import RealmSwift
struct AppContainer: View {
#ObservedObject var app: RealmSwift.App
var body: some View {
if let user = app.currentUser {
let config = user.flexibleSyncConfiguration(initialSubscriptions: { subs in
if subs.first(named: "Property") != nil {
return
} else {
subs.append(QuerySubscription<Property>(name: "Property") {
$0.ownerId == user.id
})
}
})
AppView()
.environment(\.realmConfiguration, config)
} else {
AuthView()
}
}
}
import SwiftUI
import RealmSwift
struct AppView: View {
#AutoOpen(appId: "APP_ID", timeout: 4000) var autoOpen
var body: some View {
let _ = app?.currentUser // id: 637ceaf802e0885f39a06d71
switch autoOpen {
case .connecting:
MessageView(message: "Connecting")
case .waitingForUser:
MessageView(message: "Waiting for user to log in...")
case .open(let realm):
HomeView()
.environment(\.realm, realm)
case .progress(let progress):
LoadingView(progress: progress)
case .error(let error):
MessageView(message: error.localizedDescription)
.foregroundColor(.red)
}
}
}
import SwiftUI
import Realm
import RealmSwift
struct HomeView: View {
#ObservedResults(Property.self) var properties
var body: some View {
ForEach(properties) { category in
Text("Category")
}
}
}
Greetings
after a long search and reconfiguration of the app service I've just realized that the issue was initialSubscriptions of flexibleSyncCOnfiguration not calling and also I reconfigure the schema rules in App service UI
here's my final config initialize
let config = user.flexibleSyncConfiguration(initialSubscriptions: { subscriptions in
if let _ = subscriptions.first(named: "all-properties") {
return
} else {
subscriptions.append(QuerySubscription<Property>(name: "all-properties"))
}
}, rerunOnOpen: true)

SwiftUI AVPlayer Error Reporting/Handling

I've created a media app, which plays music via HTTP live stream in SwiftUI. The code (snippet) looks like following:
struct PlayerView: View {
#State var audioPlayer: AVPlayer!
#State var buttonSymbol: String = "pause.circle.fill"
let url: URL!
var body: some View {
VStack {
Text("Sound")
HStack {
Button(action: {
if self.audioPlayer.rate != 0.0 {
self.buttonSymbol = "play.circle.fill"
self.audioPlayer.pause()
} else {
self.buttonSymbol = "pause.circle.fill"
self.audioPlayer.play()
}
}) {
Image(systemName: self.buttonSymbol)
}
.buttonStyle(PlainButtonStyle())
}
}
.onAppear {let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSession.Category.playback, mode: .default, policy: AVAudioSession.RouteSharingPolicy.longFormAudio, options: [])
} catch let error {
fatalError("*** Unable to set up the audio session: \(error.localizedDescription)")
}
let item = AVPlayerItem(url: self.url)
self.audioPlayer = AVPlayer(playerItem: item)
audioSession.activate(options: []) { (success, error) in
guard error == nil else {
fatalError("An error occurred: \(error!.localizedDescription)")
return
}
self.audioPlayer.play()
}
}
}
}
My lack of understanding is, how do I receive the underlying AVPlayer/AVPlayerItem errors (for instance the HTTP stream is interrupted) in SwiftUI in order to show them in the UI? Which SwiftUI action I have to use and how?
According to the documentation AVPlayerItem has a notification called AVPlayerItemFailedToPlayToEndTime.
One possible implementation in a Viewmodel would be:
import Combine
....
playerItemFailedToPlayToEndTimeObserver = NotificationCenter
.default
.publisher(for: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime)
.sink(receiveValue: {[weak self] notification in
// handle notification here
})
You would need to hold a reference to playerItemFailedToPlayToEndTimeObserver else it will go out of scope as soon as the method calling this finished.

SwiftUI: Error validating an if based on whether or not there is an instance

I am making a music player in swiftui, there is a part of the code where an instance of the player (audioManager.player) on which several components depend, in order not to set the same value constantly I wrapped the playback part in a conditional (if let player = audioManager.player) based on whether or not there is a “player” instance, but when doing so the content disappears strangely, as if the “player” instance does not exist or is negative, I tried it just validating a part and I realized that everything works correctly but I don't know why the content is not displayed on the canvas or in the simulator. I would appreciate if you help me with a suggestion or solution, everything is welcome. ;)
import Foundation
import AVKit
final class AudioManager: ObservableObject {
var player: AVAudioPlayer?
func startPlayer(track: String, isPreview: Bool = false) {
guard let url = Bundle.main.url(forResource: track, withExtension: "mp3") else {
print("Resourse not found")
return
}
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVAudioPlayer(contentsOf: url)
if isPreview {
player?.prepareToPlay()
} else {
player?.play()
}
} catch {
print("Fail to initialize", error)
}
}
}
import SwiftUI
struct PlayerView: View {
#EnvironmentObject var audioManager: AudioManager
#State private var value: Double = 0.0
#State private var isEditing: Bool = false
var isPreview: Bool = false
let timer = Timer
.publish(every: 0.5, on: .main, in: .common)
.autoconnect()
var body: some View {
ZStack {
VStack(alignment: .center, spacing: 50) {
VStack(spacing: 10) {
// MARK: ** THE ERROR IS HERE **
if let player = audioManager.player {
Slider(value: $value, in: 0...player.duration)
.foregroundColor(.white)
.padding(.top)
}
} // VStack
.padding(.horizontal, 30)
} // VStack
.foregroundColor(.white)
}
.onAppear {
audioManager.startPlayer(track: "meditation1", isPreview: isPreview)
}
.onReceive(timer) { _ in
guard let player = audioManager.player, !isEditing else { return }
value = player.currentTime
}
}
}
struct PlayerView_Previews: PreviewProvider {
static var previews: some View {
Group {
PlayerView(isPreview: true)
.environmentObject(AudioManager())
}
}
}

SwiftUI Firebase Authentication dismiss view after successfully login

I'm a beginner iOS developer and I have a problem with my first application. I'm using Firebase as a backend for my app and I have already sign in and sing up methods implemented. My problem is with dismissing LoginView after Auth.auth().signIn method finishing. I've managed to do this when I'm using NavigationLink by setting ObservableObject in isActive:
NavigationLink(destination: DashboardView(), isActive: $isUserLogin) { EmptyView() }
It's working as expected: when app ending login process screen is going to next view - Dashboard.
But I don't want to use NavigationLink and creating additional step, I want just go back to Dashboard using:
self.presentationMode.wrappedValue.dismiss()
In this case I don't know how to force app to wait till method loginUser() ends. This is how my code looks now:
if loginVM.loginUser() {
appSession.isUserLogin = true
self.presentationMode.wrappedValue.dismiss()
}
I've tried to use closures but it doesn't work or I'm doing something wrong.
Many thanks!
You want to use a AuthStateDidChangeListenerHandle and #EnvrionmentObject, like so:
class SessionStore: ObservableObject {
var handle: AuthStateDidChangeListenerHandle?
#Published var isLoggedIn = false
#Published var userSession: UserModel? { didSet { self.willChange.send(self) }}
var willChange = PassthroughSubject<SessionStore, Never>()
func listenAuthenticationState() {
handle = Auth.auth().addStateDidChangeListener({ [weak self] (auth, user) in
if let user = user {
let firestoreUserID = API.FIRESTORE_DOCUMENT_USER_ID(userID: user.uid)
firestoreUserID.getDocument { (document, error) in
if let dict = document?.data() {
//Decoding the user, you can do this however you see fit
guard let decoderUser = try? UserModel.init(fromDictionary: dict) else {return}
self!.userSession = decoderUser
}
}
self!.isLoggedIn = true
} else {
self!.isLoggedIn = false
self!.userSession = nil
}
})
}
func logOut() {
do {
try Auth.auth().signOut()
print("Logged out")
} catch let error {
debugPrint(error.localizedDescription)
}
}
func unbind() {
if let handle = handle {
Auth.auth().removeStateDidChangeListener(handle)
}
}
deinit {
print("deinit - seession store")
}
}
Then simply do something along these lines:
struct InitialView: View {
#EnvironmentObject var session: SessionStore
func listen() {
session.listenAuthenticationState()
}
var body: some View {
ZStack {
Color(SYSTEM_BACKGROUND_COLOUR)
.edgesIgnoringSafeArea(.all)
Group {
if session.isLoggedIn {
DashboardView()
} else if !session.isLoggedIn {
SignInView()
}
}
}.onAppear(perform: listen)
}
}
Then in your app file, you'd have this:
InitialView()
.environmentObject(SessionStore())
By using an #EnvironmentObject you can now access the user from any view, furthermore, this also allows to track the Auth status of the user meaning if they are logged in, then the application will remember.

Swift Playground didn't recognize structs in other folders

I'm using AVAudioEngine of AVFoundation with SwiftUI in Swift Playground and using MVVM as Architecture. But if I separate my structs in files, I receive two errors: Execution was interrupted, reason: signal SIGABRT. and Cannot find file in scope. The only way that all works is using them in the same file.
Here is the playground base code, where I call MusicCreatorView.
import SwiftUI
import PlaygroundSupport
public struct StartView: View {
public var body: some View {
ZStack {
Rectangle()
.fill(Color.white)
.frame(width: 400, height: 400, alignment: .center)
NavigationView {
VStack {
NavigationLink("Start", destination: MusicCreatorView())
}
}
}
}
}
PlaygroundPage.current.setLiveView(StartView()) // error: Execution was interrupted, reason: signal SIGABRT.
AudioEngine can be found by StartView (if called at StartView, just move SIGABRT error from PlaygroundPage.current.setLineView() to where it is called) but can't be found by MusicCreatorView.
import Foundation
import AVFoundation
public struct AudioEngine {
public var engine = AVAudioEngine()
public var player = AVAudioPlayerNode()
public var audioBuffer: AVAudioPCMBuffer?
public var audioFormat: AVAudioFormat?
public var audioFile: AVAudioFile? {
didSet {
if let audioFile = audioFile {
audioFormat = audioFile.fileFormat
}
}
}
public var audioFileURL: URL? {
didSet {
if let audioFileURL = audioFileURL {
audioFile = try? AVAudioFile(forReading: audioFileURL)
}
}
}
public init() {
setupAudio()
}
public mutating func setupAudio() {
audioFileURL = Bundle.main.url(forResource: "EverybodysCirculation", withExtension: "mp3")
guard let format = audioFormat else { return }
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: format)
engine.prepare()
do {
try engine.start()
} catch {
print(error.localizedDescription)
}
}
public func scheduleAudioFile() {
guard let audioFile = audioFile else { return }
player.scheduleFile(audioFile, at: nil, completionHandler: nil)
}
public func playSound() {
player.isPlaying ? player.pause() : player.play()
}
}
Trying to call AudioEngine at MusicCreatorView
import Foundation
import SwiftUI
import AVFoundation
public struct MusicCreatorView: View {
var audioEngine = AudioEngine() // Cannot find 'AudioEngine' in scope
public init() {}
public var body: some View {
Text("Try to Create your own music")
Button("play") {
print("apertou")
audioEngine.playSound() // audioEngine <<error type>>
}
}
}
Here is how my files are organized
https://i.stack.imgur.com/losWf.png
Multiple files in Sources cannot see each other. They are just independent libraries available to the actual playground. You should be developing this in a real iOS project, rather than a playground.
You just need to explicitly declare stuff as public. Its annoying, and sometimes I need to close and reopen the playground for the scoping to take effect after marking them public.