Post

Replies

Boosts

Views

Activity

Reply to SwiftUI Previews broken on local Swift Package with dependencies
I've been dealing with this problem for HOURS and your description has helped me come up with the 'solution'. TLDR: (temporarily) change the dynamic package to static during development, then switch back to dynamic before deploying. Obviously this only works if you control the client package. I'm not expert, but here's my explanation: Since the client package is dynamic, it's technically not supposed to be linked/accessed until runtime. And I don't think SwiftUI previews count as 'runtime'. So, when the SwiftUI preview tries to access code from a dynamic library, it's not available. This problem does NOT occur in an Xcode project because you have to embed the packages in the main target, which I guess solves the problem. Unfortunately, there's no option to embed (even temporarily) in a swift package. So there's no way to allow the SwiftUI preview to gain access to the third party dynamic libraries. When I commented out the dynamic declaration in the package.swift file (converting the dynamic library to static), the previews in my swift package that used the dynamic (now static) library started working. I'll likely write a script into my CI/CD to uncomment the line (and switch the package back to dynamic) before deploying so I don't forget. I hope this helps. And shame on Apple for knowing about this problem for YEARS and ignoring it.
Jul ’24
Reply to Why use async/await vs completion handlers?
I've been wondering the same thing lately, and I think the answer depends on the complexity of the problem. In the example you provided, there might be little (or no) benefit using async/await vs completion handlers. However, consider the following example: func fetchAllData(firstId: String, completion: @escaping (Result<ImportantData, Error>) -> Void) { fetchInitialData(firstId) { [weak self] result in switch result { case .success(let initialData): self?.fetchSecondaryData(initialData.id) { result in switch result { case .success(let secondaryData): self?.fetchFinalData(secondaryData.id) { result in switch result { case .success(let imporantData): completion(.success(importantData)) case .failure(let finalError): completion(.failure(finalError) } case .failure(let secondError): completion(.failure(secondError) } case .failure(let firstError): completion(.failure(firstError) } } } A silly example, to be sure, but I'm sure there are times when developers need to use multiple asynchronous methods in a synchonrous fashion (if that makes sense). each subsequent call requires info from the previous. And the above example is assuming no other work aside from another async call is being made on each success. Before async/await, that nightmare above was probably a 'good' solution. The same code using async/await: func fetchAllData(firstId: String) async throws -> ImportantData { let initialData = try await fetchInitialData(firstId) let secondaryData = try await fetchSecondaryData(initialData.id) return try await fetchFinalData(secondaryData.id) } It's almost comical how simple async/await can make these complex nested completion handlers. And even if you wanted to keep the outer-most completion handler, you can still clean things up with async/await but using a Task inside of the outermost method alongside a do/catch: func fetchAllData(firstId: String, completion: @escaping (Result<ImportantData, Error>) -> Void) { Task { do { let initialData = try await fetchInitialData(firstId) let secondaryData = try await fetchSecondaryData(initialData.id) let importantData = try await fetchFinalData(secondaryData.id) completion(.success(importantData)) } catch { completion(.failure(error)) } } } In the last example, you'll need to decide when to switch back to the MainActor (MainThread) if any UI work is being done with the ImportantData. Ideally Threading concerns should be isolated to a specific file, but I don't think it would be too ridiculous to use await MainActor.run { completion(/*result here*/) } on the final completions if necessary.
Sep ’22
Reply to Problem with Sandbox Testers
I 100% agree this process is DREADFUL (shame on apple) but I have found the solution (though it only works on real device). Step 1: create a new sandbox tester (with random fake email address and BS 'secure' password). Step 2: open Settings in your device, then navigate to AppStore Step 3: scroll down to Sandbox Account Sign In with your newly created account (you may have to first sign out of current account). *IMPORTANT You may be prompted for two-factor auth. If this occurs, tap the 'Other Options', then select 'Don't Upgrade'. This will skip two-step auth and sign you in. Step 4: Open your app on the device you did all this and make the purchase.
Aug ’22