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)
Related
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)
Situation
Implement a multi window application, where each window has its own state.
Example
Here is an example (on github) to showcase the question:
import SwiftUI
#main
struct multi_window_menuApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}.commands {
MenuCommands()
}
}
}
struct ContentView: View {
#StateObject var viewModel: ViewModel = ViewModel()
var body: some View {
TextField("", text: $viewModel.inputText)
.disabled(true)
.padding()
}
}
public class ViewModel: ObservableObject {
#Published var inputText: String = "" {
didSet {
print("content was updated...")
}
}
}
Question
How should we programmatically figure out what is the currently selected view so we can update the state when the menu command is about to finish and update the state in the view model?
import Foundation
import SwiftUI
import Combine
struct MenuCommands: Commands {
var body: some Commands {
CommandGroup(after: CommandGroupPlacement.newItem, addition: {
Divider()
Button(action: {
let dialog = NSOpenPanel();
dialog.title = "Choose a file";
dialog.showsResizeIndicator = true;
dialog.showsHiddenFiles = false;
dialog.allowsMultipleSelection = false;
dialog.canChooseDirectories = false;
if (dialog.runModal() == NSApplication.ModalResponse.OK) {
let result = dialog.url
if (result != nil) {
let path: String = result!.path
do {
let string = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8)
print(string)
// how to get access to the currently active view model to update the inputText variable?
// viewModel.inputText = string
}
catch {
print("Error \(error)")
}
}
} else {
return
}
}, label: {
Text("Open File")
})
.keyboardShortcut("O", modifiers: .command)
})
}
}
Links that might be useful to figure this out:
http://www.gfrigerio.com/build-a-macos-app-with-swiftui/
https://troz.net/post/2021/swiftui_mac_menus/
https://onmyway133.com/posts/how-to-manage-windowgroup-in-swiftui-for-macos/
Useful links:
How to access NSWindow from #main App using only SwiftUI?
How to access own window within SwiftUI view?
https://lostmoa.com/blog/ReadingTheCurrentWindowInANewSwiftUILifecycleApp/
(this is what I was able to come up with, if anyone has a better idea/approach, please share)
The idea is to create a shared "global" view model that keeps track of opened windows and view models. Each NSWindow has an attribute with a unique windowNumber. When a window becomes active (key), it looks up the view model by the windowNumber and sets it as the activeViewModel.
import SwiftUI
class GlobalViewModel : NSObject, ObservableObject {
// all currently opened windows
#Published var windows = Set<NSWindow>()
// all view models that belong to currently opened windows
#Published var viewModels : [Int:ViewModel] = [:]
// currently active aka selected aka key window
#Published var activeWindow: NSWindow?
// currently active view model for the active window
#Published var activeViewModel: ViewModel?
func addWindow(window: NSWindow) {
window.delegate = self
windows.insert(window)
}
// associates a window number with a view model
func addViewModel(_ viewModel: ViewModel, forWindowNumber windowNumber: Int) {
viewModels[windowNumber] = viewModel
}
}
Then, react on every change on window (when it is being closed and when it becomes an active aka key window):
import SwiftUI
extension GlobalViewModel : NSWindowDelegate {
func windowWillClose(_ notification: Notification) {
if let window = notification.object as? NSWindow {
windows.remove(window)
viewModels.removeValue(forKey: window.windowNumber)
print("Open Windows", windows)
print("Open Models", viewModels)
}
}
func windowDidBecomeKey(_ notification: Notification) {
if let window = notification.object as? NSWindow {
print("Activating Window", window.windowNumber)
activeWindow = window
activeViewModel = viewModels[window.windowNumber]
}
}
}
Provide a way to lookup window that is associated to the current view:
import SwiftUI
struct HostingWindowFinder: NSViewRepresentable {
var callback: (NSWindow?) -> ()
func makeNSView(context: Self.Context) -> NSView {
let view = NSView()
DispatchQueue.main.async { [weak view] in
self.callback(view?.window)
}
return view
}
func updateNSView(_ nsView: NSView, context: Context) {}
}
Here is the view that is updating the global view model with the current window and viewModel:
import SwiftUI
struct ContentView: View {
#EnvironmentObject var globalViewModel : GlobalViewModel
#StateObject var viewModel: ViewModel = ViewModel()
var body: some View {
HostingWindowFinder { window in
if let window = window {
self.globalViewModel.addWindow(window: window)
print("New Window", window.windowNumber)
self.globalViewModel.addViewModel(self.viewModel, forWindowNumber: window.windowNumber)
}
}
TextField("", text: $viewModel.inputText)
.disabled(true)
.padding()
}
}
Then we need to create the global view model and send it to the views and commands:
import SwiftUI
#main
struct multi_window_menuApp: App {
#State var globalViewModel = GlobalViewModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(self.globalViewModel)
}
.commands {
MenuCommands(globalViewModel: self.globalViewModel)
}
Settings {
VStack {
Text("My Settingsview")
}
}
}
}
Here is how the commands look like, so they can access the currently selected/active viewModel:
import Foundation
import SwiftUI
import Combine
struct MenuCommands: Commands {
var globalViewModel: GlobalViewModel
var body: some Commands {
CommandGroup(after: CommandGroupPlacement.newItem, addition: {
Divider()
Button(action: {
let dialog = NSOpenPanel();
dialog.title = "Choose a file";
dialog.showsResizeIndicator = true;
dialog.showsHiddenFiles = false;
dialog.allowsMultipleSelection = false;
dialog.canChooseDirectories = false;
if (dialog.runModal() == NSApplication.ModalResponse.OK) {
let result = dialog.url
if (result != nil) {
let path: String = result!.path
do {
let string = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8)
print("Active Window", self.globalViewModel.activeWindow?.windowNumber)
self.globalViewModel.activeViewModel?.inputText = string
}
catch {
print("Error \(error)")
}
}
} else {
return
}
}, label: {
Text("Open File")
})
.keyboardShortcut("O", modifiers: [.command])
})
}
}
All is updated and runnable under this github project: https://github.com/ondrej-kvasnovsky/swiftui-multi-window-menu
I came across this question when I was solving a similar problem. I believe the SwiftUI way is to use FocusedValue:
// create an active viewmodel key
struct ActiveViewModelKey: FocusedValueKey {
typealias Value = ViewModel
}
extension FocusedValues {
var activeViewModel: ViewModel? {
get { self[ActiveViewModelKey.self] }
set { self[ActiveViewModelKey.self] = newValue }
}
}
struct ContentView: View {
#StateObject var viewModel: ViewModel = ViewModel()
var body: some View {
TextField("", text: $viewModel.inputText)
...
.focusedValue(\.activeViewModel, viewModel) // inject the focused value
}
}
struct MenuCommands: Commands {
#FocusedValue(\.activeViewModel) var activeViewModel // inject the active viewmodel
var body: some Commands {
CommandGroup(after: CommandGroupPlacement.newItem, addition: {
Divider()
Button(action: {
...
activeViewModel?.inputText = string
}, label: {
Text("Open File")
})
.keyboardShortcut("O", modifiers: [.command])
})
}
}
I have the following code:
import SwiftUI
struct RootView: View {
#ObservedObject var authentication: AuthenticationModel
var body: some View {
ZStack {
if self.authentication.loading {
Text("Loading")
} else if self.authentication.userId == nil {
SignInView()
} else {
ContentView()
}
}
}
}
However, the #ObservedObject's changes doesn't seem to trigger the switch to the other views. I can "fix" this by rendering
var body: some View {
VStack {
Text("\(self.authentication.loading ? "true" : "false") \(self.authentication.userId ?? "0")")
}.font(.largeTitle)
ZStack {
if self.authentication.loading {
Text("Loading")
} else if self.authentication.userId == nil {
SignInView()
} else {
ContentView()
}
}
}
and suddenly it starts working. Why does #ObservedObject not seem to trigger a rerender if the watched properties are only used in conditionals?
The code for AuthenticationModel is:
import SwiftUI
import Combine
import Firebase
import FirebaseAuth
class AuthenticationModel: ObservableObject {
#Published var userId: String?
#Published var loading = true
init() {
// TODO: Properly clean up this handle.
Auth.auth().addStateDidChangeListener { [unowned self] (auth, user) in
self.userId = user?.uid
self.loading = false
}
}
}
I think the problem could be that you aren't creating an instance of AuthenticationModel.
Can you try the following in RootView?:
#ObservedObject var authentication = AuthenticationModel()
I am using the new MultiPlatform SwiftUI Document template in Xcode 12 and I don't understand how to get access to the current FileDocument from within a menu item.
My app code looks like this (straight from the template).
#main
struct MyApp: App {
var body: some Scene {
DocumentGroup(newDocument: MyDocument()) { file in
ContentView(document: file.$document)
}
.commands {
CommandMenu("Utilities") {
Button(action: {
// How to get access to the current FileDocument ?
}) {
Text("Test")
}
}
}
}
}
You can create a FocusedValueKey, publish a binding to the document from your content view, and then observe it in the menu using #FocusedBinding. There is a great explanation here.
Here is a demo of possible approach. The idea is to use app state object to store current document in it and access from any place needed.
Tested with Xcode 12 / iOS 14
#main
struct MyApp: App {
#StateObject var appState = AppState()
var body: some Scene {
DocumentGroup(newDocument: MyDocument()) { file in
createView(for: file)
}
.commands {
CommandMenu("Utilities") {
Button(action: {
if let doc = appState.currentDocument {
// do something
}
}) {
Text("Test")
}
}
}
}
private func createView(for file: FileDocumentConfiguration<MyDocument>) -> some View {
appState.currentDocument = file.document
return ContentView(document: file.document)
}
}
class AppState: ObservableObject {
#Published var currentDocument: MyDocument?
}
To answer my own question, this is how I solved it. You can just send a signal from the .commands section via Combine.
#main
struct MyApp: App {
private let exportAsImage = PassthroughSubject<Void, Never>()
var body: some Scene {
DocumentGroup(newDocument: MyDocument()) { file in
ContentView(document: file.$document)
.onReceive(exportAsImage) { _ in
file.document.exportAsImage()
}
}
.commands {
CommandGroup(replacing: .importExport) {
Button(action: {
exportAsImage.send()
}) {
Text("Export as Image...")
}
}
}
}
}
So, I’m new to this. I was recently building an export file build on my iPad on swift playgrounds, and i came across this in #Environment(/.exportFiles)
In the error, the (/.exportFiles) is highlited. A fix to this would be highly appreciated
`
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
#Environment(\.exportFiles) var exportAction
var body: some View {
Button("Export Your file") {
let url = Bundle.main.url(forResource: "symbols",
withExtension: "json")!
exportAction(moving: url) {result in
switch result {
case .success(let url):
print("Success! Moved to: /url")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
PlaygroundPage.current.setLiveView(ContentView())
`
You're using APIs from the iOS 14 beta that were replaced in the final release. That's why the environment value doesn't exist.
From the iOS 14 release notes:
The ImportFilesAction and ExportFilesAction APIs have been replaced
with a collection of new view modifiers.
Use the new .fileImporter() modifier to present a system interface for
importing one or more files into your app, and the new .fileMover()
modifier to move one or more existing files to a new location. The
following is an example of a simple UI for importing and moving files:
Use the new .fileExporter() modifier to present a system interface for
exporting one or more documents from your app. In this example, an app
provides a simple note-taking interface for quickly jotting down some
text and then exporting it to disk:
struct FileMover : View {
#Binding var selectedFiles: [URL]
var includeDirectories: Bool = false
#State private var isImporting: Bool = false
#State private var isMovingSelection: Bool = false
var body: some View {
List(selectedFiles, id: \.self) { url in
Text(url.absoluteString)
}
.toolbar {
Button("Import", action: { isImporting = true })
Button("Move", action: { isMovingSelection = true })
}
.fileImporter(
isPresented: $isImporting,
allowedContentTypes: includeDirectories ? [.item, .directory] : [.item],
allowsMultipleSelection: true
) { result in
do {
selectedFiles = try result.get()
} catch {
// Handle failure.
}
}
.fileMover(isPresented: $isMovingSelection, files: selectedFiles) {
if case .success = $0 {
selectedFiles = []
} else {
// Handle failure.
}
}
}
}