Merge branch 'development' into patch-1

This commit is contained in:
Nathan Tannar
2018-11-01 16:43:45 -07:00
committed by GitHub
14 changed files with 167 additions and 58 deletions
+16 -8
View File
@@ -4,18 +4,22 @@ The changelog for `MessageKit`. Also see the [releases](https://github.com/Messa
--------------------------------------
## [2.0.0](https://github.com/MessageKit/MessageKit/releases/tag/2.0.0-beta.1)
## Upcoming Release
### Changed
- Change acl of `handleGesture(touchLocation:)` in `MessageLabel` from internal to open.
[#912](https://github.com/MessageKit/MessageKit/pull/912) by [@julienkode](https://github.com/JulienKode)
- Fixed the way that the Strings and UIImages are parsed in the `InputTextView` to prevent crashes in `parseForComponents()`.
[#791](https://github.com/MessageKit/MessageKit/pull/791) by [@nathantannar4](https://github.com/nathantannar4)
## [2.0.0-beta.1](https://github.com/MessageKit/MessageKit/releases/tag/2.0.0-beta.1)
### Changed
- **Breaking Change** Updated codebase to Swift 4.2 [#883](https://github.com/MessageKit/MessageKit/pull/883) by [@nathantannar4](https://github.com/nathantannar4)
- Fixed the way that the Strings and UIImages are parsed in the `InputTextView` to prevent crashes in `parseForComponents()`.
[#791](https://github.com/MessageKit/MessageKit/pull/791) by [@nathantannar4](https://github.com/nathantannar4)
### Added
- Added support for detection and handling of `NSLink`s inside of messages.
@@ -30,15 +34,17 @@ The changelog for `MessageKit`. Also see the [releases](https://github.com/Messa
- Added `additionalBottomInset` property that allows to adjust the bottom content inset automatically set on the messages collection view by the view controller.
[#787](https://github.com/MessageKit/MessageKit/pull/787) by [@andreyvit](https://github.com/andreyvit)
- Added new methods to simplify using of custom messages: `customCellSizeCalculator(for:at:in:)` for `MessagesLayoutDelegate` and `customCell(for:at:in:)` for `MessagesDataSource`.
[#879](https://github.com/MessageKit/MessageKit/pull/879) by [@realbonus](https://github.com/RealBonus)
### Fixed
- Fixed a bug that prevented `MessageLabel` from laying out properly when contained by superviews using autolayout.
[#889](https://github.com/MessageKit/MessageKit/pull/889) by [@marius-serban](https://github.com/marius-serban).
- **Breaking Change** Fixed typo of `scrollsToBottomOnKeybordBeginsEditing` to `scrollsToBottomOnKeyboardBeginsEditing`.
[#856](https://github.com/MessageKit/MessageKit/pull/856) by [@p-petrenko](https://github.com/p-petrenko)
- Fixed a bug that prevented `MessageLabel` from laying out properly when contained by superviews using autolayout.
[#889](https://github.com/MessageKit/MessageKit/pull/889) by [@marius-serban](https://github.com/marius-serban).
- Fixed bottom content inset adjustment when using an undocked keyboard on iPad, or when `edgesForExtendedLayout` does not include `.top`, or when a parent container view controller adds extra views at the top of the screen.
[#787](https://github.com/MessageKit/MessageKit/pull/787) by [@andreyvit](https://github.com/andreyvit)
@@ -49,7 +55,9 @@ The changelog for `MessageKit`. Also see the [releases](https://github.com/Messa
- Fixed `MessagesCollectionView` to allow to use nibs with `MessageReusableView`.
[#832](https://github.com/MessageKit/MessageKit/pull/832) by [@maxxx777](https://github.com/maxxx777).
- Fixed multiple crashes at views, when views are being called from another XIB. [#905](https://github.com/MessageKit/MessageKit/pull/905) by [@talanov](https://github.com/talanov).
- Fixed multiple crashes at views, when views are being called from another XIB.
[#905](https://github.com/MessageKit/MessageKit/pull/905) by [@talanov](https://github.com/talanov).
## [1.0.0](https://github.com/MessageKit/MessageKit/releases/tag/1.0.0)
+1 -1
View File
@@ -2,7 +2,7 @@
### Code of Conduct
Please read our [Code of Conduct](https://github.com/MessageKit/MessageKit/blob/master/Code_of_Conduct.md).
Please read our [Code of Conduct](https://github.com/MessageKit/MessageKit/blob/master/CODE_OF_CONDUCT.md).
The MessageKit maintainers take this Code of Conduct very seriously. Intolerance, disrespect, harassment, and any form of negativity will not be tolerated.
### Ways to Contribute
+5
View File
@@ -35,6 +35,11 @@ final internal class AppDelegate: UIResponder, UIApplicationDelegate {
window?.rootViewController = NavigationController(rootViewController: LaunchViewController())
window?.makeKeyAndVisible()
if UserDefaults.isFirstLaunch() {
// Enable Text Messages
UserDefaults.standard.set(true, forKey: "Text Messages")
}
return true
}
@@ -30,6 +30,26 @@ final internal class SampleData {
static let shared = SampleData()
private init() {}
enum MessageTypes: UInt32, CaseIterable {
case Text = 0
case AttributedText = 1
case Photo = 2
case Video = 3
case Emoji = 4
case Location = 5
case Url = 6
case Phone = 7
case Custom = 8
static func random() -> MessageTypes {
// Update as new enumerations are added
let maxValue = Custom.rawValue
let rand = arc4random_uniform(maxValue+1)
return MessageTypes(rawValue: rand)!
}
}
let system = Sender(id: "000000", displayName: "System")
let nathan = Sender(id: "000001", displayName: "Nathan Tannar")
@@ -46,8 +66,6 @@ final internal class SampleData {
let messageImages: [UIImage] = [#imageLiteral(resourceName: "img1"), #imageLiteral(resourceName: "img2")]
let messageTypes = ["Text", "Text", "Text", "AttributedText", "Location", "Photo", "Emoji", "Video", "URL", "Phone", "Custom"]
let emojis = [
"👍",
"😂😂😂",
@@ -116,51 +134,62 @@ final internal class SampleData {
return date
}
}
func randomMessageType() -> MessageTypes {
let messageType = MessageTypes.random()
func randomMessage(isCustomEnabled: Bool, allowedSenders: [Sender]) -> MockMessage {
if !UserDefaults.standard.bool(forKey: "\(messageType)" + " Messages") {
return randomMessageType()
}
return messageType
}
func randomMessage(allowedSenders: [Sender]) -> MockMessage {
let randomNumberSender = Int(arc4random_uniform(UInt32(allowedSenders.count)))
let randomNumberImage = Int(arc4random_uniform(UInt32(messageImages.count)))
let randomMessageType = Int(arc4random_uniform(UInt32(messageTypes.count - (isCustomEnabled ? 0 : 1))))
let randomNumberLocation = Int(arc4random_uniform(UInt32(locations.count)))
let randomNumberEmoji = Int(arc4random_uniform(UInt32(emojis.count)))
let randomSentance = Lorem.sentence()
let uniqueID = NSUUID().uuidString
let sender = allowedSenders[randomNumberSender]
let date = dateAddingRandomTime()
switch messageTypes[randomMessageType] {
case "Text":
return MockMessage(text: randomSentance, sender: sender, messageId: uniqueID, date: date)
case "AttributedText":
let attributedText = attributedString(with: randomSentance)
switch randomMessageType() {
case .Text:
let randomSentence = Lorem.sentence()
return MockMessage(text: randomSentence, sender: sender, messageId: uniqueID, date: date)
case .AttributedText:
let randomSentence = Lorem.sentence()
let attributedText = attributedString(with: randomSentence)
return MockMessage(attributedText: attributedText, sender: senders[randomNumberSender], messageId: uniqueID, date: date)
case "Photo":
case .Photo:
let randomNumberImage = Int(arc4random_uniform(UInt32(messageImages.count)))
let image = messageImages[randomNumberImage]
return MockMessage(image: image, sender: sender, messageId: uniqueID, date: date)
case "Video":
case .Video:
let randomNumberImage = Int(arc4random_uniform(UInt32(messageImages.count)))
let image = messageImages[randomNumberImage]
return MockMessage(thumbnail: image, sender: sender, messageId: uniqueID, date: date)
case "Emoji":
case .Emoji:
let randomNumberEmoji = Int(arc4random_uniform(UInt32(emojis.count)))
return MockMessage(emoji: emojis[randomNumberEmoji], sender: sender, messageId: uniqueID, date: date)
case "Location":
case .Location:
let randomNumberLocation = Int(arc4random_uniform(UInt32(locations.count)))
return MockMessage(location: locations[randomNumberLocation], sender: sender, messageId: uniqueID, date: date)
case "URL":
case .Url:
return MockMessage(text: "https://github.com/MessageKit", sender: sender, messageId: uniqueID, date: date)
case "Phone":
case .Phone:
return MockMessage(text: "123-456-7890", sender: sender, messageId: uniqueID, date: date)
case "Custom":
case .Custom:
return MockMessage(custom: "Someone left the conversation", sender: system, messageId: uniqueID, date: date)
default:
fatalError("Unrecognized mock message type")
}
}
func getMessages(count: Int, completion: ([MockMessage]) -> Void) {
var messages: [MockMessage] = []
// Disable Custom Messages
UserDefaults.standard.set(false, forKey: "Custom Messages")
for _ in 0..<count {
let message = randomMessage(isCustomEnabled: false, allowedSenders: senders)
let message = randomMessage(allowedSenders: senders)
messages.append(message)
}
completion(messages)
@@ -168,8 +197,10 @@ final internal class SampleData {
func getAdvancedMessages(count: Int, completion: ([MockMessage]) -> Void) {
var messages: [MockMessage] = []
// Enable Custom Messages
UserDefaults.standard.set(true, forKey: "Custom Messages")
for _ in 0..<count {
let message = randomMessage(isCustomEnabled: true, allowedSenders: senders)
let message = randomMessage(allowedSenders: senders)
messages.append(message)
}
completion(messages)
@@ -177,8 +208,10 @@ final internal class SampleData {
func getMessages(count: Int, allowedSenders: [Sender], completion: ([MockMessage]) -> Void) {
var messages: [MockMessage] = []
// Disable Custom Messages
UserDefaults.standard.set(false, forKey: "Custom Messages")
for _ in 0..<count {
let message = randomMessage(isCustomEnabled: false, allowedSenders: allowedSenders)
let message = randomMessage(allowedSenders: allowedSenders)
messages.append(message)
}
completion(messages)
+3 -1
View File
@@ -77,7 +77,9 @@ final class MockSocket {
queuedMessage = nil
} else {
let sender = arc4random_uniform(1) % 2 == 0 ? connectedUsers.first! : connectedUsers.last!
queuedMessage = MockMessage(text: Lorem.sentence(), sender: sender, messageId: UUID().uuidString, date: Date())
SampleData.shared.getMessages(count: 1, allowedSenders: [sender]) { (message) in
queuedMessage = message.first
}
onTypingStatusCode?()
}
}
@@ -41,4 +41,14 @@ extension UserDefaults {
}
return 20
}
static func isFirstLaunch() -> Bool {
let hasBeenLaunchedBeforeFlag = "hasBeenLaunchedBeforeFlag"
let isFirstLaunch = !UserDefaults.standard.bool(forKey: hasBeenLaunchedBeforeFlag)
if isFirstLaunch {
UserDefaults.standard.set(true, forKey: hasBeenLaunchedBeforeFlag)
UserDefaults.standard.synchronize()
}
return isFirstLaunch
}
}
@@ -30,12 +30,12 @@ final internal class SettingsViewController: UITableViewController {
// MARK: - Properties
var selectedMockMessagesCount: Int = 20
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
let cells = ["Mock messages count", "Text Messages", "AttributedText Messages", "Photo Messages", "Video Messages", "Emoji Messages", "Location Messages", "Url Messages", "Phone Messages"]
// MARK: - Picker
var messagesPicker = UIPickerView()
@@ -55,6 +55,8 @@ final internal class SettingsViewController: UITableViewController {
messagesPicker.dataSource = self
messagesPicker.delegate = self
messagesPicker.backgroundColor = .white
messagesPicker.selectRow(UserDefaults.standard.mockMessagesCount(), inComponent: 0, animated: false)
}
// MARK: - Toolbar
@@ -82,12 +84,25 @@ final internal class SettingsViewController: UITableViewController {
// MARK: - TableViewDelegate & TableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
return cells.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellValue = cells[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell()
cell.textLabel?.text = cells[indexPath.row]
return indexPath.row == 0 ? configureTextFieldTableViewCell(at: indexPath) : UITableViewCell()
switch cellValue {
case "Mock messages count":
return configureTextFieldTableViewCell(at: indexPath)
default:
let switchView = UISwitch(frame: .zero)
switchView.isOn = UserDefaults.standard.bool(forKey: cellValue)
switchView.tag = indexPath.row
switchView.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
cell.accessoryView = switchView
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
@@ -118,6 +133,12 @@ final internal class SettingsViewController: UITableViewController {
}
return TextFieldTableViewCell()
}
@objc func switchChanged(_ sender: UISwitch!) {
let cell = cells[sender.tag]
UserDefaults.standard.set(sender.isOn, forKey: cell)
}
}
// MARK: - UIPickerViewDelegate, UIPickerViewDataSource
@@ -134,8 +155,4 @@ extension SettingsViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return "\(row)"
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedMockMessagesCount = row
}
}
+1 -1
View File
@@ -90,7 +90,7 @@ github "MessageKit/MessageKit"
## Contributing
Great! Look over these things first.
- Please read our [Code of Conduct](https://github.com/MessageKit/MessageKit/blob/master/Code_of_Conduct.md)
- Please read our [Code of Conduct](https://github.com/MessageKit/MessageKit/blob/master/CODE_OF_CONDUCT.md)
- Check the [Contributing Guide Lines](https://github.com/MessageKit/MessageKit/blob/master/CONTRIBUTING.md).
- Come join us on [Slack](https://join.slack.com/t/messagekit/shared_invite/MjI4NzIzNzMyMzU0LTE1MDMwODIzMDUtYzllYzIyNTU4MA) and 🗣 don't be a stranger.
- Check out the [current issues](https://github.com/MessageKit/MessageKit/issues) and see if you can tackle any of those.
@@ -181,6 +181,8 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
return collectionView.messagesDataSource?.numberOfItems(inSection: section, in: collectionView) ?? 0
}
/// Note:
/// If you override this method, remember to call MessagesDataSource's customCell(for:at:in:) for MessageKind.custom messages, if necessary
open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let messagesCollectionView = collectionView as? MessagesCollectionView else {
@@ -207,7 +209,7 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
cell.configure(with: message, at: indexPath, and: messagesCollectionView)
return cell
case .custom:
fatalError(MessageKitError.customDataUnresolvedCell)
return messagesDataSource.customCell(for: message, at: indexPath, in: messagesCollectionView)
}
}
+9 -9
View File
@@ -132,7 +132,7 @@ open class MessageSizeCalculator: CellSizeCalculator {
// MARK: - Avatar
public func avatarPosition(for message: MessageType) -> AvatarPosition {
open func avatarPosition(for message: MessageType) -> AvatarPosition {
let dataSource = messagesLayout.messagesDataSource
let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
var position = isFromCurrentSender ? outgoingAvatarPosition : incomingAvatarPosition
@@ -146,7 +146,7 @@ open class MessageSizeCalculator: CellSizeCalculator {
return position
}
public func avatarSize(for message: MessageType) -> CGSize {
open func avatarSize(for message: MessageType) -> CGSize {
let dataSource = messagesLayout.messagesDataSource
let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
return isFromCurrentSender ? outgoingAvatarSize : incomingAvatarSize
@@ -154,14 +154,14 @@ open class MessageSizeCalculator: CellSizeCalculator {
// MARK: - Top cell Label
public func cellTopLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
open func cellTopLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
let layoutDelegate = messagesLayout.messagesLayoutDelegate
let collectionView = messagesLayout.messagesCollectionView
let height = layoutDelegate.cellTopLabelHeight(for: message, at: indexPath, in: collectionView)
return CGSize(width: messagesLayout.itemWidth, height: height)
}
public func cellTopLabelAlignment(for message: MessageType) -> LabelAlignment {
open func cellTopLabelAlignment(for message: MessageType) -> LabelAlignment {
let dataSource = messagesLayout.messagesDataSource
let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
return isFromCurrentSender ? outgoingCellTopLabelAlignment : incomingCellTopLabelAlignment
@@ -169,14 +169,14 @@ open class MessageSizeCalculator: CellSizeCalculator {
// MARK: - Top message Label
public func messageTopLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
open func messageTopLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
let layoutDelegate = messagesLayout.messagesLayoutDelegate
let collectionView = messagesLayout.messagesCollectionView
let height = layoutDelegate.messageTopLabelHeight(for: message, at: indexPath, in: collectionView)
return CGSize(width: messagesLayout.itemWidth, height: height)
}
public func messageTopLabelAlignment(for message: MessageType) -> LabelAlignment {
open func messageTopLabelAlignment(for message: MessageType) -> LabelAlignment {
let dataSource = messagesLayout.messagesDataSource
let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
return isFromCurrentSender ? outgoingMessageTopLabelAlignment : incomingMessageTopLabelAlignment
@@ -184,14 +184,14 @@ open class MessageSizeCalculator: CellSizeCalculator {
// MARK: - Bottom Label
public func messageBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
open func messageBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize {
let layoutDelegate = messagesLayout.messagesLayoutDelegate
let collectionView = messagesLayout.messagesCollectionView
let height = layoutDelegate.messageBottomLabelHeight(for: message, at: indexPath, in: collectionView)
return CGSize(width: messagesLayout.itemWidth, height: height)
}
public func messageBottomLabelAlignment(for message: MessageType) -> LabelAlignment {
open func messageBottomLabelAlignment(for message: MessageType) -> LabelAlignment {
let dataSource = messagesLayout.messagesDataSource
let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
return isFromCurrentSender ? outgoingMessageBottomLabelAlignment : incomingMessageBottomLabelAlignment
@@ -213,7 +213,7 @@ open class MessageSizeCalculator: CellSizeCalculator {
// MARK: - MessageContainer
public func messageContainerPadding(for message: MessageType) -> UIEdgeInsets {
open func messageContainerPadding(for message: MessageType) -> UIEdgeInsets {
let dataSource = messagesLayout.messagesDataSource
let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
return isFromCurrentSender ? outgoingMessagePadding : incomingMessagePadding
@@ -147,6 +147,8 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout {
lazy open var videoMessageSizeCalculator = MediaMessageSizeCalculator(layout: self)
lazy open var locationMessageSizeCalculator = LocationMessageSizeCalculator(layout: self)
/// - Note:
/// If you override this method, remember to call MessageLayoutDelegate's customCellSizeCalculator(for:at:in:) method for MessageKind.custom messages, if necessary
open func cellSizeCalculatorForItem(at indexPath: IndexPath) -> CellSizeCalculator {
let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
switch message.kind {
@@ -163,7 +165,7 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout {
case .location:
return locationMessageSizeCalculator
case .custom:
fatalError("Must return a CellSizeCalculator for MessageKind.custom(Any?)")
return messagesLayoutDelegate.customCellSizeCalculator(for: message, at: indexPath, in: messagesCollectionView)
}
}
+3 -3
View File
@@ -52,9 +52,9 @@ public enum MessageKind {
case emoji(String)
/// A custom message.
/// - Note: Using this case requires that you override the following methods and handle this case:
/// - `collectionView(_:cellForItemAt indexPath: IndexPath) -> UICollectionViewCell`
/// - `cellSizeCalculatorForItem(at indexPath: IndexPath) -> CellSizeCalculator`
/// - Note: Using this case requires that you implement the following methods and handle this case:
/// - MessagesDataSource: customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell
/// - MessagesLayoutDelegate: customCellSizeCalculator(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator
case custom(Any?)
// MARK: - Not supported yet
@@ -92,6 +92,17 @@ public protocol MessagesDataSource: AnyObject {
///
/// The default value returned by this method is `nil`.
func messageBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString?
/// Custom collectionView cell for message with `custom` message type.
///
/// - Parameters:
/// - message: The `custom` message type
/// - indexPath: The `IndexPath` of the cell.
/// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
///
/// - Note:
/// This method will call fatalError() on default. You must override this method if you are using MessageType.custom messages.
func customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell
}
public extension MessagesDataSource {
@@ -115,4 +126,8 @@ public extension MessagesDataSource {
func messageBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
return nil
}
func customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell {
fatalError(MessageKitError.customDataUnresolvedCell)
}
}
@@ -80,6 +80,17 @@ public protocol MessagesLayoutDelegate: AnyObject {
/// - Note:
/// The default value returned by this method is zero.
func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat
/// Custom cell size calculator for messages with MessageType.custom.
///
/// - Parameters:
/// - message: The custom message
/// - indexPath: The `IndexPath` of the cell.
/// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed.
///
/// - Note:
/// The default implementation will throw fatalError(). You must override this method if you are using messages with MessageType.custom.
func customCellSizeCalculator(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator
}
public extension MessagesLayoutDelegate {
@@ -103,4 +114,8 @@ public extension MessagesLayoutDelegate {
func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
return 0
}
func customCellSizeCalculator(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator {
fatalError("Must return a CellSizeCalculator for MessageKind.custom(Any?)")
}
}