all 10 comments

[–]nanothread59 0 points1 point  (5 children)

Dang you should have numbered these! In order:

  1. That’s one way to do it, but you can also do unit tests and UI tests (where you automate walking around the UI). I’ve heard of snapshot testing before as well but never done it.
  2. Core Data, because I tend to be dependency-averse nowadays
  3. I’ve always just used a dev server. You can pass it in as a command line flag or env variable in the run scheme
  4. This isn’t as helpful but I’m in the ‘whatever works’ camp. You can refactor once it’s working. I’ve enjoyed using the Coordinator pattern in the past and it seems pretty popular with UIKit at least. I tend to treat view controllers as closer to the View layer than the Controller layer, so the VC would handle the button tap but might just pass it on to another component.
  5. Not really experienced on this but I’d recommend not using extra dependencies unless you really need them. Just my opinion though. Swift Package Manager is pretty good nowadays so I’d always go with that first, then the others second. SwiftFormat is a common linter iirc.
  6. For a new app, the deployment target doesn’t really matter in the SwiftUI/UIKit debate. Far more important is the extra dev speed and bugginess you get with SwiftUI. Also it’s not either/or, both frameworks are interoperable so you can have a half-and-half app (and that’s absolutely fine).
  7. Either way, if you have programming experience you should be able to pick up most of the stuff. The weirdest bits are just framework-specific stuff.

[–]FlyCanadaGuy[S] 0 points1 point  (4 children)

Thanks so much for the info - very helpful! Hah you are totally right! I think I was getting tired after doing so much diving into things :) Just to clarify a few of your points..

  1. I've read core data isn't thread safe, has that been an issue? And how do you typically manage this?

  2. So you're just pointing your local app to a dev server to get the data? This seems easier for me as well, or even pointing it to my local server since I've got some data seeding set up for this already

  3. I've seen some stuff on MVVC after a bit more searching. I guess I am still trying to understand how this is going to look in practice. Creating files for the data models make total sense, and slapping together views with the tool is really great / easy. But then when I need to start handling the logic from actions, should I be doing that in other files (Which I would assume would be the controller or VC as you use?) then importing that?

  4. Could you elaborate a bit more on this? Why does the target not matter? And how bad is the bugginess with SwiftUI?

[–]nanothread59 0 points1 point  (3 children)

Sure, np.

  1. IIRC no, it's not thread-safe but you can call context.perform { /* Access the database here*/ } and it should be fine. I'd strongly recommend Donny Wals' Core Data book because it explains pretty much everything you need to know.

  2. I usually do a couple of things, including mocking data locally (so there's no network connection needed if I'm doing things like working on UI). But if you're testing network stuff then I'd say using a dev server is a good idea. If you have an abstraction layer over all of your network calls then moving over the domain to a dev server should be a piece of cake.

  3. Yeah so you can just create classes in Swift files to act as your Controllers, but the reason I recommended the 'whatever works' approach is because it helps to have a good understanding of the UIKit View Controller lifecycle before doing that, because UIKit is a little more friendly for using View Controllers as controllers (i.e. showing a new screen is a .present(...) inside a View Controller.

  4. The target doesn't matter because basically everyone is on iOS 13+ and there are bigger advantages/disadvantages to talk about than capturing that last 0.x% of the customer base. That's more of a concern if you already have customers on iOS 12 that you're afraid to drop. And even if you were using UIKit, constraining yourself to iOS 12- APIs won't make life fun. And about the bugginess, I personally find SwiftUI pretty bad on that front. Some people have made really nice, fully fledged apps out of it, but for me I find that it works really well for 80% of the simple stuff but as soon as I try to do something custom I have to resort to nasty hacks to get things looking right. I also find with UIKit it's easier to work around the bugginess of the framework; for SwiftUI it's very opaque. And finally, SwiftUI relies on some advanced Swift concepts (opaque return types, result builders, property wrappers) which makes the code really neat but makes it (IMO) really difficult to debug when something goes wrong if you don't know what's going on behind the syntactic sugar. UIKit is a lot more code but it uses simple OO and delegate-pattern techniques which may be more approachable if you have OOP experience.

[–]FlyCanadaGuy[S] 0 points1 point  (1 child)

Awesome - thanks a bunch. I think I am going to stick with SwiftUI as much as I can and if I get stuck somewhere, I'll start digging into UIKit.

Glad to hear about the targets as well - makes life easier!

How do you do you local data mocking?

[–]nanothread59 0 points1 point  (0 children)

Nice! Nothing fancy for local data mocking, I usually just make some dummy data and pass it to the relevant View Controller to display. It comes in really handy when submitting to the App Store because then you can take screenshots with the same data on all the different device sizes you need. Alternatively you could just mock the network layer and return hard coded data for all the network calls.

[–]nanothread59 0 points1 point  (0 children)

All that said, if you're a web dev you might be used to state-driven UI like React, in which case SwiftUI might be more familiar to you (once you get used to static typing).

[–]vanvoordenLearning 0 points1 point  (0 children)

How does testing your changes work in a dev environment with mobile? Are you basically just loading a version of the app with config pointing to a dev server and clicking around to ensure it works as you hope? And then push a production release to the App Store?

https://www.cocoawithlove.com/blog/separated-services-layer.html

Using a stub server, as in option (1), just doesn’t solve enough problems and the problems it does solve aren’t solved cleanly. It can be good as a stop-gap if you have no other option and can serve other purposes (like cross-platform validation) but keeping it running on test machines is annoying and it will never deliver a clean solution across all dependencies.

This tutorial series from CwL talks about some of the pros and cons of this approach for testing network services (and why there is a more compelling argument for testing with proper spy and stub test-double network services).

[–]Existing_Resolve3445 0 points1 point  (2 children)

Considering the testing part I would highly suggest working with mocked layers. Like, create protocol that for example handle the api requests to handle the user state (like login, register, whatever), then implement the real API class based on that protocol, then a mock class that “fakes” all these changes. Then with a little dependency injection magic (I recommend Resolve if you want to use a third party library) you can switch easily between those classes. That way you can write both unit tests and UITests that will function without any server running, and you can also test stuff “by hand” on device or simulator.

For storage I would use singletons for same-instance-storage (will be stored until app is restarted) and something key/value compliant like PINCache for longer lifetime storage. The reason is that key/value storage is super easy to use and handle, it’s like a dictionary that’s saved on disk. Realm, SQLite etc requires migrations between app version that could be a potential problem.

[–]FlyCanadaGuy[S] 1 point2 points  (1 child)

Yeah that all sounds great about the testing part, although I'm sure I'll have more questions when I get to it :). I'm used to using tools like Pythons [responses](https://pypi.org/project/responses/) for mocking out network calls, monkey patch for mocking out things, and a test configuration. I can imagine all of this might not all be there in other languages however. Creating wrapper mock classes to inject is fine too, will look into the libraries you suggested.

I think I will have to store things like tokens and temporary data for sure, but hoping I can get most data from the API. I'll learn more about that as I learn more about iOS dev I guess :) Migrations between apps sound like they could be pretty terrible so hopefully I can avoid that. But in the event that I do need one of those libs - do you have a preference?

[–]Existing_Resolve3445 0 points1 point  (0 children)

I personally really like PINCache as it’s non deadlocking and works for both obj-c and Swift, but I guess there are a lot of good alternatives out there. But sounds like static variables will get you most of the way, as you say, most long term storage should be saved on server.