Monitor internet connection using swiftui - networking

I can't find what is wrong, I'm try to monitor the internet connectivity using swiftui.
I follow some tutorial online..
but it doest work for me.
can someone help me to find what is wrong on this code?
I'm using the simulator for testing, when I switch off the wifi of the iMac the variable isConnected still print out "we have internet!" when I switch on the wifi it print "no internet"
what is wrong in this code?
class DataManager: ObservableObject {
let monitor = NWPathMonitor()
let queue = DispatchQueue.global(qos: .background)
#Published var isConnected = true
init() {
monitor.pathUpdateHandler = { path in
DispatchQueue.main.async {
// codice eseguito sul thread principale
if path.status == .satisfied {
print("Yay! We have internet!")
self.isConnected = true
} else {
print("NOOO! no internet!")
self.isConnected = false
}
}
}
monitor.start(queue: queue)
}
}
struct ContentView: View {
#ObservedObject var dm = DataManager()
var body: some View {
VStack {
if dm.isConnected == true {
Text("connesso")
} else {
Text("non connesso")
}
}
}
}

Related

Can You Create Singletons With MVVM in SwiftUI

I am working on a SwiftUI project and attempting to use MVVM. I am using Firebase as a backend and recently introduced rules on my Firestore database and am having a lot of trouble which I believe is related to my implementation of MVVM.
I followed a tutorial from Google on MVVM and I believe the larger project I am working on is causing issues. Here is a base case for how I'm using MVVM.
Repository Files
These files are being used connect to Firestore. Here is a UserRepository example.
class ListingRepository: ObservableObject {
let db = Firestore.firestore()
private var snapshotListener: ListenerRegistration?
#Published var listings = [Listing]()
init() {
startSnapshotListener()
}
func startSnapshotListener() {
if snapshotListener == nil {
self.snapshotListener = db.collection(FirestoreCollection.listings).order(by: "created", descending: true).addSnapshotListener { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error)")
} else {
guard let documents = querySnapshot?.documents else {
print("No Listings.")
return
}
// Documents exist.
self.listings = documents.compactMap { listing in
do {
return try listing.data(as: Listing.self)
} catch {
print(error)
}
return nil
}
}
}
}
}
func stopSnapshotListener() {
if snapshotListener != nil {
snapshotListener?.remove()
snapshotListener = nil
}
}
}
ViewModel and #main
In the app. Listings are a part of a Marketplace. I am making my ViewModel the #EnvironmentObject. In #main I have the following. I am initializing an instance of ListingRepository here.
#main
struct ExchangeApp: App {
// #EnvironmentObjects
#StateObject private var marketplaceViewModel = MarketplaceViewModel(listingRepository: ListingRepository())
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(marketplaceViewModel)
}
}
}
MarketplaceViewModel
class MarketplaceViewModel: ObservableObject {
var listingRepository: ListingRepository
#Published var listingRowViewModels = [ListingRowViewModel]()
private var cancellables = Set<AnyCancellable>()
init(listingRepository: ListingRepository) {
self.listingRepository = listingRepository
self.startCombine()
}
func startCombine() {
listingRepository
.$listings
.receive(on: RunLoop.main)
.map { listings in
listings
.map { listing in
ListingRowViewModel(listing: listing)
}
}
.assign(to: \.listingRowViewModels, on: self)
.store(in: &cancellables)
}
}
MarketplaceView
In MarketplaceView I take each listing and present it. There are other files here that are not shown but I don't believe this is important to the question. I am happy to provide more if needed.
struct MarketplaceView: View {
let db = Firestore.firestore()
#EnvironmentObject var authSession: AuthSession
#EnvironmentObject var marketplaceViewModel: MarketplaceViewModel
var body: some View {
NavigationView {
List {
ForEach(self.marketplaceViewModel.filteredListingRowViewModels, id: \.id) { listingRowViewModel in
NavigationLink(destination: ListingDetailView(listingDetailViewModel: ListingDetailViewModel(listing: listingRowViewModel.listing, userRepository: UserRepository()))
.environmentObject(authSession)
) {
ListingRowView(listingRowViewModel: listingRowViewModel)
}
}
}
.navigationTitle("Marketplace")
}
}
}
While this works really well, I am not sure its expands to a larger app well. I have a lot of Repository files and view models which means my #main ends up looking like the following.
#main
#main
struct Global_Seafood_ExchangeApp: App {
#StateObject private var authListener = AuthSession(userRepository: UserRepository(), productRepository: ProductRepository(), listingRepository: ListingRepository(), offerRepository: OfferRepository(), orderRepository: OrderRepository(), businessAddressRepository: BusinessAddressRepository(), bankAccountRepository: BankAccountRepository())
#StateObject private var marketplaceViewModel = MarketplaceViewModel(listingRepository: ListingRepository())
#StateObject private var createListingViewModel = CreateListingViewModel(listingRepository: ListingRepository(), productRepository: SeafoodRepository())
#StateObject private var accountViewModel = AccountViewModel(listingRepository: ListingRepository(), offerRepository: OfferRepository(), orderRepository: OrderRepository())
#StateObject private var addBusinessViewModel = AddBusinessAddressViewModel(businessAddressRepository: BusinessAddressRepository())
#StateObject private var addBankAccountViewModel = AddBankAccountViewModel(bankAccountRepository: BankAccountRepository())
#StateObject private var listingHistoryViewModel = ListingHistoryViewModel(listingRepository: ListingRepository())
#StateObject private var offerHistoryViewModel = OfferHistoryViewModel(offerRepository: OfferRepository())
#StateObject private var orderHistoryViewModel = OrderHistoryViewModel(orderRepository: OrderRepository())
#StateObject private var businessAddressViewModel = BusinessAddressViewModel(businessAddressRepository: BusinessAddressRepository())
#StateObject private var bankAccountViewModel = BankAccountViewModel(bankAccountRepository: BankAccountRepository())
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(authListener)
.environmentObject(marketplaceViewModel)
.environmentObject(createListingViewModel)
.environmentObject(accountViewModel)
.environmentObject(addBusinessViewModel)
.environmentObject(addBankAccountViewModel)
.environmentObject(listingHistoryViewModel)
.environmentObject(offerHistoryViewModel)
.environmentObject(orderHistoryViewModel)
.environmentObject(businessAddressViewModel)
.environmentObject(bankAccountViewModel)
}
}
}
You can see here that Repositories are initialized multiple times. I don't think this is a good idea.
My issue really started when I added Firestore rules to my database. I am requiring someone to be authenticated to view data from the repositories. When someone logs in, I make a call to start all of the repositories and set up the Snapshot Listeners, and then when someone logs out I make a call to stop all of the Snapshot Listeners. However, even after stopping them, multiple are still running which I believe is due to the fact that I've initialized more than one in #main. I have confirmed this since I get permission request errors from Firestore letting me know the listeners are returning errors since the user is not authenticated.
Singletons
I looked into using Singletons but this seems to be a bad design choice.
StateObject
I looked at using the Repositories as StateObjects but this creates UI update issues.
My two questions.
Can someone confirm my issues is that I am instantiating multiple instances of Repositories?
What MVVM practice is best for an app this size? I understand this is a loaded question but I've read a number of tutorials and all seem to say they are a good idea. However, in practice I am running into issues with the ones I've tried.
Any help would be greatly appreciated.

MPNowPlayingInfoCenter does not set player controls when running iOS app on macOS with M1

I want to use iOS app on Mac with M1. It mainly works as intended in "Designed for iPad" mode, except player controls do not appear in control center as they are on iOS.
Here is the important part of the code that works perfect on iOS:
import AVFoundation
import MediaPlayer
class RemoteCommandHandler: NSObject {
static let shared = RemoteCommandHandler()
weak var delegate: MainVCDelegate?
private var player: AVAudioPlayer?
private let audioSession = AVAudioSession.sharedInstance()
private let commandCenter = MPRemoteCommandCenter.shared()
private var nowPlayingInfo = [String: Any]()
private var obs: NSKeyValueObservation?
func initPlayback() {
if obs == nil {
do {
try audioSession.setCategory(.playback, mode: .default, options: [])
try audioSession.setActive(true)
} catch {
//error
}
obs = audioSession.observe( \.outputVolume ) { avSession, _ in
self.delegate?.setVolume(avSession.outputVolume)
}
setupRemoteTransportControls()
if player == nil {
do {
try player = AVAudioPlayer(contentsOf:
URL(fileURLWithPath: Bundle.main.path(forResource: "TestTrack", ofType: "m4a")!))
player?.delegate = self
player?.volume = 0
player?.numberOfLoops = 1
} catch let error as NSError {
//error
}
}
}
}
func setupNowPlaying() {
nowPlayingInfo[MPMediaItemPropertyTitle] = TrackInfo.title.userFriendlyString()
nowPlayingInfo[MPMediaItemPropertyArtist] = TrackInfo.artist.userFriendlyString()
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = TrackInfo.album.userFriendlyString()
let image = NowPlayingInfo.getArtwork()
var artwork: MPMediaItemArtwork
artwork = MPMediaItemArtwork(boundsSize: image.size) { _ in image }
nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = PlayerStatusInfo.isPlaying() ? 1 : 0
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = NowPlayingInfo.getDurationMs() / 1000
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = NowPlayingInfo.currentMs / 1000
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackProgress] = NowPlayingInfo.calculateProgress()
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
}
Would appreciate any suggestions on how to make it work on Mac.

Xamarin Forms: How to implement location share feature?

Whenever I am opening my app I need to check the location is on or off. If the location is off, I need to show an alert to the user to enable location share like the below screenshot:
I try this using the dependency service from this thread:
The interface on shared Project:
public interface ILocSettings
{
void OpenSettings();
}
Android implementation
[assembly: Dependency(typeof(LocationShare))]
namespace ProjectName.Droid
{
public class LocationShare : ILocSettings
{
public void OpenSettings()
{
//code1
Android.App.Application.Context.StartActivity(new Android.Content.Intent(Android.Provider.Settings.ActionLocat‌​ionSourceSettings));
//code2
//LocationManager LM = (LocationManager)Android.App.Application.Context.GetSystemService(Context.LocationService);
//if (LM.IsProviderEnabled(LocationManager.GpsProvider) == false)
//{
// Context ctx = Android.App.Application.Context;
// ctx.StartActivity(new Intent(Android.Provider.Settings.ActionLocationSourceSettings));
//}
}
}
}
Finally from the shared project called like below:
var myAction = await DisplayAlert("Location", "Please Turn On Location", "OK", "CANCEL");
if (myAction)
{
if (Device.RuntimePlatform == global::Xamarin.Forms.Device.Android)
{
DependencyService.Get<ILocSettings>().OpenSettings();
}
}
else
{
await DisplayAlert("Alert", "User Denied Permission", "OK");
}
I am getting below exception when running this. (Getting the same exception for code1 and code2)
System.NullReferenceException: 'Object reference not set to an instance of an object.'
I need to show the alert only if the location is off. If the location is on, no need to do these things. How I can check the location is on or off?
Also, I need to implement the same feature for ios and windows platforms.
Update 1
Hi #Lucas Zhang - MSFT
I have tried your solution and got an alert like this. But after giving the location access, still the device's location is off. I need to on the device's location like this when the user taps the OK option in the alert (question screenshot). Either on the location directly or redirect to the location settings page.
Update 2
Tried GeolocatorPlugin and used the below code for checking the GPS is off or on. Always getting False value even if the GPS is on.
public bool IsLocationAvailable()
{
if (!CrossGeolocator.IsSupported)
return false;
return CrossGeolocator.Current.IsGeolocationAvailable;
}
Made below modification on the android service and now I am able to open the location settings.
public class LocationShare : ILocSettings
{
public void OpenSettings()
{
Intent intent = new Android.Content.Intent(Android.Provider.Settings.ActionLocat‌​ionSourceSettings);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
Before opening the location settings page, I need to verify the GPS is on or off (not the location permission).
Also I didn't understand the ios answer by Jack. So can you show me the ios dependency service like I did for android for opening ios location settings page?
In your case you could use the plugin PermissionsPlugin from Nuget.
Usage
try
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync<LocationPermission>();
if (status != PermissionStatus.Granted)
{
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Location))
{
await DisplayAlert("Need location", "Gunna need that location", "OK");
}
status = await CrossPermissions.Current.RequestPermissionAsync<LocationPermission>();
}
if (status == PermissionStatus.Granted)
{
//Query permission
}
else if (status != PermissionStatus.Unknown)
{
//location denied
}
}
catch (Exception ex)
{
//Something went wrong
}
Update
It seems that you want to check if system location is open or not , right ? If so , you could try to achieve GPS info after you get the location permission . If the GPS info is still unavailable , that means the system setting is OFF .And you can invoke dependency service to open platform setting page.
public async void ShareLocation()
{
var status = await Permissions.RequestAsync<Permissions.LocationAlways>();
if (status == PermissionStatus.Granted)
{
bool gpsStatus = DependencyService.Get<ILocSettings>().isGpsAvailable();
if (!gpsStatus)
{
var myAction = await DisplayAlert("Location", "Please turn on GPS for the proper working of the application.", "TURN ON", "CANCEL");
if (myAction)
{
DependencyService.Get<ILocSettings>().OpenSettings();
}
}
}
}
//ILocSettings
public interface ILocSettings
{
void OpenSettings();
bool isGpsAvailable();
}
//Android Dependency Service
[assembly: Dependency(typeof(LocationShare))]
namespace Projectname.Droid.Services
{
public class LocationShare : ILocSettings
{
public bool isGpsAvailable()
{
bool value = false;
Android.Locations.LocationManager manager = (Android.Locations.LocationManager)Android.App.Application.Context.GetSystemService(Android.Content.Context.LocationService);
if (!manager.IsProviderEnabled(Android.Locations.LocationManager.GpsProvider))
{
//gps disable
value = false;
}
else
{
//Gps enable
value = true;
}
return value;
}
public void OpenSettings()
{
Intent intent = new Android.Content.Intent(Android.Provider.Settings.ActionLocat‌​ionSourceSettings);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
}
For iOS
public void CheckAuthorization(CLLocationManager manager, CLAuthorizationStatus status)
{
switch (status)
{
case CLAuthorizationStatus.Authorized | CLAuthorizationStatus.AuthorizedAlways | CLAuthorizationStatus.AuthorizedWhenInUse:
Console.WriteLine("Access");
break;
case CLAuthorizationStatus.Denied | CLAuthorizationStatus.NotDetermined | CLAuthorizationStatus.Restricted:
Console.WriteLine("No Access");
break;
default:
Console.WriteLine("No Access");
break;
}
}
UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString));

Switch camera on webview video call

I have app in xamarin form where i am accessing webview for video call. Everything is working fine just i need to know how i can switch back/front camera during call? as when video call start front camera open by default.
Code for initializing video call
function initializeLocalMedia(options, callback) {
if(options) {
options['audio'] = true;
if(options['video'])
options['video'] = true;
} else {
options['audio'] = true;
options['video'] = false;
}
// Get audio/video stream
navigator.getUserMedia(options, function(stream) {
// Set your video displays
window.localStream = stream;
myapp.setMyVideo(window.localStream)
if(callback)
callback();
}, function(err) {
console.log("The following error occurred: " + err.name);
alert('Unable to call ' + err.name)
});
}
Going straight to code then it should look like:
Camera.CameraInfo camInfo = new Camera.CameraInfo ();
for (int i = 0; i < Camera.NumberOfCameras; i++) {
Camera.GetCameraInfo (i, camInfo);
if (camInfo.Facing == CameraFacing.Front){
try {
return Camera.Open(i);
} catch (Exception e) {
// log or something
}
}
}
return null;
What we are doing is iterating over the hardware and then check to match the front camera, if it match then do the things. Same is true for back camera too

cannot connect to itunes store xamarin forms

enter image description hereHi Thanks in advance i have facing a problem in my xamarin forms ios. Problem is that when i want to purchase product it thrown an exception that cannot to connect to itune store my same code in working fine on xamarin forms android.My code for restore purchases is working fine.
Here is my code for make purchases
private async Task<bool> MakePurchase(string ProductId, string Payload)
{
if (!CrossInAppBilling.IsSupported)
{
return false;
}
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync();
if (!connected)//Couldn't connect to billing, could be offline,
alert user
{
DependencyService.Get<IToast>().Show("Something went
wrong or you may not connected with the internet!");
return false;
}
//try to purchase item
var purchase = await billing.PurchaseAsync(ProductId,
ItemType.InAppPurchase, Payload);
if (purchase == null)
{
return false;
}
else
{
//Purchased, save this information
var responseId = purchase.Id;
var responseToken = purchase.PurchaseToken;
var state = purchase.State;
return true;
}
}
catch (InAppBillingPurchaseException ex)
{
if (ex.PurchaseError == PurchaseError.DeveloperError)
{
DependencyService.Get<IToast>().Show("DeveloperError");
ex.Message.ToString();
}
else if (ex.PurchaseError == PurchaseError.AlreadyOwned)
{
DependencyService.Get<IToast>().Show("AlreadyOwned");
return true;
}
else if(ex.PurchaseError == PurchaseError.BillingUnavailable)
{
DependencyService.Get<IToast>
().Show("BillingUnavailable");
return false;
}
else if(ex.PurchaseError == PurchaseError.InvalidProduct)
{
DependencyService.Get<IToast>().Show("InvalidProduct");
return false;
}
else if(ex.PurchaseError == PurchaseError.ItemUnavailable)
{
DependencyService.Get<IToast>().Show("ItemUnavailable");
return false;
}
else if(ex.PurchaseError == PurchaseError.GeneralError)
{
DependencyService.Get<IToast>().Show("General Error");
return false;
}
//Something bad has occurred, alert user
}
finally
{
//Disconnect, it is okay if we never connected
await billing.DisconnectAsync();
}
return false;
}

Resources