diff --git a/lib/config/definitions.js b/lib/config/definitions.js
index 600bac44feb318128197eaafab477dd2754890b8..91c888981f6f9369cd2c766717e738a35f3e5fb3 100644
--- a/lib/config/definitions.js
+++ b/lib/config/definitions.js
@@ -438,6 +438,7 @@ const options = [
       'poetry',
       'ruby',
       'semver',
+      'swift',
     ],
     default: 'semver',
     cli: false,
@@ -1811,6 +1812,19 @@ const options = [
     env: false,
     mergeable: true,
   },
+  {
+    name: 'swift',
+    description: 'Configuration for Package.swift files',
+    stage: 'package',
+    type: 'object',
+    default: {
+      fileMatch: ['(^|/)Package\\.swift'],
+      versionScheme: 'swift',
+      rangeStrategy: 'bump',
+    },
+    mergeable: true,
+    cli: false,
+  },
 ];
 
 function getOptions() {
diff --git a/lib/datasource/git-tags/index.js b/lib/datasource/git-tags/index.js
index 7baf90a69018985703360db31538783983918992..7d2e3ff0c721cefa25d0578a7eb8bb55c74419b6 100644
--- a/lib/datasource/git-tags/index.js
+++ b/lib/datasource/git-tags/index.js
@@ -6,7 +6,7 @@ const cacheMinutes = 10;
 async function getPkgReleases({ lookupName }) {
   try {
     const cachedResult = await renovateCache.get(cacheNamespace, lookupName);
-    // istanbul ignore if
+    /* istanbul ignore next line */
     if (cachedResult) return cachedResult;
 
     const info = await getRemoteInfo({
diff --git a/lib/manager/index.js b/lib/manager/index.js
index ad1bb4dd42e54e3cd8982dc75baee58dab96320a..90b3cdde18864341ae0436b8bcd5cf05b2cc51b8 100644
--- a/lib/manager/index.js
+++ b/lib/manager/index.js
@@ -27,6 +27,7 @@ const managerList = [
   'poetry',
   'pub',
   'sbt',
+  'swift',
   'terraform',
   'travis',
   'ruby-version',
diff --git a/lib/manager/swift/extract.js b/lib/manager/swift/extract.js
new file mode 100644
index 0000000000000000000000000000000000000000..bc5cbb9dce7badcb81f0babf3c1c10ed7a46ab8c
--- /dev/null
+++ b/lib/manager/swift/extract.js
@@ -0,0 +1,346 @@
+const { isValid } = require('../../versioning/swift');
+
+const regExps = {
+  wildcard: /^.*?/,
+  space: /(\s+|\/\/[^\n]*|\/\*.*\*\/)+/s,
+  depsKeyword: /dependencies/,
+  colon: /:/,
+  beginSection: /\[/,
+  endSection: /],?/,
+  package: /\s*.\s*package\s*\(\s*/,
+  urlKey: /url/,
+  stringLiteral: /"[^"]+"/,
+  comma: /,/,
+  from: /from/,
+  rangeOp: /\.\.[.<]/,
+  exactVersion: /\.\s*exact\s*\(\s*/,
+};
+
+const WILDCARD = 'wildcard';
+const SPACE = 'space';
+const DEPS = 'depsKeyword';
+const COLON = 'colon';
+const BEGIN_SECTION = 'beginSection';
+const END_SECTION = 'endSection';
+const PACKAGE = 'package';
+const URL_KEY = 'urlKey';
+const STRING_LITERAL = 'stringLiteral';
+const COMMA = 'comma';
+const FROM = 'from';
+const RANGE_OP = 'rangeOp';
+const EXACT_VERSION = 'exactVersion';
+
+const searchLabels = {
+  wildcard: WILDCARD,
+  space: SPACE,
+  depsKeyword: DEPS,
+  colon: COLON,
+  beginSection: BEGIN_SECTION,
+  endSection: END_SECTION,
+  package: PACKAGE,
+  urlKey: URL_KEY,
+  stringLiteral: STRING_LITERAL,
+  comma: COMMA,
+  from: FROM,
+  rangeOp: RANGE_OP,
+  exactVersion: EXACT_VERSION,
+};
+
+function searchKeysForState(state) {
+  switch (state) {
+    case 'dependencies':
+      return [SPACE, COLON, WILDCARD];
+    case 'dependencies:':
+      return [SPACE, BEGIN_SECTION, WILDCARD];
+    case 'dependencies: [':
+      return [SPACE, PACKAGE, END_SECTION];
+    case '.package(':
+      return [SPACE, URL_KEY, PACKAGE, END_SECTION];
+    case '.package(url':
+      return [SPACE, COLON, PACKAGE, END_SECTION];
+    case '.package(url:':
+      return [SPACE, STRING_LITERAL, PACKAGE, END_SECTION];
+    case '.package(url: [depName]':
+      return [SPACE, COMMA, PACKAGE, END_SECTION];
+    case '.package(url: [depName],':
+      return [
+        SPACE,
+        FROM,
+        STRING_LITERAL,
+        RANGE_OP,
+        EXACT_VERSION,
+        PACKAGE,
+        END_SECTION,
+      ];
+    case '.package(url: [depName], .exact(':
+      return [SPACE, STRING_LITERAL, PACKAGE, END_SECTION];
+    case '.package(url: [depName], from':
+      return [SPACE, COLON, PACKAGE, END_SECTION];
+    case '.package(url: [depName], from:':
+      return [SPACE, STRING_LITERAL, PACKAGE, END_SECTION];
+    case '.package(url: [depName], [value]':
+      return [SPACE, RANGE_OP, PACKAGE, END_SECTION];
+    case '.package(url: [depName], [rangeFrom][rangeOp]':
+      return [SPACE, STRING_LITERAL, PACKAGE, END_SECTION];
+    default:
+      return [DEPS];
+  }
+}
+
+function getMatch(str, state) {
+  const keys = searchKeysForState(state);
+  let result = null;
+  for (let i = 0; i < keys.length; i += 1) {
+    const key = keys[i];
+    const regex = regExps[key];
+    const label = searchLabels[key];
+    const match = str.match(regex);
+    if (match) {
+      const idx = match.index;
+      const substr = match[0];
+      const len = substr.length;
+      if (idx === 0) {
+        return { idx, len, label, substr };
+      }
+      if (!result || idx < result.idx) {
+        result = { idx, len, label, substr };
+      }
+    }
+  }
+  return result;
+}
+
+function getDepName(url) {
+  try {
+    const { host, pathname } = new URL(url);
+    if (host === 'github.com' || host === 'gitlab.com') {
+      return pathname
+        .replace(/^\//, '')
+        .replace(/\.git$/, '')
+        .replace(/\/$/, '');
+    }
+    return url;
+  } catch (e) {
+    return null;
+  }
+}
+
+function extractPackageFile(content, packageFile = null) {
+  if (!content) return null;
+
+  const result = {
+    packageFile,
+  };
+  const deps = [];
+
+  let offset = 0;
+  let restStr = content;
+  let state = null;
+  let match = getMatch(restStr, state);
+
+  let lookupName = null;
+  let currentValue = null;
+  let fileReplacePosition = null;
+
+  function yieldDep() {
+    const depName = getDepName(lookupName);
+    if (depName && currentValue && fileReplacePosition) {
+      const dep = {
+        datasource: 'gitTags',
+        depName,
+        lookupName,
+        currentValue,
+        fileReplacePosition,
+      };
+
+      if (isValid(currentValue)) {
+        deps.push(dep);
+      }
+    }
+    lookupName = null;
+    currentValue = null;
+    fileReplacePosition = null;
+  }
+
+  while (match) {
+    const { idx, len, label, substr } = match;
+    offset += idx;
+    // eslint-disable-next-line default-case
+    switch (state) {
+      case null:
+        if (deps.length) break;
+        if (label === DEPS) {
+          state = 'dependencies';
+        }
+        break;
+      case 'dependencies':
+        if (label === COLON) {
+          state = 'dependencies:';
+        } else if (label !== SPACE) {
+          state = null;
+        }
+        break;
+      case 'dependencies:':
+        if (label === BEGIN_SECTION) {
+          state = 'dependencies: [';
+        } else if (label !== SPACE) {
+          state = null;
+        }
+        break;
+      case 'dependencies: [':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === PACKAGE) {
+          yieldDep();
+          state = '.package(';
+        }
+        break;
+      case '.package(':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === URL_KEY) {
+          state = '.package(url';
+        } else if (label === PACKAGE) {
+          yieldDep();
+        }
+        break;
+      case '.package(url':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === COLON) {
+          state = '.package(url:';
+        } else if (label === PACKAGE) {
+          yieldDep();
+          state = '.package(';
+        }
+        break;
+      case '.package(url:':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === STRING_LITERAL) {
+          lookupName = substr.replace(/^"/, '').replace(/"$/, '');
+          state = '.package(url: [depName]';
+        } else if (label === PACKAGE) {
+          yieldDep();
+          state = '.package(';
+        }
+        break;
+      case '.package(url: [depName]':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === COMMA) {
+          state = '.package(url: [depName],';
+        } else if (label === PACKAGE) {
+          yieldDep();
+          state = '.package(';
+        }
+        break;
+      case '.package(url: [depName],':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === FROM) {
+          fileReplacePosition = offset;
+          currentValue = substr;
+          state = '.package(url: [depName], from';
+        } else if (label === STRING_LITERAL) {
+          fileReplacePosition = offset;
+          currentValue = substr;
+          state = '.package(url: [depName], [value]';
+        } else if (label === RANGE_OP) {
+          fileReplacePosition = offset;
+          currentValue = substr;
+          state = '.package(url: [depName], [rangeFrom][rangeOp]';
+        } else if (label === EXACT_VERSION) {
+          state = '.package(url: [depName], .exact(';
+        } else if (label === PACKAGE) {
+          yieldDep();
+          state = '.package(';
+        }
+        break;
+      case '.package(url: [depName], .exact(':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === STRING_LITERAL) {
+          currentValue = substr.slice(1, substr.length - 1);
+          fileReplacePosition = offset;
+          yieldDep();
+        } else if (label === PACKAGE) {
+          yieldDep();
+          state = '.package(';
+        }
+        break;
+      case '.package(url: [depName], from':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === COLON) {
+          currentValue += substr;
+          state = '.package(url: [depName], from:';
+        } else if (label === SPACE) {
+          currentValue += substr;
+        } else if (label === PACKAGE) {
+          yieldDep();
+          state = '.package(';
+        }
+        break;
+      case '.package(url: [depName], from:':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === STRING_LITERAL) {
+          currentValue += substr;
+          yieldDep();
+          state = 'dependencies: [';
+        } else if (label === SPACE) {
+          currentValue += substr;
+        } else if (label === PACKAGE) {
+          yieldDep();
+          state = '.package(';
+        }
+        break;
+      case '.package(url: [depName], [value]':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === RANGE_OP) {
+          currentValue += substr;
+          state = '.package(url: [depName], [rangeFrom][rangeOp]';
+        } else if (label === SPACE) {
+          currentValue += substr;
+        } else if (label === PACKAGE) {
+          yieldDep();
+          state = '.package(';
+        }
+        break;
+      case '.package(url: [depName], [rangeFrom][rangeOp]':
+        if (label === END_SECTION) {
+          yieldDep();
+          state = null;
+        } else if (label === STRING_LITERAL) {
+          currentValue += substr;
+          state = 'dependencies: [';
+        } else if (label === SPACE) {
+          currentValue += substr;
+        } else if (label === PACKAGE) {
+          yieldDep();
+          state = '.package(';
+        }
+        break;
+    }
+    offset += len;
+    restStr = restStr.slice(idx + len);
+    match = getMatch(restStr, state);
+  }
+  return deps.length ? { ...result, deps } : null;
+}
+
+module.exports = {
+  extractPackageFile,
+};
diff --git a/lib/manager/swift/index.js b/lib/manager/swift/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..749cae6f4db0ec27bb7cde5845780752ce144ac9
--- /dev/null
+++ b/lib/manager/swift/index.js
@@ -0,0 +1,7 @@
+const { extractPackageFile } = require('./extract');
+const { updateDependency } = require('./update');
+
+module.exports = {
+  extractPackageFile,
+  updateDependency,
+};
diff --git a/lib/manager/swift/update.js b/lib/manager/swift/update.js
new file mode 100644
index 0000000000000000000000000000000000000000..09b2baa68c4902af34d01c81a03b376feaf7735b
--- /dev/null
+++ b/lib/manager/swift/update.js
@@ -0,0 +1,30 @@
+const { isVersion } = require('../../versioning/swift');
+
+const fromParam = /^\s*from\s*:\s*"([^"]+)"\s*$/;
+
+function updateDependency(fileContent, upgrade) {
+  const { currentValue, newValue, fileReplacePosition } = upgrade;
+  const leftPart = fileContent.slice(0, fileReplacePosition);
+  const rightPart = fileContent.slice(fileReplacePosition);
+  const oldVal = isVersion(currentValue) ? `"${currentValue}"` : currentValue;
+  let newVal;
+  if (fromParam.test(oldVal)) {
+    const [, version] = oldVal.match(fromParam);
+    newVal = oldVal.replace(version, newValue);
+  } else if (isVersion(newValue)) {
+    newVal = `"${newValue}"`;
+  } else {
+    newVal = newValue;
+  }
+  if (rightPart.indexOf(oldVal) === 0) {
+    return leftPart + rightPart.replace(oldVal, newVal);
+  }
+  if (rightPart.indexOf(newVal) === 0) {
+    return fileContent;
+  }
+  return null;
+}
+
+module.exports = {
+  updateDependency,
+};
diff --git a/lib/versioning/swift/index.js b/lib/versioning/swift/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..5d6934066cc9222fbba57a49c545755298ac705a
--- /dev/null
+++ b/lib/versioning/swift/index.js
@@ -0,0 +1,48 @@
+const semver = require('semver');
+const stable = require('semver-stable');
+const { toSemverRange, getNewValue } = require('./range');
+
+const { is: isStable } = stable;
+
+const {
+  compare: sortVersions,
+  maxSatisfying,
+  minSatisfying,
+  major: getMajor,
+  minor: getMinor,
+  patch: getPatch,
+  satisfies,
+  valid,
+  validRange,
+  ltr,
+  gt: isGreaterThan,
+  eq: equals,
+} = semver;
+
+const isValid = input => !!valid(input) || !!validRange(toSemverRange(input));
+const isVersion = input => !!valid(input);
+const maxSatisfyingVersion = (versions, range) =>
+  maxSatisfying(versions, toSemverRange(range));
+const minSatisfyingVersion = (versions, range) =>
+  minSatisfying(versions, toSemverRange(range));
+const isLessThanRange = (version, range) => ltr(version, toSemverRange(range));
+const matches = (version, range) => satisfies(version, toSemverRange(range));
+
+module.exports = {
+  equals,
+  getMajor,
+  getMinor,
+  getNewValue,
+  getPatch,
+  isCompatible: isVersion,
+  isGreaterThan,
+  isLessThanRange,
+  isSingleVersion: isVersion,
+  isStable,
+  isValid,
+  isVersion,
+  matches,
+  maxSatisfyingVersion,
+  minSatisfyingVersion,
+  sortVersions,
+};
diff --git a/lib/versioning/swift/range.js b/lib/versioning/swift/range.js
new file mode 100644
index 0000000000000000000000000000000000000000..4b9e3f36b9743e1737f5d2ec0750c0f86a7bd14a
--- /dev/null
+++ b/lib/versioning/swift/range.js
@@ -0,0 +1,58 @@
+const semver = require('semver');
+
+const fromParam = /^\s*from\s*:\s*"([^"]+)"\s*$/;
+const fromRange = /^\s*"([^"]+)"\s*\.\.\.\s*$/;
+const binaryRange = /^\s*"([^"]+)"\s*(\.\.[.<])\s*"([^"]+)"\s*$/;
+const toRange = /^\s*(\.\.[.<])\s*"([^"]+)"\s*$/;
+
+function toSemverRange(range) {
+  if (fromParam.test(range)) {
+    const [, version] = range.match(fromParam);
+    if (semver.valid(version)) {
+      const nextMajor = `${semver.major(version) + 1}.0.0`;
+      return `>=${version} <${nextMajor}`;
+    }
+  } else if (fromRange.test(range)) {
+    const [, version] = range.match(fromRange);
+    if (semver.valid(version)) {
+      return `>=${version}`;
+    }
+  } else if (binaryRange.test(range)) {
+    const [, fromVersion, op, toVersion] = range.match(binaryRange);
+    if (semver.valid(fromVersion) && semver.valid(toVersion)) {
+      return op === '..<'
+        ? `>=${fromVersion} <${toVersion}`
+        : `>=${fromVersion} <=${toVersion}`;
+    }
+  } else if (toRange.test(range)) {
+    const [, op, toVersion] = range.match(toRange);
+    if (semver.valid(toVersion)) {
+      return op === '..<' ? `<${toVersion}` : `<=${toVersion}`;
+    }
+  }
+  return null;
+}
+
+function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) {
+  if (fromParam.test(currentValue)) {
+    return toVersion;
+  }
+  if (fromRange.test(currentValue)) {
+    const [, version] = currentValue.match(fromRange);
+    return currentValue.replace(version, toVersion);
+  }
+  if (binaryRange.test(currentValue)) {
+    const [, , , version] = currentValue.match(binaryRange);
+    return currentValue.replace(version, toVersion);
+  }
+  if (toRange.test(currentValue)) {
+    const [, , version] = currentValue.match(toRange);
+    return currentValue.replace(version, toVersion);
+  }
+  return currentValue;
+}
+
+module.exports = {
+  toSemverRange,
+  getNewValue,
+};
diff --git a/renovate-schema.json b/renovate-schema.json
index 1e7259cea4e8807a7ec2c0af98928b0309b920c7..a97ff59832356d4e96a822d3dd8a8f64b8e06b95 100644
--- a/renovate-schema.json
+++ b/renovate-schema.json
@@ -290,7 +290,8 @@
         "pep440",
         "poetry",
         "ruby",
-        "semver"
+        "semver",
+        "swift"
       ],
       "default": "semver"
     },
@@ -1241,6 +1242,16 @@
       "description": "Options to suppress various types of warnings and other notifications",
       "type": "array",
       "default": ["deprecationWarningIssues"]
+    },
+    "swift": {
+      "description": "Configuration for Package.swift files",
+      "type": "object",
+      "default": {
+        "fileMatch": ["(^|/)Package\\.swift"],
+        "versionScheme": "swift",
+        "rangeStrategy": "bump"
+      },
+      "$ref": "#"
     }
   }
 }
diff --git a/test/config/__snapshots__/validation.spec.js.snap b/test/config/__snapshots__/validation.spec.js.snap
index be2e63073f55672116365f6448df9fb4c0fe9e06..ed19fc38fdbcde7b59dd2f84c2f6983e2166c782 100644
--- a/test/config/__snapshots__/validation.spec.js.snap
+++ b/test/config/__snapshots__/validation.spec.js.snap
@@ -87,7 +87,7 @@ Array [
     "depName": "Configuration Error",
     "message": "packageRules:
         You have included an unsupported manager in a package rule. Your list: foo. 
-        Supported managers are: (ansible, bazel, buildkite, bundler, cargo, circleci, composer, deps-edn, docker-compose, dockerfile, github-actions, gitlabci, gomod, gradle, gradle-wrapper, kubernetes, leiningen, maven, meteor, npm, nuget, nvm, pip_requirements, pip_setup, pipenv, poetry, pub, sbt, terraform, travis, ruby-version, homebrew).",
+        Supported managers are: (ansible, bazel, buildkite, bundler, cargo, circleci, composer, deps-edn, docker-compose, dockerfile, github-actions, gitlabci, gomod, gradle, gradle-wrapper, kubernetes, leiningen, maven, meteor, npm, nuget, nvm, pip_requirements, pip_setup, pipenv, poetry, pub, sbt, swift, terraform, travis, ruby-version, homebrew).",
   },
 ]
 `;
diff --git a/test/datasource/git-tags.spec.js b/test/datasource/git-tags.spec.js
index eaca9b0ad2e69759f5ba590b66adef599b91674f..d9b389e4f4538779f1abaabbf9f701daf0bced0c 100644
--- a/test/datasource/git-tags.spec.js
+++ b/test/datasource/git-tags.spec.js
@@ -3,21 +3,23 @@ const { getPkgReleases } = require('../../lib/datasource/git-tags');
 
 jest.mock('isomorphic-git');
 
-const lookupName = 'https://github.com/vapor/vapor.git';
+const lookupName = 'vapor';
+const registryUrls = ['https://github.com/vapor/vapor.git'];
+const registryUrlsAlt = ['https://github.com/vapor/vapor/'];
 
 describe('datasource/git-tags', () => {
   beforeEach(() => global.renovateCache.rmAll());
   describe('getPkgReleases', () => {
     it('returns nil if response is wrong', async () => {
       getRemoteInfo.mockReturnValue(Promise.resolve(null));
-      const versions = await getPkgReleases({ lookupName });
+      const versions = await getPkgReleases({ lookupName, registryUrls });
       expect(versions).toEqual(null);
     });
     it('returns nil if remote call throws exception', async () => {
       getRemoteInfo.mockImplementation(() => {
         throw new Error();
       });
-      const versions = await getPkgReleases({ lookupName });
+      const versions = await getPkgReleases({ lookupName, registryUrls });
       expect(versions).toEqual(null);
     });
     it('returns versions filtered from tags', async () => {
@@ -32,7 +34,10 @@ describe('datasource/git-tags', () => {
           },
         })
       );
-      const versions = await getPkgReleases({ lookupName });
+      const versions = await getPkgReleases({
+        lookupName,
+        registryUrls: registryUrlsAlt,
+      });
       const result = versions.releases.map(x => x.version).sort();
       expect(result).toEqual(['0.0.1', '0.0.2']);
     });
diff --git a/test/manager/swift/__snapshots__/index.spec.js.snap b/test/manager/swift/__snapshots__/index.spec.js.snap
new file mode 100644
index 0000000000000000000000000000000000000000..9605f6e5abbc6e935998aba17b691679f106eee4
--- /dev/null
+++ b/test/manager/swift/__snapshots__/index.spec.js.snap
@@ -0,0 +1,134 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`lib/manager/swift extractPackageFile() parses multiple packages 1`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "0.1.0",
+      "datasource": "gitTags",
+      "depName": "avito-tech/GraphiteClient",
+      "fileReplacePosition": 1177,
+      "lookupName": "https://github.com/avito-tech/GraphiteClient.git",
+    },
+    Object {
+      "currentValue": "1.0.16",
+      "datasource": "gitTags",
+      "depName": "IBM-Swift/BlueSignals",
+      "fileReplacePosition": 1268,
+      "lookupName": "https://github.com/IBM-Swift/BlueSignals.git",
+    },
+    Object {
+      "currentValue": "3.0.6",
+      "datasource": "gitTags",
+      "depName": "daltoniam/Starscream",
+      "fileReplacePosition": 1439,
+      "lookupName": "https://github.com/daltoniam/Starscream.git",
+    },
+    Object {
+      "currentValue": "1.4.6",
+      "datasource": "gitTags",
+      "depName": "httpswift/swifter",
+      "fileReplacePosition": 1523,
+      "lookupName": "https://github.com/httpswift/swifter.git",
+    },
+    Object {
+      "currentValue": "from : \\"0.9.6\\"",
+      "datasource": "gitTags",
+      "depName": "weichsel/ZIPFoundation",
+      "fileReplacePosition": 1626,
+      "lookupName": "https://github.com/weichsel/ZIPFoundation/",
+    },
+  ],
+  "packageFile": null,
+}
+`;
+
+exports[`lib/manager/swift extractPackageFile() parses package descriptions 1`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "from:\\"1.2.3\\"",
+      "datasource": "gitTags",
+      "depName": "vapor/vapor",
+      "fileReplacePosition": 64,
+      "lookupName": "https://github.com/vapor/vapor.git",
+    },
+  ],
+  "packageFile": null,
+}
+`;
+
+exports[`lib/manager/swift extractPackageFile() parses package descriptions 2`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "\\"1.2.3\\"...",
+      "datasource": "gitTags",
+      "depName": "vapor/vapor",
+      "fileReplacePosition": 64,
+      "lookupName": "https://github.com/vapor/vapor.git",
+    },
+  ],
+  "packageFile": null,
+}
+`;
+
+exports[`lib/manager/swift extractPackageFile() parses package descriptions 3`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "\\"1.2.3\\"...\\"1.2.4\\"",
+      "datasource": "gitTags",
+      "depName": "vapor/vapor",
+      "fileReplacePosition": 64,
+      "lookupName": "https://github.com/vapor/vapor.git",
+    },
+  ],
+  "packageFile": null,
+}
+`;
+
+exports[`lib/manager/swift extractPackageFile() parses package descriptions 4`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "\\"1.2.3\\"..<\\"1.2.4\\"",
+      "datasource": "gitTags",
+      "depName": "vapor/vapor",
+      "fileReplacePosition": 64,
+      "lookupName": "https://github.com/vapor/vapor.git",
+    },
+  ],
+  "packageFile": null,
+}
+`;
+
+exports[`lib/manager/swift extractPackageFile() parses package descriptions 5`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "...\\"1.2.3\\"",
+      "datasource": "gitTags",
+      "depName": "vapor/vapor",
+      "fileReplacePosition": 64,
+      "lookupName": "https://github.com/vapor/vapor.git",
+    },
+  ],
+  "packageFile": null,
+}
+`;
+
+exports[`lib/manager/swift extractPackageFile() parses package descriptions 6`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "..<\\"1.2.3\\"",
+      "datasource": "gitTags",
+      "depName": "vapor/vapor",
+      "fileReplacePosition": 64,
+      "lookupName": "https://github.com/vapor/vapor.git",
+    },
+  ],
+  "packageFile": null,
+}
+`;
diff --git a/test/manager/swift/_fixtures/SamplePackage.swift b/test/manager/swift/_fixtures/SamplePackage.swift
new file mode 100644
index 0000000000000000000000000000000000000000..2c8e2a13d011435e13b23f662de392622b7c8be9
--- /dev/null
+++ b/test/manager/swift/_fixtures/SamplePackage.swift
@@ -0,0 +1,1105 @@
+// swift-tools-version:4.2
+
+import PackageDescription
+
+let package = Package(
+    name: "AvitoUITestRunner",
+    products: [
+        // MARK: - Products
+        .executable(
+            // MARK: AvitoRunner
+            name: "AvitoRunner",
+            targets: [
+                "AvitoRunner"
+            ]
+        ),
+        .library(
+            // MARK: EmceePlugin
+            name: "EmceePlugin",
+            targets: [
+                "Models",
+                "Logging",
+                "Plugin"
+            ]
+        ),
+        .executable(
+            // MARK: fake_fbxctest
+            name: "fake_fbxctest",
+            targets: ["FakeFbxctest"]
+        ),
+        .executable(
+            // MARK: testing_plugin
+            name: "testing_plugin",
+            targets: ["TestingPlugin"]
+        )
+    ],
+    dependencies : [
+        // MARK: - Dependencies
+        .package(url: "https://github.com/0x7fs/CountedSet", .branch("master")),
+        .package(url: "foo", .exact("1.2.3.4")),
+        .package(url: "bar", "1.2.3.4.5"...),
+        .package(url: "baz", from: "1.2.3.4"),
+        .package(url: "https://github.com/avito-tech/GraphiteClient.git", .exact(   "0.1.0"   )),
+        .package(url: "https://github.com/IBM-Swift/BlueSignals.git", .exact("1.0.16")),
+        .package(url: "https://github.com/beefon/Shout", .branch("UpdateSocket")),
+        .package(url: "https://github.com/daltoniam/Starscream.git", .exact("3.0.6")),
+        .package(url: "https://github.com/httpswift/swifter.git", .exact("1.4.6")),
+        . package ( url : "https://github.com/weichsel/ZIPFoundation/" ,
+        /*foobar*/ from : "0.9.6")
+        .package(url: "https://github.com/apple/swift-package-manager.git", .branch("swift-5.0-branch")),
+    ],
+    targets: [
+        // MARK: - Targets
+        .target(
+            // MARK: Ansi
+            name: "Ansi",
+            dependencies: [
+            ]
+        ),
+        .target(
+            // MARK: ArgumentsParser
+            name: "ArgumentsParser",
+            dependencies: [
+                "Extensions",
+                "Logging",
+                "Models",
+                "RuntimeDump",
+                "SPMUtility"
+            ]
+        ),
+        .target(
+        // MARK: AutomaticTermination
+            name: "AutomaticTermination",
+            dependencies: [
+                "DateProvider",
+                "Logging",
+                "Timer"
+            ]
+        ),
+        .testTarget(
+            // MARK: AutomaticTerminationTests
+            name: "AutomaticTerminationTests",
+            dependencies: [
+                "AutomaticTermination",
+                "DateProvider"
+            ]
+        ),
+        .target(
+            // MARK: AvitoRunner
+            name: "AvitoRunner",
+            dependencies: [
+                "AvitoRunnerLib"
+            ]
+        ),
+        .target(
+            // MARK: AvitoRunnerLib
+            name: "AvitoRunnerLib",
+            dependencies: [
+                "ArgumentsParser",
+                "ChromeTracing",
+                "Deployer",
+                "DistRunner",
+                "DistWorker",
+                "EventBus",
+                "JunitReporting",
+                "LaunchdUtils",
+                "LocalQueueServerRunner",
+                "LoggingSetup",
+                "Metrics",
+                "Models",
+                "PluginManager",
+                "PortDeterminer",
+                "ProcessController",
+                "RemoteQueue",
+                "ResourceLocationResolver",
+                "SSHDeployer",
+                "ScheduleStrategy",
+                "Scheduler",
+                "SignalHandling",
+                "SPMUtility",
+                "Version"
+            ]
+        ),
+        .testTarget(
+            // MARK: AvitoRunnerLibTests
+            name: "AvitoRunnerLibTests",
+            dependencies: [
+                "AvitoRunnerLib",
+                "Models",
+                "ModelsTestHelpers"
+            ]
+        ),
+        .target(
+            // MARK: BalancingBucketQueue
+            name: "BalancingBucketQueue",
+            dependencies: [
+                "BucketQueue",
+                "DateProvider",
+                "Logging",
+                "Models",
+                "ResultsCollector",
+                "SPMUtility"
+            ]
+        ),
+        .testTarget(
+            // MARK: BalancingBucketQueueTests
+            name: "BalancingBucketQueueTests",
+            dependencies: [
+                "BalancingBucketQueue",
+                "BucketQueueTestHelpers",
+                "ResultsCollector"
+            ]
+        ),
+        .target(
+            // MARK: BucketQueue
+            name: "BucketQueue",
+            dependencies: [
+                "DateProvider",
+                "Logging",
+                "Models",
+                "WorkerAlivenessTracker"
+            ]
+        ),
+        .target(
+            // MARK: BucketQueueTestHelpers
+            name: "BucketQueueTestHelpers",
+            dependencies: [
+                "BucketQueue",
+                "DateProviderTestHelpers",
+                "Models",
+                "ModelsTestHelpers",
+                "WorkerAlivenessTracker"
+            ]
+        ),
+        .testTarget(
+            // MARK: BucketQueueTests
+            name: "BucketQueueTests",
+            dependencies: [
+                "BucketQueue",
+                "BucketQueueTestHelpers",
+                "DateProviderTestHelpers",
+                "ModelsTestHelpers",
+                "WorkerAlivenessTrackerTestHelpers"
+            ]
+        ),
+        .target(
+            // MARK: ChromeTracing
+            name: "ChromeTracing",
+            dependencies: [
+                "Models"
+            ]
+        ),
+        .target(
+            // MARK: CurrentlyBeingProcessedBucketsTracker
+            name: "CurrentlyBeingProcessedBucketsTracker",
+            dependencies: [
+                "CountedSet"
+            ]
+        ),
+        .testTarget(
+            // MARK: CurrentlyBeingProcessedBucketsTrackerTests
+            name: "CurrentlyBeingProcessedBucketsTrackerTests",
+            dependencies: [
+                "CurrentlyBeingProcessedBucketsTracker"
+            ]
+        ),
+        .target(
+            // MARK: DateProvider
+            name: "DateProvider",
+            dependencies: []
+        ),
+        .target(
+            // MARK: DateProviderTestHelpers
+            name: "DateProviderTestHelpers",
+            dependencies: [
+                "DateProvider"
+            ]
+        ),
+        .target(
+            // MARK: Deployer
+            name: "Deployer",
+            dependencies: [
+                "Extensions",
+                "Logging",
+                "Models",
+                "SPMUtility",
+                "ZIPFoundation"
+            ]
+        ),
+        .testTarget(
+            // MARK: DeployerTests
+            name: "DeployerTests",
+            dependencies: [
+                "Deployer"
+            ]
+        ),
+        .target(
+            // MARK: DistDeployer
+            name: "DistDeployer",
+            dependencies: [
+                "Deployer",
+                "LaunchdUtils",
+                "Logging",
+                "Models",
+                "SSHDeployer",
+                "TempFolder",
+                "Version"
+            ]
+        ),
+        .testTarget(
+            // MARK: DistDeployerTests
+            name: "DistDeployerTests",
+            dependencies: [
+                "Deployer",
+                "DistDeployer",
+                "Extensions",
+                "Models",
+                "ModelsTestHelpers",
+                "ResourceLocationResolver",
+                "TempFolder",
+                "SPMUtility"
+            ]
+        ),
+        .target(
+            // MARK: DistRunner
+            name: "DistRunner",
+            dependencies: [
+                "AutomaticTermination",
+                "BucketQueue",
+                "DateProvider",
+                "DistDeployer",
+                "EventBus",
+                "Extensions",
+                "LocalHostDeterminer",
+                "Models",
+                "PortDeterminer",
+                "QueueServer",
+                "ResourceLocationResolver",
+                "ScheduleStrategy",
+                "TempFolder",
+                "Version"
+            ]
+        ),
+        .testTarget(
+            // MARK: DistRunnerTests
+            name: "DistRunnerTests",
+            dependencies: [
+                "DistRunner"
+            ]
+        ),
+        .target(
+            // MARK: DistWorker
+            name: "DistWorker",
+            dependencies: [
+                "CurrentlyBeingProcessedBucketsTracker",
+                "EventBus",
+                "Extensions",
+                "Logging",
+                "Models",
+                "PluginManager",
+                "QueueClient",
+                "RESTMethods",
+                "ResourceLocationResolver",
+                "Scheduler",
+                "SimulatorPool",
+                "SynchronousWaiter",
+                "Timer",
+                "SPMUtility"
+            ]
+        ),
+        .testTarget(
+            // MARK: DistWorkerTests
+            name: "DistWorkerTests",
+            dependencies: [
+                "DistWorker",
+                "ModelsTestHelpers",
+                "Scheduler"
+            ]
+        ),
+        .target(
+            // MARK: EventBus
+            name: "EventBus",
+            dependencies: [
+                "Logging",
+                "Models"
+            ]
+        ),
+        .testTarget(
+            // MARK: EventBusTests
+            name: "EventBusTests",
+            dependencies: [
+                "EventBus",
+                "ModelsTestHelpers",
+                "SynchronousWaiter"
+            ]
+        ),
+        .target(
+            // MARK: Extensions
+            name: "Extensions",
+            dependencies: [
+            ]
+        ),
+        .testTarget(
+            // MARK: ExtensionsTests
+            name: "ExtensionsTests",
+            dependencies: [
+                "Extensions",
+                "SPMUtility"
+            ]
+        ),
+        .target(
+            // MARK: FakeFbxctest
+            name: "FakeFbxctest",
+            dependencies: [
+                "Extensions",
+                "TestingFakeFbxctest"
+            ]
+        ),
+        .target(
+            // MARK: fbxctest
+            name: "fbxctest",
+            dependencies: [
+                "Ansi",
+                "JSONStream",
+                "LocalHostDeterminer",
+                "Logging",
+                "Metrics",
+                "Models",
+                "ProcessController",
+                "Timer",
+                "SPMUtility"
+            ]
+        ),
+        .target(
+            // MARK: FileCache
+            name: "FileCache",
+            dependencies: [
+                "Extensions",
+                "SPMUtility"
+            ]
+        ),
+        .testTarget(
+            // MARK: FileCacheTests
+            name: "FileCacheTests",
+            dependencies: [
+                "FileCache"
+            ]
+        ),
+        .target(
+            // MARK: FileHasher
+            name: "FileHasher",
+            dependencies: [
+                "AtomicModels",
+                "Extensions",
+                "Models"
+            ]
+        ),
+        .testTarget(
+            // MARK: FileHasherTests
+            name: "FileHasherTests",
+            dependencies: [
+                "FileHasher",
+                "TempFolder"
+            ]
+        ),
+        .target(
+            // MARK: LocalHostDeterminer
+            name: "LocalHostDeterminer",
+            dependencies: [
+                "Logging"
+            ]
+        ),
+        .target(
+            // MARK: JSONStream
+            name: "JSONStream",
+            dependencies: []
+        ),
+        .testTarget(
+            // MARK: JSONStreamTests
+            name: "JSONStreamTests",
+            dependencies: [
+                "SPMUtility",
+                "JSONStream"
+            ]
+        ),
+        .target(
+            // MARK: JunitReporting
+            name: "JunitReporting",
+            dependencies: [
+            ]
+        ),
+        .testTarget(
+            // MARK: JunitReportingTests
+            name: "JunitReportingTests",
+            dependencies: [
+                "Extensions",
+                "JunitReporting"
+            ]
+        ),
+        .target(
+            // MARK: LaunchdUtils
+            name: "LaunchdUtils",
+            dependencies: [
+            ]
+        ),
+        .testTarget(
+            // MARK: LaunchdUtilsTests
+            name: "LaunchdUtilsTests",
+            dependencies: [
+                "LaunchdUtils"
+            ]
+        ),
+        .target(
+            // MARK: ListeningSemaphore
+            name: "ListeningSemaphore",
+            dependencies: [
+            ]
+        ),
+        .testTarget(
+            // MARK: ListeningSemaphoreTests
+            name: "ListeningSemaphoreTests",
+            dependencies: [
+                "ListeningSemaphore"
+            ]
+        ),
+        .target(
+            // MARK: LocalQueueServerRunner
+            name: "LocalQueueServerRunner",
+            dependencies: [
+                "AutomaticTermination",
+                "DateProvider",
+                "Logging",
+                "Models",
+                "PortDeterminer",
+                "QueueServer",
+                "ScheduleStrategy",
+                "SynchronousWaiter",
+                "Version"
+            ]
+        ),
+        .testTarget(
+        // MARK: LocalQueueServerRunnerTests
+            name: "LocalQueueServerRunnerTests",
+            dependencies: [
+                "AutomaticTermination",
+                "LocalQueueServerRunner"
+            ]
+        ),
+        .target(
+            // MARK: Logging
+            name: "Logging",
+            dependencies: [
+                "Ansi",
+                "Extensions"
+            ]
+        ),
+        .target(
+            // MARK: LoggingSetup
+            name: "LoggingSetup",
+            dependencies: [
+                "Ansi",
+                "GraphiteClient",
+                "IO",
+                "LocalHostDeterminer",
+                "Logging",
+                "Metrics",
+                "Sentry",
+                "SPMUtility",
+                "Version"
+            ]
+        ),
+        .testTarget(
+            // MARK: LoggingTests
+            name: "LoggingTests",
+            dependencies: [
+                "Logging",
+                "SPMUtility"
+            ]
+        ),
+        .target(
+            // MARK: Metrics
+            name: "Metrics",
+            dependencies: []
+        ),
+        .testTarget(
+            // MARK: MetricsTests
+            name: "MetricsTests",
+            dependencies: [
+                "Metrics"
+            ]
+        ),
+        .target(
+            // MARK: Models
+            name: "Models",
+            dependencies: [
+                "Extensions"
+            ]
+        ),
+        .target(
+            // MARK: ModelsTestHelpers
+            name: "ModelsTestHelpers",
+            dependencies: [
+                "Models",
+                "ScheduleStrategy"
+            ]
+        ),
+        .testTarget(
+            // MARK: ModelsTests
+            name: "ModelsTests",
+            dependencies: [
+                "Models",
+                "ModelsTestHelpers",
+                "TempFolder"
+            ]
+        ),
+        .target(
+            // MARK: Plugin
+            name: "Plugin",
+            dependencies: [
+                "EventBus",
+                "JSONStream",
+                "Logging",
+                "LoggingSetup",
+                "Models",
+                "SimulatorVideoRecorder",
+                "Starscream",
+                "SynchronousWaiter",
+                "TestsWorkingDirectorySupport",
+                "SPMUtility"
+            ]
+        ),
+        .target(
+            // MARK: PluginManager
+            name: "PluginManager",
+            dependencies: [
+                "EventBus",
+                "LocalHostDeterminer",
+                "Logging",
+                "ResourceLocationResolver",
+                "Models",
+                "ProcessController",
+                "Scheduler",
+                "Swifter",
+                "SynchronousWaiter"
+            ]
+        ),
+        .testTarget(
+            // MARK: PluginManagerTests
+            name: "PluginManagerTests",
+            dependencies: [
+                "EventBus",
+                "Models",
+                "ModelsTestHelpers",
+                "PluginManager",
+                "ResourceLocationResolver",
+                "SPMUtility"
+            ]
+        ),
+        .target(
+            // MARK: PortDeterminer
+            name: "PortDeterminer",
+            dependencies: [
+                "Logging",
+                "Swifter"
+            ]
+        ),
+        .testTarget(
+            // MARK: PortDeterminerTests
+            name: "PortDeterminerTests",
+            dependencies: [
+                "PortDeterminer",
+                "Swifter"
+            ]
+        ),
+        .target(
+            // MARK: ProcessController
+            name: "ProcessController",
+            dependencies: [
+                "Extensions",
+                "Logging",
+                "ResourceLocationResolver",
+                "Timer",
+                "SPMUtility"
+            ]
+        ),
+        .testTarget(
+            // MARK: ProcessControllerTests
+            name: "ProcessControllerTests",
+            dependencies: [
+                "Extensions",
+                "ProcessController",
+                "SPMUtility"
+            ]
+        ),
+        .target(
+            // MARK: QueueClient
+            name: "QueueClient",
+            dependencies: [
+                "Logging",
+                "Models",
+                "RESTMethods",
+                "SynchronousWaiter",
+                "Version",
+                "SPMUtility"
+            ]
+        ),
+        .testTarget(
+            // MARK: QueueClientTests
+            name: "QueueClientTests",
+            dependencies: [
+                "Models",
+                "ModelsTestHelpers",
+                "PortDeterminer",
+                "QueueClient",
+                "QueueServer",
+                "RESTMethods",
+                "Swifter",
+                "SynchronousWaiter"
+            ]
+        ),
+        .target(
+            // MARK: QueueServer
+            name: "QueueServer",
+            dependencies: [
+                "AutomaticTermination",
+                "BalancingBucketQueue",
+                "BucketQueue",
+                "DateProvider",
+                "EventBus",
+                "Extensions",
+                "FileHasher",
+                "Logging",
+                "Metrics",
+                "Models",
+                "PortDeterminer",
+                "RESTMethods",
+                "ResultsCollector",
+                "ScheduleStrategy",
+                "Swifter",
+                "SynchronousWaiter",
+                "Timer",
+                "Version",
+                "WorkerAlivenessTracker"
+            ]
+        ),
+        .testTarget(
+            // MARK: QueueServerTests
+            name: "QueueServerTests",
+            dependencies: [
+                "AutomaticTermination",
+                "BalancingBucketQueue",
+                "BucketQueue",
+                "BucketQueueTestHelpers",
+                "DateProviderTestHelpers",
+                "Deployer",
+                "EventBus",
+                "FileHasher",
+                "Models",
+                "ModelsTestHelpers",
+                "QueueServer",
+                "ResourceLocationResolver",
+                "RESTMethods",
+                "ResultsCollector",
+                "ScheduleStrategy",
+                "TempFolder",
+                "VersionTestHelpers",
+                "WorkerAlivenessTracker",
+                "WorkerAlivenessTrackerTestHelpers"
+            ]
+        ),
+        .target(
+            // MARK: RemotePortDeterminer
+            name: "RemotePortDeterminer",
+            dependencies: [
+                "QueueClient",
+                "Version"
+            ]
+        ),
+        .target(
+            // MARK: RemotePortDeterminerTestHelpers
+            name: "RemotePortDeterminerTestHelpers",
+            dependencies: [
+                "RemotePortDeterminer"
+            ]
+        ),
+        .testTarget(
+            // MARK: RemotePortDeterminerTests
+            name: "RemotePortDeterminerTests",
+            dependencies: [
+                "RemotePortDeterminer"
+            ]
+        ),
+        .target(
+            // MARK: RemoteQueue
+            name: "RemoteQueue",
+            dependencies: [
+                "DistDeployer",
+                "Models",
+                "RemotePortDeterminer",
+                "SSHDeployer",
+                "Version"
+            ]
+        ),
+        .testTarget(
+            // MARK: RemoteQueueTests
+            name: "RemoteQueueTests",
+            dependencies: [
+                "RemotePortDeterminerTestHelpers",
+                "RemoteQueue",
+                "VersionTestHelpers"
+            ]
+        ),
+        .target(
+            // MARK: ResultsCollector
+            name: "ResultsCollector",
+            dependencies: [
+                "Models"
+            ]
+        ),
+        .testTarget(
+            // MARK: ResultsCollectorTests
+            name: "ResultsCollectorTests",
+            dependencies: [
+                "ModelsTestHelpers",
+                "ResultsCollector"
+            ]
+        ),
+        .target(
+            // MARK: ResourceLocationResolver
+            name: "ResourceLocationResolver",
+            dependencies: [
+                "AtomicModels",
+                "Extensions",
+                "FileCache",
+                "Models",
+                "URLResource"
+            ]
+        ),
+        .testTarget(
+            // MARK: ResourceLocationResolverTests
+            name: "ResourceLocationResolverTests",
+            dependencies: [
+                "FileCache",
+                "ResourceLocationResolver",
+                "Swifter",
+                "TempFolder",
+                "URLResource"
+            ]
+        ),
+        .target(
+            // MARK: RESTMethods
+            name: "RESTMethods",
+            dependencies: [
+                "Models",
+                "Version"
+            ]
+        ),
+        .target(
+            // MARK: Runner
+            name: "Runner",
+            dependencies: [
+                "EventBus",
+                "fbxctest",
+                "LocalHostDeterminer",
+                "Logging",
+                "Models",
+                "SimulatorPool",
+                "TempFolder",
+                "TestsWorkingDirectorySupport"
+            ]
+        ),
+        .testTarget(
+            // MARK: RunnerTests
+            name: "RunnerTests",
+            dependencies: [
+                "Extensions",
+                "Models",
+                "ModelsTestHelpers",
+                "ResourceLocationResolver",
+                "Runner",
+                "ScheduleStrategy",
+                "SimulatorPool",
+                "TestingFakeFbxctest",
+                "TempFolder"
+            ]
+        ),
+        .target(
+            // MARK: RuntimeDump
+            name: "RuntimeDump",
+            dependencies: [
+                "EventBus",
+                "Extensions",
+                "Metrics",
+                "Models",
+                "Runner",
+                "SynchronousWaiter",
+                "TempFolder"
+            ]
+        ),
+        .testTarget(
+            // MARK: RuntimeDumpTests
+            name: "RuntimeDumpTests",
+            dependencies: [
+                "Models",
+                "ModelsTestHelpers",
+                "ResourceLocationResolver",
+                "RuntimeDump",
+                "TestingFakeFbxctest",
+                "TempFolder"
+            ]
+        ),
+        .target(
+            // MARK: Sentry
+            name: "Sentry",
+            dependencies: []
+        ),
+        .testTarget(
+            // MARK: SentryTests
+            name: "SentryTests",
+            dependencies: [
+                "Sentry"
+            ]
+        ),
+        .target(
+            // MARK: Scheduler
+            name: "Scheduler",
+            dependencies: [
+                "EventBus",
+                "ListeningSemaphore",
+                "Logging",
+                "Models",
+                "Runner",
+                "RuntimeDump",
+                "ScheduleStrategy",
+                "SimulatorPool",
+                "SynchronousWaiter",
+                "TempFolder"
+            ]
+        ),
+        .target(
+            // MARK: ScheduleStrategy
+            name: "ScheduleStrategy",
+            dependencies: [
+                "Extensions",
+                "Logging",
+                "Models"
+            ]
+        ),
+        .testTarget(
+            // MARK: ScheduleStrategyTests
+            name: "ScheduleStrategyTests",
+            dependencies: [
+                "Models",
+                "ModelsTestHelpers",
+                "ScheduleStrategy"
+            ]
+        ),
+        .target(
+        // MARK: SignalHandling
+            name: "SignalHandling",
+            dependencies: [
+                "Models",
+                "Signals"
+            ]
+        ),
+        .testTarget(
+        // MARK: SignalHandlingTests
+        name: "SignalHandlingTests",
+        dependencies: [
+            "SignalHandling",
+            "Signals"
+            ]
+        ),
+        .target(
+            // MARK: SimulatorPool
+            name: "SimulatorPool",
+            dependencies: [
+                "Extensions",
+                "fbxctest",
+                "Logging",
+                "Models",
+                "ProcessController",
+                "TempFolder",
+                "SPMUtility"
+            ]
+        ),
+        .testTarget(
+            // MARK: SimulatorPoolTests
+            name: "SimulatorPoolTests",
+            dependencies: [
+                "Models",
+                "ModelsTestHelpers",
+                "ResourceLocationResolver",
+                "SimulatorPool",
+                "SynchronousWaiter",
+                "TempFolder"
+            ]
+        ),
+        .target(
+            // MARK: SimulatorVideoRecorder
+            name: "SimulatorVideoRecorder",
+            dependencies: [
+                "Logging",
+                "Models",
+                "ProcessController"
+            ]
+        ),
+        .target(
+            // MARK: SSHDeployer
+            name: "SSHDeployer",
+            dependencies: [
+                "Ansi",
+                "Extensions",
+                "Logging",
+                "Models",
+                "SPMUtility",
+                "Deployer",
+                "Shout"
+            ]
+        ),
+        .testTarget(
+            // MARK: SSHDeployerTests
+            name: "SSHDeployerTests",
+            dependencies: [
+                "SSHDeployer"
+            ]
+        ),
+        .target(
+            // MARK: SynchronousWaiter
+            name: "SynchronousWaiter",
+            dependencies: []
+        ),
+        .testTarget(
+            // MARK: SynchronousWaiterTests
+            name: "SynchronousWaiterTests",
+            dependencies: ["SynchronousWaiter"]
+        ),
+        .target(
+            // MARK: TempFolder
+            name: "TempFolder",
+            dependencies: [
+                "SPMUtility"
+            ]
+        ),
+        .testTarget(
+            // MARK: TempFolderTests
+            name: "TempFolderTests",
+            dependencies: [
+                "TempFolder"
+            ]
+        ),
+        .target(
+            // MARK: TestingFakeFbxctest
+            name: "TestingFakeFbxctest",
+            dependencies: [
+                "Extensions",
+                "fbxctest",
+                "Logging"
+            ]
+        ),
+        .target(
+            // MARK: TestingPlugin
+            name: "TestingPlugin",
+            dependencies: [
+                "Extensions",
+                "Models",
+                "Logging",
+                "LoggingSetup",
+                "Plugin"
+            ]
+        ),
+        .target(
+            // MARK: TestsWorkingDirectorySupport
+            name: "TestsWorkingDirectorySupport",
+            dependencies: [
+                "Models"
+            ]
+        ),
+        .target(
+            // MARK: Timer
+            name: "Timer",
+            dependencies: [
+            ]
+        ),
+        .target(
+            // MARK: URLResource
+            name: "URLResource",
+            dependencies: [
+                "FileCache",
+                "Logging",
+                "Models",
+                "SynchronousWaiter",
+                "SPMUtility"
+            ]
+        ),
+        .testTarget(
+            // MARK: URLResourceTests
+            name: "URLResourceTests",
+            dependencies: [
+                "FileCache",
+                "Swifter",
+                "URLResource",
+                "SPMUtility"
+            ]
+        ),
+        .target(
+            // MARK: Version
+            name: "Version",
+            dependencies: [
+                "FileHasher"
+            ]
+        ),
+        .target(
+            // MARK: VersionTestHelpers
+            name: "VersionTestHelpers",
+            dependencies: [
+                "Version"
+            ]
+        ),
+        .testTarget(
+            // MARK: VersionTests
+            name: "VersionTests",
+            dependencies: [
+                "Extensions",
+                "FileHasher",
+                "Version",
+                "SPMUtility"
+            ]
+        ),
+        .target(
+            // MARK: WorkerAlivenessTracker
+            name: "WorkerAlivenessTracker",
+            dependencies: [
+                "Logging"
+            ]
+        ),
+        .target(
+            // MARK: WorkerAlivenessTrackerTestHelpersTests
+            name: "WorkerAlivenessTrackerTestHelpers",
+            dependencies: [
+                "WorkerAlivenessTracker"
+            ]
+        ),
+        .testTarget(
+            // MARK: WorkerAlivenessTrackerTests
+            name: "WorkerAlivenessTrackerTests",
+            dependencies: [
+                "WorkerAlivenessTracker",
+                "WorkerAlivenessTrackerTestHelpers"
+            ]
+        ),
+        .target(
+            // MARK: XcTestRun
+            name: "XcTestRun",
+            dependencies: [
+            ]
+        ),
+        .testTarget(
+            // MARK: XcTestRunTests
+            name: "XcTestRunTests",
+            dependencies: [
+                "XcTestRun"
+            ]
+        )
+    ]
+)
diff --git a/test/manager/swift/index.spec.js b/test/manager/swift/index.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..15ab7c4bb9407e879518ffab2db7a62a326b652d
--- /dev/null
+++ b/test/manager/swift/index.spec.js
@@ -0,0 +1,203 @@
+const fs = require('fs');
+const path = require('path');
+const { extractPackageFile } = require('../../../lib/manager/swift/extract');
+const { updateDependency } = require('../../../lib/manager/swift/update');
+
+const pkgContent = fs.readFileSync(
+  path.resolve(__dirname, `./_fixtures/SamplePackage.swift`),
+  'utf8'
+);
+
+describe('lib/manager/swift', () => {
+  describe('extractPackageFile()', () => {
+    it('returns null for empty content', () => {
+      expect(extractPackageFile(null)).toBeNull();
+      expect(extractPackageFile(``)).toBeNull();
+      expect(extractPackageFile(`dependencies:[]`)).toBeNull();
+      expect(extractPackageFile(`dependencies:["foobar"]`)).toBeNull();
+    });
+    it('returns null for invalid content', () => {
+      expect(extractPackageFile(`dependen`)).toBeNull();
+      expect(extractPackageFile(`dependencies!: `)).toBeNull();
+      expect(extractPackageFile(`dependencies :`)).toBeNull();
+      expect(extractPackageFile(`dependencies...`)).toBeNull();
+      expect(extractPackageFile(`dependencies:!`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[...`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[]`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package.package(`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package(asdf`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package]`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package(]`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package(.package(`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package(`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package(]`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package(url],`)).toBeNull();
+      expect(
+        extractPackageFile(`dependencies:[.package(url.package(]`)
+      ).toBeNull();
+      expect(
+        extractPackageFile(`dependencies:[.package(url:.package(`)
+      ).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package(url:]`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package(url:"fo`)).toBeNull();
+      expect(extractPackageFile(`dependencies:[.package(url:"fo]`)).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://example.com/something.git"]`
+        )
+      ).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git"]`
+        )
+      ).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git".package(]`
+        )
+      ).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git", ]`
+        )
+      ).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git", .package(]`
+        )
+      ).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git", .exact(]`
+        )
+      ).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git", from]`
+        )
+      ).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git", from.package(`
+        )
+      ).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git", from:]`
+        )
+      ).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git", from:.package(`
+        )
+      ).toBeNull();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git","1.2.3")]`
+        )
+      ).toBeNull();
+    });
+    it('parses package descriptions', () => {
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git",from:"1.2.3")]`
+        )
+      ).toMatchSnapshot();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git","1.2.3"...)]`
+        )
+      ).toMatchSnapshot();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git","1.2.3"..."1.2.4")]`
+        )
+      ).toMatchSnapshot();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git","1.2.3"..<"1.2.4")]`
+        )
+      ).toMatchSnapshot();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git",..."1.2.3")]`
+        )
+      ).toMatchSnapshot();
+      expect(
+        extractPackageFile(
+          `dependencies:[.package(url:"https://github.com/vapor/vapor.git",..<"1.2.3")]`
+        )
+      ).toMatchSnapshot();
+    });
+    it('parses multiple packages', () => {
+      expect(extractPackageFile(pkgContent)).toMatchSnapshot();
+    });
+  });
+  describe('updateDependency()', () => {
+    it('updates successfully', () => {
+      [
+        [
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git",.exact("1.2.3")]',
+          '1.2.4',
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git",.exact("1.2.4")]',
+        ],
+        [
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git", from: "1.2.3")]',
+          '1.2.4',
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git", from: "1.2.4")]',
+        ],
+        [
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git", "1.2.3"..."1.2.4")]',
+          '"1.2.3"..."1.2.5"',
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git", "1.2.3"..."1.2.5")]',
+        ],
+        [
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git", "1.2.3"..<"1.2.4")]',
+          '"1.2.3"..<"1.2.5"',
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git", "1.2.3"..<"1.2.5")]',
+        ],
+        [
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git", ..."1.2.4")]',
+          '..."1.2.5"',
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git", ..."1.2.5")]',
+        ],
+        [
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git", ..<"1.2.4")]',
+          '..<"1.2.5"',
+          'dependencies:[.package(url:"https://github.com/vapor/vapor.git", ..<"1.2.5")]',
+        ],
+      ].forEach(([content, newValue, result]) => {
+        const { deps } = extractPackageFile(content);
+        const [dep] = deps;
+        const upgrade = { ...dep, newValue };
+        const updated = updateDependency(content, upgrade);
+        expect(updated).toEqual(result);
+      });
+    });
+    it('returns content if already updated', () => {
+      const content =
+        'dependencies:[.package(url:"https://github.com/vapor/vapor.git",.exact("1.2.3")]';
+      const currentValue = '1.2.3';
+      const newValue = '1.2.4';
+      const { deps } = extractPackageFile(content);
+      const [dep] = deps;
+      const upgrade = { ...dep, newValue };
+      const replaced = content.replace(currentValue, newValue);
+      const updated = updateDependency(replaced, upgrade);
+      expect(updated).toBe(replaced);
+    });
+    it('returns null if content is different', () => {
+      const content =
+        'dependencies:[.package(url:"https://github.com/vapor/vapor.git",.exact("1.2.3")]';
+      const currentValue = '1.2.3';
+      const newValue = '1.2.4';
+      const { deps } = extractPackageFile(content);
+      const [dep] = deps;
+      const upgrade = { ...dep, newValue };
+      const replaced = content.replace(currentValue, '1.2.5');
+      expect(updateDependency(replaced, upgrade)).toBe(null);
+    });
+  });
+});
diff --git a/test/versioning/swift.spec.js b/test/versioning/swift.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..ad86d2009041b872bc26e43f28ac8de3180d4f27
--- /dev/null
+++ b/test/versioning/swift.spec.js
@@ -0,0 +1,85 @@
+const {
+  getNewValue,
+  isValid,
+  minSatisfyingVersion,
+  maxSatisfyingVersion,
+  isLessThanRange,
+  matches,
+} = require('../../lib/versioning/swift');
+
+describe('isValid(input)', () => {
+  it('understands Swift version ranges', () => {
+    expect(isValid('from: "1.2.3"')).toBe(true);
+    expect(isValid('from : "1.2.3"')).toBe(true);
+    expect(isValid('from:"1.2.3"')).toBe(true);
+    expect(isValid(' from:"1.2.3" ')).toBe(true);
+    expect(isValid(' from : "1.2.3" ')).toBe(true);
+
+    expect(isValid('"1.2.3"..."1.2.4"')).toBe(true);
+    expect(isValid(' "1.2.3" ... "1.2.4" ')).toBe(true);
+
+    expect(isValid('"1.2.3"...')).toBe(true);
+    expect(isValid(' "1.2.3" ... ')).toBe(true);
+
+    expect(isValid('..."1.2.4"')).toBe(true);
+    expect(isValid(' ... "1.2.4" ')).toBe(true);
+
+    expect(isValid('"1.2.3"..<"1.2.4"')).toBe(true);
+    expect(isValid(' "1.2.3" ..< "1.2.4" ')).toBe(true);
+
+    expect(isValid('..<"1.2.4"')).toBe(true);
+    expect(isValid(' ..< "1.2.4" ')).toBe(true);
+  });
+  it('should return null for irregular versions', () => {
+    expect(isValid('17.04.0')).toBeFalsy();
+  });
+  it('should support simple semver', () => {
+    expect(isValid('1.2.3')).toBe(true);
+  });
+  it('should support semver with dash', () => {
+    expect(isValid('1.2.3-foo')).toBe(true);
+  });
+  it('should reject semver without dash', () => {
+    expect(isValid('1.2.3foo')).toBeFalsy();
+  });
+  it('should support ranges', () => {
+    expect(isValid('~1.2.3')).toBeFalsy();
+    expect(isValid('^1.2.3')).toBeFalsy();
+    expect(isValid('from: "1.2.3"')).toBe(true);
+    expect(isValid('"1.2.3"..."1.2.4"')).toBe(true);
+    expect(isValid('"1.2.3"..."1.2.4"')).toBe(true);
+    expect(isValid('"1.2.3"..<"1.2.4"')).toBe(true);
+    expect(isValid('"1.2.3"..<"1.2.4"')).toBe(true);
+    expect(isValid('..."1.2.3"')).toBe(true);
+    expect(isValid('..<"1.2.4"')).toBe(true);
+    expect(
+      minSatisfyingVersion(['1.2.3', '1.2.4', '1.2.5'], '..<"1.2.4"')
+    ).toBe('1.2.3');
+    expect(
+      maxSatisfyingVersion(['1.2.3', '1.2.4', '1.2.5'], '..<"1.2.4"')
+    ).toBe('1.2.3');
+    expect(
+      maxSatisfyingVersion(['1.2.3', '1.2.4', '1.2.5'], '..."1.2.4"')
+    ).toBe('1.2.4');
+    expect(isLessThanRange('1.2.3', '..."1.2.4"')).toBe(false);
+    expect(isLessThanRange('1.2.3', '"1.2.4"...')).toBe(true);
+    expect(matches('1.2.4', '..."1.2.4"')).toBe(true);
+    expect(matches('1.2.4', '..."1.2.3"')).toBe(false);
+  });
+});
+describe('getNewValue()', () => {
+  it('supports range update', () => {
+    [
+      ['1.2.3', 'auto', '1.2.3', '1.2.4', '1.2.3'],
+      ['from: "1.2.3"', 'auto', '1.2.3', '1.2.4', '1.2.4'],
+      ['"1.2.3"...', 'auto', '1.2.3', '1.2.4', '"1.2.4"...'],
+      ['"1.2.3"..."1.2.4"', 'auto', '1.2.3', '1.2.5', '"1.2.3"..."1.2.5"'],
+      ['"1.2.3"..<"1.2.4"', 'auto', '1.2.3', '1.2.5', '"1.2.3"..<"1.2.5"'],
+      ['..."1.2.4"', 'auto', '1.2.3', '1.2.5', '..."1.2.5"'],
+      ['..<"1.2.4"', 'auto', '1.2.3', '1.2.5', '..<"1.2.5"'],
+    ].forEach(([range, strategy, fromVersion, toVersion, result]) => {
+      const newValue = getNewValue(range, strategy, fromVersion, toVersion);
+      expect(newValue).toEqual(result);
+    });
+  });
+});
diff --git a/test/workers/repository/extract/__snapshots__/index.spec.js.snap b/test/workers/repository/extract/__snapshots__/index.spec.js.snap
index 8df23e46180d99cd56ded78859f4748ab2703b7c..fd6c9711c1b9e6ffca64b029a0ab2bd80d6b1c21 100644
--- a/test/workers/repository/extract/__snapshots__/index.spec.js.snap
+++ b/test/workers/repository/extract/__snapshots__/index.spec.js.snap
@@ -92,6 +92,9 @@ Object {
   "sbt": Array [
     Object {},
   ],
+  "swift": Array [
+    Object {},
+  ],
   "terraform": Array [
     Object {},
   ],
diff --git a/website/docs/configuration-options.md b/website/docs/configuration-options.md
index 19432cfb90724c7b5142db1f9a84ba64eb4d45df..a1cbbe890b306dac4c1daf9b1fc709c969e53a63 100644
--- a/website/docs/configuration-options.md
+++ b/website/docs/configuration-options.md
@@ -1080,6 +1080,22 @@ Use this field to suppress various types of warnings and other notifications fro
 
 The above config will suppress the comment which is added to a PR whenever you close a PR unmerged.
 
+## swift
+
+Anything other than `.exact(<...>)` will be treated as range with respect to Swift specific.
+Because of this, some PR descriptions will look like `from: <...> => <...>`.
+
+Examples:
+
+```swift
+package(name: "<...>", from: "1.2.3")     // => from: "2.0.0"
+package(name: "<...>", "1.2.3"...)        // => "2.0.0"...
+package(name: "<...>", "1.2.3"..."1.3.0") // => "1.2.3"..."2.0.0"
+package(name: "<...>", "1.2.3"..<"1.3.0") // => "1.2.3"..<"2.0.0"
+package(name: "<...>", ..."1.2.3")        // => ..."2.0.0"
+package(name: "<...>", ..<"1.2.3")        // => ..<"2.0.0"
+```
+
 ## terraform
 
 Currently Terraform support is limited to Terraform registry sources and github sources that include semver refs, e.g. like `github.com/hashicorp/example?ref=v1.0.0`.