def _by_json(opts)
      valid_keys = %i(typeArray onlyFirst onlyVisible name label value)
      unknown_keys = opts.keys - valid_keys
      raise "Unknown keys: #{unknown_keys}" unless unknown_keys.empty?

      type_array = opts[:typeArray]
      raise 'typeArray must be an array' unless type_array.is_a? Array

      only_first = opts[:onlyFirst]
      raise 'onlyFirst must be a boolean' unless [true, false].include? only_first

      only_visible = opts[:onlyVisible]
      raise 'onlyVisible must be a boolean' unless [true, false].include? only_visible

      # name/label/value are optional. when searching for class only, then none
      # will be present.
      _validate_object opts[:name], opts[:label], opts[:value]

      # note that mainWindow is sometimes nil so it's passed as a param
      # $._elementOrElementsByType will validate that the window isn't nil
      element_or_elements_by_type = <<-JS
        (function() {
          var opts = #{opts.to_json};
          var result = false;

          try {
            result = $._elementOrElementsByType($.mainWindow(), opts);
          } catch (e) {
          }

          return result;
        })();
      JS

      res = execute_script element_or_elements_by_type
      res || raise(Appium::Ios::CommandError, 'mainWindow is nil')
    end
def _by_json(opts)
      valid_keys = %i(typeArray onlyFirst onlyVisible name label value)
      unknown_keys = opts.keys - valid_keys
      raise "Unknown keys: #{unknown_keys}" unless unknown_keys.empty?

      type_array = opts[:typeArray]
      raise 'typeArray must be an array' unless type_array.is_a? Array

      only_first = opts[:onlyFirst]
      raise 'onlyFirst must be a boolean' unless [true, false].include? only_first

      only_visible = opts[:onlyVisible]
      raise 'onlyVisible must be a boolean' unless [true, false].include? only_visible

      # name/label/value are optional. when searching for class only, then none
      # will be present.
      _validate_object opts[:name], opts[:label], opts[:value]

      # note that mainWindow is sometimes nil so it's passed as a param
      # $._elementOrElementsByType will validate that the window isn't nil
      element_or_elements_by_type = <<-JS
        (function() {
          var opts = #{opts.to_json};
          var result = false;

          try {
            result = $._elementOrElementsByType($.mainWindow(), opts);
          } catch (e) {
          }

          return result;
        })();
      JS

      res = execute_script element_or_elements_by_type
      res || raise(Appium::Ios::CommandError, 'mainWindow is nil')
    end
For Appium(automation name), not XCUITest
 typeArray - array of string types to search for. Example: ["UIAStaticText"]
 onlyFirst - boolean. returns only the first result if true. Example: true
 onlyVisible - boolean. returns only visible elements if true. Example: true
 target - string. the target value to search for. Example: "Buttons, Various uses of UIButton"
 substring - boolean. matches on substrings if true otherwise an exact mathc is required. Example: true
 insensitive - boolean. ignores case sensitivity if true otherwise it's case sensitive. Example: true

 opts = {
   typeArray: ["UIAStaticText"],
   onlyFirst: true,
   onlyVisible: true,
   name: {
     target: "Buttons, Various uses of UIButton",
     substring: false,
     insensitive: false,
   },
   label: {
     target: "Buttons, Various uses of UIButton",
     substring: false,
     insensitive: false,
   },
   value: {
     target: "Buttons, Various uses of UIButton",
     substring: false,
     insensitive: false,
   }
 }