Pull request Work around memory leak when checking for `nil` on composed-swift/Composed
Added to work around https://bugs.swift.org/browse/SR-14103
Added to work around https://bugs.swift.org/browse/SR-14103
Overall this is pretty minor but when dealing with a lot of SingleElementSections
that are infrequently updated is does start to slow down.
I thought a faster approach would be to use generics on replace(element:)
to only check for nil
when the value is Optional
, but generics don't allow for this kind of overload.
Also added some basic tests. The performance tests show a ~9% performance increase.
Replacing https://github.com/composed-swift/ComposedUI/pull/15
This PR is being used to diff between the OpenNet fork.
Hopefully other PRs will be merged before this (e.g. https://github.com/composed-swift/Composed/pull/21), which should lead to this PR having no changes and purely to ensure some of the history is not lost.
We've discussed returning -1
not being great previously, but now we have the 2.0
branch we can make this change :)
Updated to match syntax in README.md.
Maybe these were intentionally left because the package has a minimum tools version of 4.2?
@bill201207 found that UINib(nibName:bundle:)
is a big part of the poor performance when our app launches.
Ultimately batching updates will help with a lot of things like this, but really that'll just be masking some of the performance issues so it's useful to find issues like this now.
When displaying an alert in SwiftUI, if the value used to calculate whether the alert is presented is both Optional
and does not conform to Identifiable
1 it is often recommended to use a separate flag, similar to:
struct ContentView: View { @State private var alertText: String? @State private var isPresentingAlert = false var body: some View { Button("Show Alert") { self.alertText = "Alert Text" self.isPresentingAlert = true } .alert(isPresented: $isPresentingAlert) { Alert(title: Text(alertText!)) } } }
There are 2 main downsides to this:
alertText
is not set back to nil
, which may cause bugs and will increase memory usage (even if only a little in this case)isPresentingAlert
flag needs to be managedTo work around these issues I create a small extension to Binding
the allows this same code to be updated to:
struct ContentView: View { @State private var alertText: String? var body: some View { Button("Show Alert") { self.alertText = "Alert Text" } .alert(isPresented: $alertText.mappedToBool()) { Alert(title: Text(alertText!)) } } }
The extension is fairly small and simple:
import os.log import SwiftUI extension Binding where Value == Bool { /// Creates a binding by mapping an optional value to a `Bool` that is /// `true` when the value is non-`nil` and `false` when the value is `nil`. /// /// When the value of the produced binding is set to `false` the value /// of `bindingToOptional`'s `wrappedValue` is set to `nil`. /// /// Setting the value of the produce binding to `true` does nothing and /// will log an error. /// /// - parameter bindingToOptional: A `Binding` to an optional value, used to calculate the `wrappedValue`. public init<Wrapped>(mappedTo bindingToOptional: Binding<Wrapped?>) { self.init( get: { bindingToOptional.wrappedValue != nil }, set: { newValue in if !newValue { bindingToOptional.wrappedValue = nil } else { os_log( .error, "Optional binding mapped to optional has been set to `true`, which will have no effect. Current value: %@", String(describing: bindingToOptional.wrappedValue) ) } } ) } } extension Binding { /// Returns a binding by mapping this binding's value to a `Bool` that is /// `true` when the value is non-`nil` and `false` when the value is `nil`. /// /// When the value of the produced binding is set to `false` this binding's value /// is set to `nil`. public func mappedToBool<Wrapped>() -> Binding<Bool> where Value == Wrapped? { return Binding<Bool>(mappedTo: self) } }
The extension isn't tied directly to showing an alert or a sheet and can be used in any context, but this is one of the better examples of its usage.
This extension is available on GitHub under the MIT license.
1 If it does conform to Identifiable
use alert(item:content:)
This adds the ComposedLayout
and ComposedUI
packages, which will close #16.
The base for this is 2.0-beta
, which we can use for testing new changes, which can also break API.
If you go to the repo settings you can (temporarily) set the default branch to 2.0-beta
and we can add a note to the README stating this is the beta release and the stable release is available in the master
branch.
Once merged and these changes are made the READMEs for ComposedLayout
and ComposedUI
repos can be updated to point to this repo and their repos archived.
This is a new type of section that is similar to ComposedSectionProvider
, but rather than flattening each of the children in to a single SectionProvider
it flattens them in to a single Section
.
The ComposedUI
side of this has been updated to support multiple cells per section, with a convenience for FlatSection
that delegates the calls to each of the flattened sections.
This is a breaking change since the protocol requirements have changed. I have set the base of the PR to merge-all-libraries
since that's also a breaking change and this PR relies on those changes.
edit: We've moved to our own fork to allow for more rapid development. The latest changes are in https://github.com/opennetltd/Composed/tree/feature/FlatSection, which will eventually be merged back in to this repo once some of the other PRs/issues have been resolved.