fyi, I know that I can do something like:
func emptyView() -> EmptyView {
Self.logger.debug("hello")
return EmptyView()
}
But it just feels like a contrived way to have to add logging.
Post
Replies
Boosts
Views
Activity
I had the same problem, after recently agreeing to an updated agreement. I quit Xcode, restarted, and was able to upload successfully
Hi Szymczyk, thanks for your questions :-)
What version of Xcode are you running?
16.1
Can you run the unit tests if you create a new project and select the Include Tests checkbox?
No. I see the same 'bad' behaviour when I do the following:
Create a new project and choose: Multiproject and Document App (and include Unit and UI test targets)
choose iPhone 16 simulator as the target, Build and Run (app builds and runs fine)
In Xcode, open the Tests file auto created in the Unit tests target
click the diamond to the left of the final class declaration
Build succeeds, but then it spins forever at Testing...
I repeated the above steps, but instead chose App instead of Document App, and I see the same results.
I repeated the above steps again, but chose iOS (instead of Multiplatform) and App. In this case Xcode runs (and passes) the tests.
I ran one final test using iOS and Document App. In this case Xcode successfully runs the tests.
I'll look further, but currently it seems like Multiplatform projects in Xcode aren't able to run test targets for me.
thanks again for the questions that helped me move this forward a bit :-)
Mike
I came up with an implementation that mostly meets my needs. I've described it here: https://developer.apple.com/forums/thread/756192
I found an implementation I'm mostly happy with.
First, I defined a Notification.Name and created a userDefaults binding 'factory'
extension Notification.Name {
static let userDefaultsChanged = Notification.Name(rawValue: "user.defaults.changed")
}
struct BindingFactory {
static func binding(for defaultsKey: String) -> Binding<Bool> {
return Binding {
return UserDefaults.standard.bool(forKey: defaultsKey)
} set: { newValue in
UserDefaults.standard.setValue(newValue, forKey: defaultsKey)
NotificationCenter.default.post(name: .userDefaultsChanged, object: defaultsKey)
}
}
}
Then in my UI elements that were previously using @AppStorage, they now use the new bindings.
var body: some View {
VStack {
Toggle("Extended", isOn: BindingFactory.binding(for: "extended"))
Text(LanguageManager.shared.summary)
}
}
And now LanguageManager's init can add a subscriber to the userDefaultsChanged notification.
init() {
NotificationCenter.default.publisher(for: .userDefaultsChanged)
.sink(receiveValue: { notification in
print("\(notification.object!) changed")
self.updateItems()
})
.store(in: &subscriptions)
}
The main thing I don't really like about this is the need to create a static func binding(for defaultsKey: String) -> Binding<Bool> for each type of binding (Bool, String, Int, etc.)
Hi again @Claude31
turns out I was curious enough about this to make time to investigate it sooner.
In my sample code, neither the View's willSet/didSet nor the class's willSet/didSet are getting called.
import SwiftUI
struct ContentView: View {
@AppStorage("extended") var extended: Bool = true {
willSet {
print("ConteentView willSet")
}
didSet {
print("ContentView didSet")
}
}
var body: some View {
VStack {
Toggle("Extended", isOn: $extended)
Text(LanguageManager.shared.summary)
}
.padding()
}
}
#Preview {
ContentView()
}
class LanguageManager {
static let shared = LanguageManager()
var items: [String] = ["basic", "list"]
var summary: String {
return items.reduce("") {
return "\($0)\n\($1)"
}
}
@AppStorage("extended") var extended: Bool = true {
willSet {
print("LanguageManager willSet")
}
didSet {
print("LanguageManager didSet")
updateItems()
}
}
func updateItems() {
if extended {
items = ["much", "more", "than", "basic", "list"]
} else {
items = ["basic", "list"]
}
}
}
**
Update**: I now see I should have followed the link you included. Sorry about not doing that before answering.
However this answer suggests that when a user updates the UI, the @AppStorage var in my class will most definitely not be getting updated. And that is the one that I'm hoping will be able to be notified when 'somebody else' updates one of the UserDefault values.
I want to define multiple UserDefault values that can be updated via settings/prefs UI.
and then when any of these values change, LanguageManager will be notified (pub/sub?) of the change, and run updateItems()
Good suggestion @Claude31. I just tried adding this:
struct UserDefaultsKey: EnvironmentKey {
static var defaultValue: UserDefaults = .standard
}
extension EnvironmentValues {
var userDefaults: UserDefaults {
get { self[UserDefaultsKey.self] }
set { self[UserDefaultsKey.self] = newValue }
}
}
and updating the original to include this:
@Environment(\.userDefaults) var userDefaults
@AppStorage("enhanced", store: userDefaults) var scriptPickers: Bool = true
I now get the following error one the @AppStorage line:
Cannot use instance member 'userDefaults' within property initializer; property initializers run before 'self' is available
bummer...
I think I've found something that works.
Moved CombineOptions into the model, and subscribe to the options changes in the Model's init.
Still, I'd appreciate any feedback on this code below.
struct ContentView: View {
@ObservedObject var model = Model()
var body: some View {
VStack {
Image(uiImage: model.composed)
.resizable()
.aspectRatio(contentMode: .fit)
Slider(value: $model.options.scale)
Stepper(value: $model.options.numberOfImages, label:
{
Text("\(model.options.numberOfImages)")})
}
.padding()
}
private var enhancedImage: UIImage {
return model.inputImage.combine(options: model.options)
}
}
class Model: ObservableObject {
let inputImage: UIImage = UIImage.init(named: "IMG_4097")!
@Published var options = CombineOptions.basic
@Published var composed: UIImage
private var cancellables: [AnyCancellable] = []
init() {
self.composed = inputImage
$options
.debounce(for: 1.0, scheduler: DispatchQueue.global(qos: .background))
.map( { mapOptions in
return UIImage.composed(from: self.inputImage, using: mapOptions)
} )
.receive(on: DispatchQueue.main)
.assign(to: \.composed, on: self)
.store(in: &cancellables)
}
}
Heavy Sigh on this missing functionality.
When I tried moving my dev schema to production, it failed and didn't give a reason.
Attempting to debug, I created a second container just by adding a 2 at the end of the identifier.
the new container works fine, so I tried deleting all the Record Types from the original. I then created a new recordType with a single custom field. it still fails with no reason given.
Then I thought I'd just delete and recreate the original container......
apparently not :-(
When a property of an element (value type) of an Array is modified, Swift treats as the Array itself is modified. That's the nature of value type in Swift. This nicely summarizes my main take away message from this exchange.
thx again!
Yes. Have you heard that Swift Array is a value type? I was not aware. For quite some time I've imagined Swift arrays more like NSArray
> initially: filter.tangleTypes = [0x123456, 0x123466]
What do you mean? What are 0x123456...?
Are you talking about the case of Array containing struct (value type)? This was my attempt to: better understand how value types are stored in array
the mechanism used for swiftUI to recognize that an @Published property has changed
0x123456 was intended to be a memory location (address)
I was under the impression that a swift array containing structs would be represented as a list of memory locations (one per struct/item in the array)
Further, I was imagining that when the item/struct was modified the array (modelled as a list of memLocations) would now include a new location for the modified item. (and that this new address in the array was what would allow swiftUI View classes to be made aware of the changed in the @Published property in the appropriate ObservableObject.
I recently learned that value types in an array don't really have a memory location.
https://forums.swift.org/t/memory-address-of-value-types-and-reference-types/6637/7
I guess they're just stored directly in the array's allocated memory? As such when the value object changes in anyway, the array's memory/contents are modified, and detected by any swiftUI View classes that happen to be listening.
Mike
Hi OOPer,
This was definitely not obvious (I'm gonna reserve judgement on whether this says more about me or the complexity of my question)
having said that, I think I now see:
when SwiftUI depends on properties in 'child' model objects stored in an array in a parent model object: the parent should conform to ObservableObject (and be a ref type)
the children in the array need to be value type (struct)
does this mean that updating a property in a value type that is stored in an array will replace the array element with a new copy of the value type.
initially: filter.tangleTypes = [0x123456, 0x123466]
filter.tangleTypes[1].isChecked = true
Are you saying at this point filter.tangleTypes = [0x123456, 0x123476]
?
as always thanks for taking the time to share :-)
Thanks OOPer, that is exactly the answer I was looking for.
(and fair point about the 'lazy' configuration of the DateFormatter :-)
Thanks OOPer. (and thanks for keeping my Canadian spelling of favourite in your sample code :-)
hmm, some progress. things compile now that i've changed phraseChangePublisher to this:
var phraseChangedPublisher: AnyCancellable {
Publishers.CombineLatest(leftWord.$currentWord, rightWord.$currentWord)
.map({
return $0.0 + $0.1
})
.debounce(for: 1.0, scheduler: RunLoop.main)
.sink { someValue in
print("someValue: \(someValue)")
}
}
Is AnyCancellable the best way to proceed here?
At this point the pipeline wasn't firing, even when the words were changing.
When I added the following ivar in my View:
let pipeline: AnyCancellable
init(model: PhraseModel) {
self.model = model
self.pipeline = model.phraseChangedPublisher
}
the pipeline code runs and behaves as expected. However when I change body to the following:
var body: some View {
HStack {
Text(model.leftWord.currentWord)
Text(model.rightWord.currentWord)
}
.onReceive(pipeline, perform: {_ in
print("hello")
})
}
I get a compile error at line2: "Unable to infer complex closure return type; add explicit type to disambiguate"
Any guidance on how to improve this?
(heavy sigh)