I am making a client that connect to a websocket and shows different pages. I am looking for help on how I should order my code. First the user logs in and it connects them to the websocket to verify and if they are authorized it will continue, if not it will show an error.
TLDR
My code need to somehow connect to a websocket, go to a new page, and show info. My code also needs to alert the user about any errors.
var request = URLRequest(url: URL(string: "wss://link")!)
struct LoginView: View {
#State private var loggedIn = false
#State private var loginCode = ""
var body: some View {
NavigationView {
VStack(spacing: 30) {
NavigationLink(destination: newView().navigationBarHidden(true), isActive: $loggedIn) { EmptyView() }
HStack {
Image(systemName: "person").foregroundColor(.gray)
TextField("Login Code", text: $loginCode)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.frame(width: 300)
.offset(y: -75)
})
.buttonStyle(ContainedButtonStyle())
.offset(y: -50)
}
.navigationBarTitle("Log In")
}
}
}
func connectToSocket(loginCode: String) {
request.addValue("Bearer \(loginCode)", forHTTPHeaderField: "Authorization")
let ws = WebSocket(request: request)
struct newView: View {
#State private var accountsCheckedShown = false
let username = "MarcoD1337"
let games = 5
let lives = 10
let money = "$5/$5"
let status = "Ready For Cashout"
var body: some View {
NavigationView {
TabView {
ZStack {
List{
HStack {
ZStack {
Circle().fill(Color(red: 0.913, green: 0.737, blue: 0.447, opacity: 1))
.frame(maxWidth: 50, maxHeight: 50)
Text("🇮🇱")
.font(.largeTitle)
.multilineTextAlignment(.center)
}
VStack(alignment: .leading) {
Text(username)
.font(.title)
.fontWeight(.bold)
.minimumScaleFactor(0.25)
Text("Games: \(games)")
.minimumScaleFactor(0.5)
Text("Lives: \(lives)")
.minimumScaleFactor(0.5)
Text("\(money) Available")
.minimumScaleFactor(0.5)
Text("Status: \(status)")
.minimumScaleFactor(0.5)
}
.padding()
}
}
.navigationTitle("Accounts")
}
.tabItem {
Image(systemName: "star")
Text("User Info")
}
}
}
}
}
ws.event.open = { }
ws.event.message = { message in
let msg = JSON(message)
if msg["type"] == "" { }
}
ws.event.error = { error in
//errorLoginView(error: "\(error)")
}
}
I've used WebSocket(StarScream) in a Swift demo app.
I'd recommend using a standalone state object (#ObservableObject or #EnvironmentObject)to handle websocket. Think of it as a shared websocket service.
In this service, you publish a couple of events based on your needs.
E.g.;
enum LoginState {
case initial, authOK, authFail
}
final class Websocket: ObservableObject {
#Published var state: LoginState = .initial
// implement websocket callbacks
// e.g.; change state to .authFail in error handler
}
Your LoginView simply observes state changes:
struct LoginView: View {
#ObservedObject var ws = Websocket()
var body: some View {
// setup websocket as needed
switch ws.state { // supported in Xcode 12 as I recall, use if otherwise
case .authOK:
// some view
// other cases
}
}
}
This is the overall picture, you need to fill in the implementation yourself.
I think this is sufficient to get you started on the right track.
Related
I just tried to make an API app in SwiftUI with loveCalculator from rapidapi.com
The problem is that API first needs names from me before it gives me the results.
My program works but fetching data form API earlier that I want (when I click to show my data in next view, first show default data, then show the data that should be displayed when I click).
Also Is it possible to initialize #Published var loveData (in LoveViewModel) without passing any default data or empty String?
Something like make the data from LoveData optional ?
Can You tell me when I make mistake?
MY CODE IS :
LoveData (for api)
struct LoveData: Codable {
let percentage: String
let result: String
}
LoveViewModel
class LoveViewModel: ObservableObject {
#Published var loveData = LoveData(percentage: "50", result: "aaa")
let baseURL = "https://love-calculator.p.rapidapi.com/getPercentage?"
let myApi = "c6c134a7f0msh980729b528fe273p1f337fjsnd17137cb2f24"
func loveCal (first: String, second: String) async {
let completedurl = "\(baseURL)&rapidapi-key=\(myApi)&sname=\(first)&fname=\(second)"
guard let url = URL(string: completedurl) else {return }
do {
let decoder = JSONDecoder()
let (data, _) = try await URLSession.shared.data(from: url)
if let safeData = try? decoder.decode(LoveData.self, from: data) {
print("succesfully saved data")
self.loveData = safeData
}
} catch {
print(error)
}
}
}
LoveCalculator View
struct LoveCalculator: View {
#State var firstName = ""
#State var secondName = ""
#ObservedObject var loveViewModel = LoveViewModel()
var body: some View {
NavigationView {
ZStack{
Color(.systemTeal).ignoresSafeArea()
VStack(alignment: .center) {
Text("Love Calculator")
.padding(.top, 100)
Text("Enter first name:")
.padding()
TextField("first name", text: $firstName)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 300)
.padding(.bottom)
Text("Enter second name:")
.padding()
TextField("second name", text: $secondName, onEditingChanged: { isBegin in
if isBegin == false {
print("finish get names")
}
})
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 300)
.padding(.bottom)
NavigationLink {
LoveResults(percentage: loveViewModel.loveData.percentage, description: loveViewModel.loveData.result)
} label: {
Text("CHECK IT!")
}
Spacer()
}
}
.task {
await loveViewModel.loveCal(first: firstName, second: secondName)
}
}
}
}
LoveResults View
struct LoveResults: View {
var percentage: String
var description: String
var body: some View {
ZStack{
Color(.green).ignoresSafeArea()
VStack{
Text("RESULTS :")
.padding()
Text(percentage)
.font(.system(size: 80, weight: .heavy))
.foregroundColor(.red)
.padding()
Text(description)
}
}
}
}
Thanks for help!
Regards,
Michal
.task is the same modifier as .onAppear in terms of view lifecycle, meaning it will fire immediately when the view appears. You have to move your API call to a more controlled place, where you can call it once you have all the required data.
If you want to fire the API request only after both names are entered, you can create a computed variable, that checks for desired state of TextFields and when that variable turns to true, then you call the API.
Like in this example:
struct SomeView: View {
#State var firstName: String = ""
#State var secondName: String = ""
var namesAreValid: Bool {
!firstName.isEmpty && !secondName.isEmpty // << validation logic here
}
var body: some View {
Form {
TextField("Enter first name", text: $firstName)
TextField("Enter second name", text: $secondName)
}
.onChange(of: namesAreValid) { isValid in
if isValid {
// Call API
}
}
}
}
You can also set your loveData to optional using #Published var loveData: LoveData? and disable/hide the navigation link, until your data is not nil. In that case, you might need to provide default values with ?? to handle optional string errors in LoveResults view initializer
It was very helpful but still not enough for me, .onChange work even if I was type 1 letter in my Form.
I find out how to use Task { } and .onCommit on my TextField, now everything working well !
My code now looks like that :
LoveCalculator View :
struct LoveCalculator: View {
#State var firstName = ""
#State var secondName = ""
var namesAreValid: Bool {
!firstName.isEmpty && !secondName.isEmpty
}
func makeApiCall() {
if namesAreValid {
DispatchQueue.main.async {
Task {
await self.loveViewModel.loveCal(first: firstName, second: secondName)
}
}
}
}
#ObservedObject var loveViewModel = LoveViewModel()
var body: some View {
NavigationView {
ZStack{
Color(.systemTeal).ignoresSafeArea()
VStack(alignment: .center) {
Text("Love Calculator")
.padding(.top, 100)
Text("Enter first name:")
.padding()
TextField("first name", text: $firstName, onCommit: {makeApiCall()})
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 300)
.padding(.bottom)
Text("Enter second name:")
.padding()
TextField("second name", text: $secondName, onCommit: {
makeApiCall()
})
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 300)
.padding(.bottom)
NavigationLink {
LoveResults(percentage: loveViewModel.loveData.percentage ?? "", description: loveViewModel.loveData.result ?? "")
} label: {
Text("CHECK IT!")
}
Spacer()
}
}
}
}
}
Thank You one more time for very helpful tip!
Peace!
Michał ;)
Editing :
Just look at Apple documentation and I see that they say that .onCommit is deprecated whatever it means.
So instead of this I use .onSubmit and works the same !
TextField("second name", text: $secondName)
.textFieldStyle(.roundedBorder)
.frame(maxWidth: 300)
.padding(.bottom)
.onSubmit {
makeApiCall()
}
Peace! :)
I am having a problem while I want to dismiss a popup (that appears automatically depending on a specific condition) by clicking a button.
This is the PopUp struct:
struct dataPrivacyPopUp: View {
let model: OffersView.Model
let termsOfUseText = "Nutzungsbedingungen"
let privacyPolicyText = "Datenschutzerklärung"
#State var termsOfUseChecked = false
#State var privacyPolicyChecked = false
#State var buttonDisabled = true
#State private var showPopUp: Bool = false
#Binding var showModal: Bool
var body: some View {
ZStack {
if ( model.showPopUp == true) {
// PopUp Window
VStack(alignment: .center){
Image("logo")
.aspectRatio(contentMode: .fill)
.frame(alignment: .center)
.padding()
VStack(alignment: .leading) {
Text((model.acceptance?.salutation)!)
.multilineTextAlignment(.leading)
.padding()
.foregroundColor(Color.black)
Text((model.acceptance?.statement)!)
.multilineTextAlignment(.leading)
.padding()
.foregroundColor(Color.black)
Text((model.acceptance?.declarationIntro)!)
.multilineTextAlignment(.leading)
.padding()
.foregroundColor(Color.black)
if ((model.acceptance?.dpr)! == true) {
VStack(alignment: .leading){
HStack {
CheckBoxView(checked: $privacyPolicyChecked)
HStack(spacing: 0){
Text(R.string.localizable.dataPrivacyPopupText())
.foregroundColor(Color.black)
Button(privacyPolicyText) {
model.openUrl(url: API.privacyPolicyURL)
}
}
}
Text((model.acceptance?.declarationOutro)!)
.multilineTextAlignment(.leading)
.padding()
}
.padding()
Button(action: {
model.setTos()
print("showModal PopUpView2 1: \(showModal)")
self.showModal.toggle()
print("showModal PopUpView2 2: \(showModal)")
}, label: {
Text(R.string.localizable.dataPrivacyButton())
.foregroundColor(Color.white)
.font(Font.system(size: 23, weight: .semibold))
})
.disabled(model.buttonDisabledForOne(privacyPolicyChecked: privacyPolicyChecked, termsOfUseChecked: termsOfUseChecked))
.padding()
}
}
}
// .onAppear(perform: )
.background(Color.white01)
.padding()
}
}
}
}
and this is where I call it (contentView):
struct OffersView: View {
#StateObject var model = Model()
#State private var showingPopUp = false
#State private var showModal = false
#State private var showingAddUser = false
// var showPopup : Bool = true
var body: some View {
NavigationView {
Group {
switch model.sections {
case .loading:
ActivityIndicator(animate: true)
case .success(let sections):
ScrollView(.vertical) {
VStack(alignment: .leading, spacing: 0) {
Text(R.string.localizable.offersHello(model.firstName))
.aplFont(.headline02)
.padding(.bottom, 24)
VStack(spacing: 48) {
ForEach(sections) { section in
OffersSectionView(section: section, model: model)
}
}
}
.useFullWidth(alignment: .leading)
.padding()
}
default:
Color.clear
if ( model.showPopUp == true) {
ZStack {
Color.black.opacity(model.showPopUp ? 0.3 : 0).edgesIgnoringSafeArea(.all)
dataPrivacyPopUp(model: model, showModal: self.$showModal)
.onAppear(perform: {
self.showModal.toggle()
})
}
}
}
}
.navigationBarHidden(true)
.handleNavigation(model.navigationPublisher)
.onAppear(perform: model.onAppear)
.onDisappear(perform: model.onDisappear)
.environment(\.dynamicTypeEnabled, false)
.safariView(isPresented: model.showSafari) {
SafariView(url: model.safariUrl!)
}
}
}
}
I need help about this, I tried the traditional method to set a #Binding variable etc .. but that's not working, the boolean value is changing but the UI is not updating (the popup is not dismissing), thank you
I tried to look at your code - I suggest you simplify it to the bare minimum to exemplify your issue - and it seems that you are using 2 properties to show your pop-up: showingPopUp and showModal. It is quite likely that you are having trouble keeping them both in sync.
For starters, I would suggest to use only one variable, either it is true or false - "a man with two watches never knows what time it is".
For the solution:
If you prefer keeping your ZStack approach, the solution would look something like:
struct MyPrivacy: View {
#Binding var showMe: Bool
var body: some View {
VStack {
Text("The content of the pop-up")
.padding()
Button {
withAnimation {
showMe.toggle()
}
} label: {
Text("Dismiss")
}
}
}
}
struct Offers: View {
#State private var showPopup = false
var body: some View {
NavigationView {
ZStack {
VStack {
Text("View behind the pop-up")
.padding()
Button {
withAnimation {
showPopup.toggle()
}
} label: {
Text("Pop")
}
}
if showPopup {
Color.white
MyPrivacy(showMe: $showPopup)
}
}
}
}
}
If instead you want to go for a more flexible approach, if you are developing for iOS, SwiftUI has a convenient object - Sheets. You can use it as suggested in the documentation, or build a specific struct that manages all the modal views of this type and use your model to handle the presentation.
The process goes like:
Create a struct that will handle all kinds of Sheets of your app.
Add to your view-model the property to present any sheet.
Create the Views that will be the content of each sheet.
Call the .sheet(item:content:) method on each View the requires a sheet.
Here's the sample code:
SheetView handler:
struct SheetView: Identifiable {
// This struct controls what modal view will be presented.
// The enum SheetScreenType can grow to as many as different
// modal views your app needs - add the content in the switch below.
let id = UUID()
var screen: SheetScreenType
#ViewBuilder
var content: some View {
switch screen {
case .dataPrivacy:
DataPrivacy()
default:
EmptyView()
}
}
enum SheetScreenType {
case dataPrivacy
case none
}
}
Presenter in your view-model:
class MyViewModel: ObservableObject {
// This code can fit anywhere within your view-model.
// It controls the presentation of the modal view, which in
// this case is a Sheet.
private let sharedSheet = SheetView(screen: .none)
// Show the selected sheet
#Published var sheetView: SheetView?
var showSheet: SheetView.SheetScreenType {
get {
return sheetView?.screen ?? .none
}
set {
switch newValue {
case .none:
sheetView = nil
default:
sheetView = sharedSheet
}
sheetView?.screen = newValue
}
}
}
Content of your modal view:
struct DataPrivacy: View {
#EnvironmentObject var model: MyViewModel // Pass YOUR model here
var body: some View {
VStack(alignment: .center){
Text("Respecting your privacy, no details are shown here")
.padding()
Button {
print("Anything you need")
// Set the showSheet property of your model to
// present a modal view. Setting it to .none dismisses
// the modal view.
model.showSheet = .none
} label: {
Text("Time do dismiss the modal view")
}
.padding()
}
}
}
Enable your view to listen to your model to present the sheet:
struct OffersView: View {
#ObservedObject var model = MyViewModel() // Pass YOUR model here
var body: some View {
VStack {
Text("Anything you wish")
.padding()
Button {
withAnimation {
// Set the showSheet property of your model to
// present a modal view. Set it to any choice
// among the ones in the SheetScreen.SheetScreenType enum.
model.showSheet = .dataPrivacy
}
} label: {
Text("Tap here for the privacy in modal view")
}
}
// Show a modal sheet.
// Add this property at the top level of every view that
// requires a modal view presented - whatever content it might have.
.sheet(item: $model.sheetView) { sheet in
sheet.content
.environmentObject(model)
}
}
}
Good luck with your project!
Disclaimer: I am trying to learn the basics of IOS development, so this question might be very basic.
I'm currently building out my first IOS project, which consists of pulling a random Poem from an API and then giving the user the possibility to save that poem to a "Saved Poem" list. My app currently has a Poem Detail screen (ContentView) and a Home Page screen (where the saved poem list will be).
I've attempted to follow Paul Hudson's tutorial on CoreData (https://www.youtube.com/watch?v=7_Afen3PlDE&ab_channel=PaulHudson). Currently, my goal is to save a poem once the "Bookmark" button on the Detail Screen is tapped. Once a poem saved to CoreData, I would like to display it in a list on the home page.
Code for the Detail View (which includes the Bookmark button)
import SwiftUI
struct ContentView: View {
#ObservedObject var fetch = FetchPoem()
#Environment(\.managedObjectContext) var moc
var currentDate = Text(Date().addingTimeInterval(600), style: .date)
var body: some View {
VStack {
HStack{
Button(action: {}) {
Image(systemName: "arrow.backward")
.font(.system(size: 25, weight: .heavy))
.foregroundColor(.black)
}
Spacer(minLength: 0)
Button(action: {
let savedpoem = SavedPoem(context: self.moc)
savedpoem.id = UUID()
savedpoem.title = "\(poem.title)"
savedpoem.author = "\(poem.author)"
savedpoem.lines = "\(joined)"
try? self.moc.save()
}) {
Image(systemName: "bookmark")
.font(.system(size: 25, weight: .heavy))
.foregroundColor(.black)
}
}
.padding(.vertical, 10)
ScrollView {
VStack {
HStack{
VStack (alignment: .leading) {
Text("Today's Poem, \(currentDate)")
.font(.subheadline)
.foregroundColor(Color.gray)
.padding(.bottom, 20)
.padding(.top, 10)
if let poem = fetch.poems.first {
let joined = poem.lines.joined(separator: "\n")
Text("\(poem.title)")
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(.black)
.padding(.bottom, 20)
.lineSpacing(0)
Text("BY "+poem.author.uppercased())
.font(.subheadline)
.foregroundColor(Color.gray)
.padding(.bottom, 20)
HStack {
Text("\(joined)")
.font(.body)
.foregroundColor(.black)
.padding(.bottom)
.lineSpacing(5)
Spacer()
}
} else {
Spacer()
}
}
}
}
}
Button("Get Next Poem") { fetch.getPoem() }
}
.background(Color.white.ignoresSafeArea())
.padding(.horizontal)
}
Code for the Home Page View
import SwiftUI
import CoreData
struct HomeView: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: SavedPoem.entity(), sortDescriptors: []) var savedpoems:FetchedResults<SavedPoem>
var body: some View {
VStack{
List{
ForEach(savedpoems, id: \.id) { savedpoem in
Text(savedpoem.name ?? "Unkown")
}
}
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
My CoreData Data Model
And finally, my Data Model to pull from the API:
import Foundation
struct Poem: Codable, Hashable {
let title, author: String
let lines: [String]
let linecount: String
}
class FetchPoem: ObservableObject {
// 1.
#Published var poems = [Poem]()
init() {
getPoem()
}
func getPoem() {
let url = URL(string: "https://poetrydb.org/random/1")!
// 2.
URLSession.shared.dataTask(with: url) {(data, response, error) in
do {
if let poemData = data {
// 3.
let decodedData = try JSONDecoder().decode([Poem].self, from: poemData)
DispatchQueue.main.async {
self.poems = decodedData
}
} else {
print("No data")
}
} catch {
print("Error")
}
}.resume()
}
}
Unfortunately, this code doesn't build and is throwing me the following errors:
On the Home Page:
Cannot find type 'SavedPoem' in scope
Cannot infer key path type from context; consider explicitly specifying a root type
Generic parameter 'Content' could not be inferred
Missing argument for parameter 'content' in call
In Detail View:
Cannot Find "Poem" in Scope
Any ideas? Thanks in advance.
I completely new on Swift, so excuse possible lack of precision. I am trying to build a login form that authenticates with a REST API.
Here is what I have so far following this tutorial.
My next step is understanding where to put all of the logic for submitting the form. I would like to extract it from the inline manner it is taking.
Can I pass a function to the action parameter? I tried finding some sort of extract feature on Xcode but couldn't get it to work (they are greyed).
import SwiftUI
let lightGreyColor = Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0, opacity: 1.0)
let storedUsername = "john"
let storedPassword = "1234"
struct ContentView: View {
#State var username: String = ""
#State var password: String = ""
#State var authenticationDidFail: Bool = false
#State var authenticationDidSucceed: Bool = false
var body: some View {
ZStack {
VStack {
Image("logo")
EmailField(username: $username)
PasswordField(password: $password)
if authenticationDidFail {
Text("Information not correct. Try again.")
.offset(y: -10)
.foregroundColor(.red)
}
Button(action: {
if self.password == storedPassword {
print(password)
self.authenticationDidSucceed = true
self.authenticationDidFail = false
} else {
self.authenticationDidFail = true
}
}) {
LoginButtonContent()
}
}
if authenticationDidSucceed {
Text("Login succeeded!")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct LoginButtonContent: View {
var body: some View {
Text("LOGIN")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(width: 220, height: 60)
.background(Color.green)
.cornerRadius(15.0)
}
}
struct PasswordField: View {
#Binding var password: String
var body: some View {
SecureField("Password", text: $password)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 20)
}
}
struct EmailField: View {
#Binding var username: String
var body: some View {
TextField("Username", text: $username)
.padding()
.cornerRadius(5.0)
.padding(.bottom, 20)
}
}
You can use an Object of your Model inside your Button's action, there you can execute your REST call and return a result to set a value to authenticationDidSucceed, so that the UI is updated.
If the used class conforms to the ObservableObject protocol you can even use its published variables to automatically update the UI.
Thanks for all the support I have received, I trying to build an macos app that tags pdfs for machine learning purposes. I have followed Stanford SwiftUI course, and I want to create main view for my app that contains the document and to type a regex string to find in the document. The deal is I need to create a document chooser, to add documents to be analized, but I don't know how to deal with 2 view models in the same view. In fact one of those view models depend on the other one. The solution I found (not a solution a messy workaround) is to initialize the document manager as a separate view and use it as a navigation view, with a navigation link, but the look is horrible. I'll paste the code and explain it better.
This is the stores view
struct PDFTaggerDocumentStoreView: View {
#EnvironmentObject var store:PDFTaggerDocumentStore
var body: some View {
NavigationView {
List {
Spacer()
Text("Document Store").fontWeight(.heavy)
Button(action: {self.store.addDocument()}, label: {Text("Add document")})
Divider()
ForEach(store.documents){ doc in
NavigationLink(destination: PDFTaggerMainView(pdfTaggerDocument: doc)) {
Text(self.store.name(for: doc))
}
}
.onDelete { indexSet in
indexSet.map{self.store.documents[$0]}.forEach { (document) in
self.store.removeDocuments(document)
}
}
}
}
}
}
The main view.
struct PDFTaggerDocumentView: View {
#ObservedObject var document:PDFTaggerDocument
#State private var expression = ""
#State private var regexField = ""
#State private var showExpressionEditor = false
var body: some View {
VStack {
ZStack {
RoundedRectangle(cornerRadius: 15, style: .continuous)
.stroke(Color.white, lineWidth: 0.5)
.frame(width: 600)
.padding()
VStack {
Text("Try expression ")
HStack {
TextField("Type regex", text: $document.regexString)
.frame(width: 200)
Image(nsImage: NSImage(named: "icons8-save-80")!)
.scaleEffect(0.3)
.onTapGesture {
self.showExpressionEditor = true
print(self.document.regexString)
print(self.regexField)
}
.popover(isPresented: $showExpressionEditor) {
ExpressionEditor().environmentObject(self.document)
.frame(width: 200, height: 300)
}
}
Picker(selection: $expression, label: EmptyView()) {
ForEach(document.expressionNames.sorted(by: >), id:\.key) { key, value in
Text(key)
}
}
Button(action: self.addToDocument, label: {Text("Add to document")})
.padding()
.frame(width: 200)
}
.frame(width:600)
.padding()
}
.padding()
Rectangle()
.foregroundColor(Color.white).overlay(OptionalPDFView(pdfDocument: document.backgroundPDF))
.frame(width:600, height:500)
.onDrop(of: ["public.file-url"], isTargeted: nil) { (providers, location) -> Bool in
let result = providers.first?.hasItemConformingToTypeIdentifier("public.file-url")
providers.first?.loadDataRepresentation(forTypeIdentifier: "public.file-url") { data, error in
if let safeData = data {
let newURL = URL(dataRepresentation: safeData, relativeTo: nil)
DispatchQueue.main.async {
self.document.backgroundURL = newURL
}
}
}
return result!
}
}
}
I'd like to be able to initialize both views models in the same view, and make the document view, be dependent on the document chooser model.
Is there a way I can do it?
Thanks a lot for your time.