Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
SaTaMaS
Apr 18, 2003
I'm trying to customize a TabView, and I'm having some trouble understanding how the component works (it's dumb as hell for Apple to not make SwiftUI open-source to get it out of the buggy black-box phase faster)

code:
struct ContentView: View {
    @State private var selection = 0
    @State private var selection2 = 1
    
    var body: some View {
        TabView(selection: $selection) {
            TabView(selection: $selection2) {
                Text("View A")
                    .font(.title)
                    .tabItem{ Text("View A") }
                    .tag(0)
                Text("View B")
                    .font(.title)
                    .tabItem{ Text("View B") }
                    .tag(1)
                Text("View C")
                    .font(.title)
                    .tabItem{ Text("View C") }
                    .tag(2)
            }
            .tabItem{ Text("View 1") }
            .tag(0)
        }
    }
}
This sample works correctly, with 1 item in the parent and 3 items in the child. It seems likely that Apple is using a PreferenceKey to hold the tabItems, but I don't understand why the .tabItem() calls from inside the child TabView don't propagate to the parent TabView. Is there some way to keep the child and parent PreferenceKey structs separate, or some way to achieve this without using PreferenceKey, which AFAIK gets set all the way up the chain of parent views?

Adbot
ADBOT LOVES YOU

uncle blog
Nov 18, 2012

What's the good and smart way to initialize a ViewModel that depends on other variables within a view?

code:
struct MyView: View {

	@EnivronmentObject var appData: AppData
	let coordinator: Coordinator
	@ObservedObject var myViewModel = MyViewModel(appData: appData, coordinator: coordinator) <- this is what I want to accomplish

	var body: some View {
		...
	}
}
Tried setting the VM as lazy, but that's apparently illegal when it has a property wrapper.

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.
Can you make a proper initializer for MyView that takes a Coordinator, then initialize the view model from there? (Though I'm not sure if the AppData instance is available at that point.)

Graniteman
Nov 16, 2002

uncle blog posted:

What's the good and smart way to initialize a ViewModel that depends on other variables within a view?


First, just note that you shouldn’t initialize an ObservedObject. That should be @StateObject if you are instantiating the class inside the view.

There are two problems. One, apple has left us with no approved way of doing this, and we are hoping for a change to _newVM = StateObject(wrappedvalue: VM(params)) at WWDC next week. Second, the only half-good unapproved solution doesn’t work because you are using an EnvironmentObject.

People seem to feel it’s best practice to not have views create their own view models, but instead to pass those in using some mechanism. If you can, it’s probably a better design to have myViewModel instantiated somewhere else and either put into the environment for MyView, or passed in. This sidesteps the whole problem. Find a place where these entities all exist and instantiate MyViewModel there.

Sometimes I’ve found that there wasn’t a good way to do all that, and what I’ve done is made appData an optional inside myViewModel. Then, inside MyView, you can assign it using .onAppear (EnvironmentObjects exist within .onAppear closures). It makes MyViewModel ugly because you need to deal with the optional, but, it’s the only way I know to get something from an @EnvironmentObject into a new object inside a view.

Without the EnvironmentObject (like with your coordinator) you can just create your StateObject inside the view init block. See what I’m doing with the coordinator below. Apple docs say not to do this, but they don’t give us anything else.

code:
class MyViewModel: ObservableObject {
	coordinator: Coordinator
	appData: AppData?

	init(coordinator: Coordinator) { }
}

struct MyView: View {
	@EnvironmentObject var appData: AppData
	let coordinator: Coordinator
	@StateObject var myViewModel: MyViewModel

	init(coordinator: Coordinator) {
		let newViewModel = MyViewModel(coordinator: coordinator)
		// can’t assign newViewModel.appData here because self.appData isn’t populated.
		_myViewModel = StateObject(wrappedvalue:  newViewModel)
	}

	var body: some View {
		Text(“Hello”)
		.onAppear {
			//self.appData is populated here, so you can use it.
			myViewModel.appData = self.appData
		}
	}
}

101
Oct 15, 2012


Vault Dweller

Graniteman posted:

Sometimes I’ve found that there wasn’t a good way to do all that, and what I’ve done is made appData an optional inside myViewModel. Then, inside MyView, you can assign it using .onAppear (EnvironmentObjects exist within .onAppear closures). It makes MyViewModel ugly because you need to deal with the optional, but, it’s the only way I know to get something from an @EnvironmentObject into a new object inside a view.

Could you not safely do
code:
appData: AppData!
in the model there, and avoid having it be optional since it's always going to be initialised in onCreate?

Graniteman
Nov 16, 2002

101 posted:

Could you not safely do
code:
appData: AppData!
in the model there, and avoid having it be optional since it's always going to be initialised in onCreate?

Yeah, I’ve done that, too. If you can be certain that nothing in your view model initializer will use AppData it should be fine I guess, but it feels a little more risky than a normal force unwrap to me since I don’t understand the exact sequence between a view calling it’s initializer (creating the class with a nil appData), any opportunity for code to run in the viewmodel, and when the .onAppear closure finally runs and populates the optional permanently. Since I don’t understand the risk, I would lean toward leaving it optional instead of permanently force-unwrapped.

KidDynamite
Feb 11, 2005

am i burnt out or was wwdc kind of meh this year?

Stringent
Dec 22, 2004


image text goes here
depends on whether weatherkit actually works in japan or not

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

KidDynamite posted:

am i burnt out or was wwdc kind of meh this year?

I'm feeling more sad about "looking forward to using this in a few years when I bump a min sdk requirement" than usual, which is maybe a sign that I'm more excited about what I've seen so far.

former glory
Jul 11, 2011

I like the new SwiftUI charts and the modifiers to the SF symbols. I feel like I'll get a lot of mileage out of that. I didn't get to catch as much as I'd hoped, but I have bookmarks to go back to when there's time.

The

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
There's a bunch of nice low-level improvements that you probably don't need to take any actions regarding, a bunch of SwiftUI improvements, and not a lot in the middle. Xcode 14 seems to be exactly what I've been wanting them to do: lots of significant performance improvements and general bug fixing rather than focusing on flashy new features, but if you weren't previously getting mad at the state of Xcode that's probably not exciting to you.

take boat
Jul 8, 2006
boat: TAKEN
RoomPlan looks cool, I'm excited to play around with that. Charts sounds promising, also glad they're improving on SwiftUI navigation. haven't watched any sessions yet to see what else is new/improved

pokeyman posted:

I'm feeling more sad about "looking forward to using this in a few years when I bump a min sdk requirement" than usual

yeah this is always the worst part. here's to 2024/2025

Glimm
Jul 27, 2005

Time is only gonna pass you by

pokeyman posted:

I'm feeling more sad about "looking forward to using this in a few years when I bump a min sdk requirement" than usual, which is maybe a sign that I'm more excited about what I've seen so far.

100%

SwiftUI and related libraries need to be back ported and available via SPM. The bugs from version to version are hard to deal with, so many version checks in our app.

take boat
Jul 8, 2006
boat: TAKEN
one dumb thing I'm excited for as a developer and not a user: dropping support for 1st gen iPhone SEs

no more 320pt screens! (iiuc)

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.
rip Three20 framework

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
As a library developer I'm very excited by bitcode and armv7 loving off, but most people aren't going to care about that.

eschaton
Mar 7, 2007

Don't you just hate when you wind up in a store with people who are in a socioeconomic class that is pretty obviously about two levels lower than your own?

pokeyman posted:

I'm feeling more sad about "looking forward to using this in a few years when I bump a min sdk requirement" than usual, which is maybe a sign that I'm more excited about what I've seen so far.

Just can bump the SDK guard your use of features newer than your deployment target, your users will love it.

duck monster
Dec 15, 2004

Froist posted:

I don’t want to make a baseless claim without doing my research so I won’t say it was the first, but QML was surely one of the early ones. I worked on the framework itself way back in my career - dates get fuzzy at this point but wiki says it launched in 2009 which feels about right.

Nokia doomed their own platform for many unrelated reasons, but it’s odd to see so much of the industry latch onto the declarative idea half-to-a decade later.

Older post but gently caress it, SA moves slow these days, and I have BIG OPINIONS about Qt.

Its a loving dream framework, it really is. QML when you put the effort into a design can be made to look amazing, it feels like its the declarative display language HTML could have been if HTML wasnt designed by a physicist and then fed through 30 years of hell committees, and the framework actually makes C++ into a language that feels comfortable and ergonomic to work in. Seriously, look up its networking classes, its just so good.

But the loving licensing, good grief. I would *love* to use it as my do-anything toolkit. Hell the netwoking class alone makes it a serious contender for writing servers, and QML works well on pretty much anything. But the licensing. Its completely beyond the reach of low budget hobbyist and one man band coders. And thats a shame, because an MIT open sourced QT would change *everything*

duck monster fucked around with this message at 03:10 on Jun 17, 2022

vote_no
Nov 22, 2005

The rush is on.
Maybe I’ve got my desktop application developer blinders on, but what situation would prevent one from dynamic linking the libraries and following the LGPL license?

dc3k
Feb 18, 2003

what.

take boat posted:

one dumb thing I'm excited for as a developer and not a user: dropping support for 1st gen iPhone SEs

no more 320pt screens! (iiuc)

iphone 8/se2/etc have the same res as og se when display zoom is on iirc

duck monster
Dec 15, 2004

vote_no posted:

Maybe I’ve got my desktop application developer blinders on, but what situation would prevent one from dynamic linking the libraries and following the LGPL license?

It may have improved somewhat in recent times, but I dont believe the mobile ports are LGPL

edit: It appears thats not true anymore.

Neato. All it needs now is someone to port the Crystal-lang wrappers and the language itself to IOS and Android and we'd have gods own mobile dev toolkit.

duck monster fucked around with this message at 03:12 on Jun 17, 2022

uncle blog
Nov 18, 2012

I have a viewmodel which gets passed an instance of an AppData class. And I want the viewmodel to trigger an update of the appData every x seconds. Have tried a few things, but nothing seems to work properly (or either makes the CPU go crazy).

This is a basic representation of the setup I'm working with:
code:
struct ContainerView: View {
	@StateObject var appData: AppData = AppData()
    	let appService: AppService?

	var body: some View {
        	ScrollView {
                	ChildView(childViewVM: ChildViewVM(appData: appData, appService: appService))

			// other childViews
            }
        }
        .onAppear {
            appService?.getAppData(completionHandler: appData.updateAppData)
        }
}



class ChildViewVM: ObservableObject {
	var appData: AppData
        var appService: AppService?

	@Published var stuffToShow: [String] = []

	init(appData: AppDataObservable, appService: AppService?) {
        	self.appData = appData
        	self.appService = appService 
	}

	private func update() {
		appService?.getAppData(completionHandler: appData.updateAppData) // This is what I want to call every x seconds
	}
}



struct ChildView: View {

	@ObservedObject var childViewVM: ChildViewVM

	init(childViewVM: ChildViewVM) {
        	self.childViewVM = ChildViewVM
    	}

	var body: some View {
		ForEach(childViewVM.stuffToShow) { thing in
			Text(thing)
		}	
	}
}
I tried having a timer in ChildViewVM, that would trigger the update function every X seconds. But I quickly started calling it much faster, possibly due to triggering a rerender of the containerview? It also wouldn't properly deinit when I moved to a completely different view.

Let me know if you need to see any more of the code, or more specific examples. Not really sure how to progress from here :question:

KidDynamite
Feb 11, 2005

do you need the timer to be in the view model?

could try placing the timer in ChildView, put an .onReceive on your ForEach, and expose a function from your ChildVM that lets ChildView ask for an update.

KidDynamite
Feb 11, 2005

hey I have 3k I can spend and get reimbursement from work. What swift resources would y’all spend this money on? Bonus points if it will help land a new job.

Already have objcio and pointfree on the list.

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

KidDynamite posted:

hey I have 3k I can spend and get reimbursement from work. What swift resources would y’all spend this money on? Bonus points if it will help land a new job.

Already have objcio and pointfree on the list.

Donny Wals has a couple books. I've not read them but their blog is great, so maybe worth a look.

take boat
Jul 8, 2006
boat: TAKEN
it's not recent and covers more Objective-C than Swift, but the best iOS app development book I've read to date is https://www.amazon.com/iOS-macOS-Performance-Tuning-Objective-C/dp/0321842847 and I'd recommend buying a copy

pointfree is good if you're not familiar with how to build an iOS app in a React/Redux-style architecture, but if you are I don't know I'd recommend it. it's not a bad use of work reimbursement though

former glory
Jul 11, 2011

Does a widely used pattern exist for triggering pop-up views overtop all views in a swiftui app? I have one with a lot of views and each view in turn has a some sub-views in which I need to trigger a pop-up display over over the child+parent view. Mostly to trigger help messages and informative things.

I've created a custom PopUpView and overtop the main parent views, I have something like:

code:
ZStack { 
   Fart()
   Butts()
}
.overlay { 
   if viewModel.popUpVisible { 
      displayTheCustomPopUpView(popUpDetails: viewModel.popUpDetails) 
   } 
}
And then I pass along the published viewModel down through the children so that sub-views can instantiate the popUpDetails and toggle the popUpVisible bool. This feels like a dirty hack polluting the entire codebase, but I haven't seen a cleaner method in my travels yet, and it's working. I'm thinking a published singleton could at least get rid of the passing along of the VM and keep the overlay code + triggering in the children more clean.

dizzywhip
Dec 23, 2005

Environment and preferences are the tools you want if you need views to communicate through the hierarchy without manually passing things around.

former glory
Jul 11, 2011

Thanks. I'll check out Environment. I recall seeing that used in CoreData.

dizzywhip
Dec 23, 2005

I haven't used Core Data so maybe they also have something called the environment, but to be clear I was talking about SwiftUI's concept of environment which is its own thing unrelated to Core Data. Here's an article that goes over it and shows how to create your own custom environment values: https://useyourloaf.com/blog/swiftui-custom-environment-values/

former glory
Jul 11, 2011

dizzywhip posted:

I haven't used Core Data so maybe they also have something called the environment, but to be clear I was talking about SwiftUI's concept of environment which is its own thing unrelated to Core Data. Here's an article that goes over it and shows how to create your own custom environment values: https://useyourloaf.com/blog/swiftui-custom-environment-values/

That article and entire site is a pro click. Much appreciated.

uncle blog
Nov 18, 2012

If I revoke my Development and Distribution certificates, apps already on the App Store will be unaffected?

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.

uncle blog posted:

If I revoke my Development and Distribution certificates, apps already on the App Store will be unaffected?

Correct.

uncle blog
Nov 18, 2012

Our app got updated to iOS 13, making URLSession.init deprecated. I'm tasked with updating a bunch of tests that got broken by this deprecation.
I'm not sure what a good, clean way to rewrite this mocksession, so that it can easily provide a URLSession for our APIClient?

code:
class MockSession: URLSession {
    var mockRequests: [String: MockRequest] = [:]

    func mock(path: String = "/") -> MockRequest {
        let mock = MockRequest(path: path)
        mockRequests[path] = mock

        return mock
    }

    override func uploadTask(with request: URLRequest,
                             from _: Data?,
                             completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionUploadTask {
        let path = request.url!.relativePath
        let mockRequest = mockRequests[path]! // .precondition("Mock for "\(path)" missing")
        return MockUploadTest(request: request, response: mockRequest, completionHandler: completionHandler)
    }

    override func dataTask(with request: URLRequest,
                           completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
        let path = request.url!.relativePath
        let mockRequest = mockRequests[path]! // .precondition("Mock for "\(path)" missing")
        return MockTask(request: request, response: mockRequest, completionHandler: completionHandler)
    }

    override func finishTasksAndInvalidate() {}

    override func invalidateAndCancel() {}
}
code:
    let mockSession = MockSession()
    lazy var apiClient = ApiClient(session: mockSession)

pokeyman
Nov 26, 2006

That elephant ate my entire platoon.
Shortest answer is define your own init and call the designated super.init, which is probably init(configuration:delegate:delegateQueue:).

Maybe better answer is make a protocol Session, declare the methods of URLSession that you use, then conform URLSession to it. Make your MockSession conform to the protocol instead of subclassing. Then have your ApiClient take an instance of the protocol instead of a URLSession.

uncle blog
Nov 18, 2012

Thanks, I’ll look into that!

uncle blog
Nov 18, 2012

edit: made it work -ish

uncle blog fucked around with this message at 14:37 on Aug 25, 2022

ultramiraculous
Nov 12, 2003

"No..."
Grimey Drawer
Yeah go with making a protocol IMO and have URLSession and your MockSession conform to it via an extension IMO. I wish they'd built the Foundation APIs to be protocol-based but what can you do at this point :shrug:.

SaTaMaS
Apr 18, 2003

ultramiraculous posted:

Yeah go with making a protocol IMO and have URLSession and your MockSession conform to it via an extension IMO. I wish they'd built the Foundation APIs to be protocol-based but what can you do at this point :shrug:.

related WWDC session: https://developer.apple.com/videos/play/wwdc2018-417/?time=539

Adbot
ADBOT LOVES YOU

101
Oct 15, 2012


Vault Dweller
I feel like I'm going crazy.

Every time I try SwiftUI, I hit some dumb but huge hurdle so quickly.

code:
List {
	
	ForEach(items, id: \.self) { item in
		
		Text(item)
	}
	.onDelete { indexSet in
		
		print("Delete")
	}
}
.onTapGesture {
	
	print("Tapped")
}
So, you have to use ForEach if you want onDelete, it doesn't just work on a List of items.

The tap gesture takes precedence over the onDelete so the onDelete almost never gets triggered or if it rarely does, it's at the same time as the tap gesture.

I've tried using swipeActions instead, both with a ForEach and just a List (they don't work on just a list) and the issue persists.

Am I missing something, or am I giving up on SwiftUI for another few years?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply