@@ -26,7 +26,7 @@ class AnyUIKitSensoryFeedbackCache {
2626
2727class UIKitSensoryFeedbackCache < V> : AnyUIKitSensoryFeedbackCache where V: View {
2828 weak var host : _UIHostingView < V > ?
29- var cachedGenerators : [ SensoryFeedback . FeedbackType : UIFeedbackGenerator ] = [ : ]
29+ private var cachedGenerators : [ GeneratorCacheKey : UIFeedbackGenerator ] = [ : ]
3030
3131 override func implementation(
3232 type: SensoryFeedback . FeedbackType
@@ -59,6 +59,7 @@ class UIKitSensoryFeedbackCache<V>: AnyUIKitSensoryFeedbackCache where V: View {
5959 } createIfNeeded: {
6060 UINotificationFeedbackGenerator ( )
6161 }
62+ // OpenSwiftUI Addition:
6263 // SwiftUI implementation Bug: introduced since iOS 17 & iOS 26.2 is still not fixed
6364 // FB21332474
6465 case /*.increase, .decrease,*/ . selection:
@@ -108,17 +109,56 @@ class UIKitSensoryFeedbackCache<V>: AnyUIKitSensoryFeedbackCache where V: View {
108109 }
109110 }
110111
112+ /* OpenSwiftUI Addition Begin */
113+
114+ // MARK: - GeneratorCacheKey
115+
116+ /// A cache key that excludes intensity to prevent unbounded cache growth.
117+ ///
118+ /// Using `SensoryFeedback.FeedbackType` directly as cache key causes issues
119+ /// because it includes the intensity value. Since intensity is a `Double`,
120+ /// every different intensity creates a new cache entry and generator interaction.
121+ /// The generator style (weight/flexibility) doesn't depend on intensity -
122+ /// intensity is only used at feedback generation time.
123+ private enum GeneratorCacheKey : Hashable {
124+ case success
125+ case warning
126+ case error
127+ case selection
128+ case alignment
129+ case pathComplete
130+ case impactWeight( SensoryFeedback . Weight . Storage )
131+ case impactFlexibility( SensoryFeedback . Flexibility . Storage )
132+
133+ init ? ( _ type: SensoryFeedback . FeedbackType ) {
134+ switch type {
135+ case . success: self = . success
136+ case . warning: self = . warning
137+ case . error: self = . error
138+ case . selection: self = . selection
139+ case . alignment: self = . alignment
140+ case . pathComplete: self = . pathComplete
141+ case let . impactWeight( weight, _) : self = . impactWeight( weight)
142+ case let . impactFlexibility( flexibility, _) : self = . impactFlexibility( flexibility)
143+ default : return nil
144+ }
145+ }
146+ }
147+
148+ /* OpenSwiftUI Addition End */
149+
111150 private func getGenerator< Generator, Feedback> (
112151 _ type: SensoryFeedback . FeedbackType ,
113152 work: ( Generator ) -> Feedback ,
114153 createIfNeeded: ( ) -> Generator
115- ) -> Feedback where Generator: UIFeedbackGenerator , Feedback: LocationBasedSensoryFeedback {
154+ ) -> Feedback ? where Generator: UIFeedbackGenerator , Feedback: LocationBasedSensoryFeedback {
155+ guard let cacheKey = GeneratorCacheKey ( type) else { return nil }
116156 let generator : Generator
117- if let cachedGenerator = cachedGenerators [ type ] {
157+ if let cachedGenerator = cachedGenerators [ cacheKey ] {
118158 generator = cachedGenerator as! Generator
119159 } else {
120160 generator = createIfNeeded ( )
121- cachedGenerators [ type ] = generator
161+ cachedGenerators [ cacheKey ] = generator
122162 host!. addInteraction ( generator)
123163 }
124164 return work ( generator)
0 commit comments