exyte / Chat

A SwiftUI Chat UI framework with fully customizable message cells and a built-in media picker
MIT License
873 stars 133 forks source link

Feature request: use environments instead of vars #75

Closed dankinsoid closed 4 months ago

dankinsoid commented 4 months ago

Now all chat configuration like showDateHeaders, messageUseMarkdown, avatarSize, etc, are simple variables and configured through methods from ChatView extension. It means, that it's possible to set them only locally, directly with a ChatView. I suggest to use EnvironmentValues instead that allows to setup all configs globally for the whole app and override them locally if needed. Example:

extension EnvironmentValues {

   public struct Chat: Equatable {
     public var type: ChatType = .chat
     public var showDateHeaders: Bool = true
     public var avatarSize: CGFloat = 32
     public var messageUseMarkdown: Bool = false
     public var showMessageMenuOnLongPress: Bool = true
     public var tapAvatarClosure: TapAvatarClosure?
     public var mediaPickerSelectionParameters: MediaPickerParameters?
     public var orientationHandler: MediaPickerOrientationHandler = {_ in}
     public var chatTitle: String?
     public var showMessageTimeView = true
     public var messageFont = UIFontMetrics.default.scaledFont(for: UIFont.systemFont(ofSize: 15))
     public var availablelInput: AvailableInputType = .full
   }

   private enum ChatKey: EnvironmentKey {
     static let defaultValue = EnvironmentValues.Chat()
   }

   public var chat: Chat {
      get { self[ChatKey.self] }
      set { self[ChatKey.self] = newValue }
   }
}

extension View {

    public func chatView<T>(_ keyPath: WritableKeyPath<EnvironmentValues.Chat, T>, _ value: T) -> some View {
       environment(\.chat[keyPath: keyPath], value)
    }

    // convenience methods for each var if needed
}
f3dm76 commented 4 months ago

Hey @dankinsoid, that way you won't be able to use different settings for different chats in the same app. If you need many chats which are all the same, please just create a custom view wrapping the chat with the setting you need and reuse it. Have a nice day

dankinsoid commented 4 months ago

@f3dm76 Hi, why? Environments can be overriden. With environments you can define a default style at the app root view and customize it if needed for any specific chat. It works out of box

f3dm76 commented 4 months ago

Yeah, but if you spread config code for chat across your project, it'll be easy to forget/lose part of it, and then get unexpected results. Also, I will have to use more specific names for all the setup - just using .type is ok for ChatView modifier, but not ok if it'll become a global thing. In the current approach, you'll be sure to have all you code in one place exactly where it needs to be. What is the benefit of environment approach, compared to using a wrapper?

dankinsoid commented 4 months ago

Benefits:

Strictly speaking, all these reasons are more important for small, highly reusable views like buttons, texts, checkmarks, etc., and they are not as important for a ChatView. However, I like environments a lot; they are highly useful, convenient and perfect suits for any styling.

f3dm76 commented 4 months ago

These are good arguments, however, there are two sides to most of them:

The bottomline is - unfortunately I don't like environment approach. Why make global that which can be local? Narrowing the scope is the first thing I've been taught as a programmer. We have encapsulation principal for a reason, and I think making everything global, while convenient, is potentially dangerous and gets hard to track fast.

Summing up: